PHP-Code:
<?php if (!defined('TL_ROOT')) die('You can not access this file directly!');
/**
* TYPOlight webCMS
*
* The TYPOlight webCMS is an accessible web content management system that
* specializes in accessibility and generates W3C-compliant HTML code. It
* provides a wide range of functionality to develop professional websites
* including a built-in search engine, form generator, file and user manager,
* CSS engine, multi-language support and many more. For more information and
* additional TYPOlight applications like the TYPOlight MVC Framework please
* visit the project website https://contao.org.
*
* The Catalog extension allows the creation of multiple catalogs of custom items,
* each with its own unique set of selectable field types, with field extendability.
* The Front-End modules allow you to build powerful listing and filtering of the
* data in each catalog.
*
* PHP version 5
* @copyright Martin Komara, Thyon Design, CyberSpectrum 2007-2009
* @author Martin Komara,
* John Brand <john.brand@thyon.com>,
* Christian Schiffler <c.schiffler@cyberspectrum.de>
* @package Catalog
* @license LGPL
* @filesource
*/
/**
* Class ModuleCatalog
*
* @copyright Martin Komara, Thyon Design, CyberSpectrum 2007-2009
* @author Martin Komara,
* John Brand <john.brand@thyon.com>,
* Christian Schiffler <c.schiffler@cyberspectrum.de>
* @package Controller
*
*/
abstract class ModuleCatalog extends Module
{
/**
* Tablename String
* @var string
*/
protected $strTable;
/**
* Name of the alias field
* @var string
*/
protected $strAliasField;
/**
* Search String
* @var string
*/
protected $strSearch = 'search';
/**
* Sort String
* @var string
*/
protected $strSort = 'sort';
/**
* OrderBy String
* @var string
*/
protected $strOrderBy = 'orderby';
protected $systemColumns = array('id', 'pid', 'sorting', 'tstamp');
protected $arrTree;
protected $cacheJumpTo;
public function generate()
{
if (!strlen($this->catalog))
{
return '';
}
// get DCA
$objCatalog = $this->Database->prepare('SELECT * FROM tl_catalog_types WHERE id=?')
->limit(1)
->execute($this->catalog);
if ($objCatalog->numRows > 0 && $objCatalog->tableName)
{
$this->strTable = $objCatalog->tableName;
$this->strAliasField=$objCatalog->aliasField;
$this->publishField=$objCatalog->publishField;
// dynamically load dca for catalog operations
$this->Import('Catalog');
if(!$GLOBALS['TL_DCA'][$objCatalog->tableName]['Cataloggenerated'])
{
// load language files and DC.
$this->loadLanguageFile($objCatalog->tableName);
$this->loadDataContainer($objCatalog->tableName);
// load default language
$GLOBALS['TL_LANG'][$objType->tableName] = is_array($GLOBALS['TL_LANG'][$objType->tableName])
? Catalog::array_replace_recursive($GLOBALS['TL_LANG']['tl_catalog_items'], $GLOBALS['TL_LANG'][$objType->tableName])
: $GLOBALS['TL_LANG']['tl_catalog_items'];
// load dca
$GLOBALS['TL_DCA'][$objCatalog->tableName] =
is_array($GLOBALS['TL_DCA'][$objCatalog->tableName])
? Catalog::array_replace_recursive($this->Catalog->getCatalogDca($this->catalog), $GLOBALS['TL_DCA'][$objCatalog->tableName])
: $this->Catalog->getCatalogDca($this->catalog);
$GLOBALS['TL_DCA'][$objCatalog->tableName]['Cataloggenerated'] = true;
}
}
// Send file to the browser
$blnDownload = ($this instanceof ModuleCatalogList || $this instanceof ModuleCatalogFeatured || $this instanceof ModuleCatalogRelated || $this instanceof ModuleCatalogReference || $this instanceof ModuleCatalogReader);
if ($blnDownload && strlen($this->Input->get('file')) && $this->catalog_visible)
{
foreach ($this->catalog_visible as $k)
{
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$k];
if ($fieldConf['eval']['catalog']['type'] == 'file' && !$fieldConf['eval']['catalog']['showImage'])
{
// check file in Catalog
$objDownload = $this->Database->prepare('SELECT id FROM '.$this->strTable.' WHERE '.(!BE_USER_LOGGED_IN && $this->publishField ? $this->publishField.'=1 AND ' : '').'(LOCATE(?,'.$k.')>0 OR LOCATE(?,'.$k.')>0)')
->limit(1)
->execute($this->Input->get('file'), dirname($this->Input->get('file')));
if ($objDownload->numRows)
{
$this->sendFileToBrowser($this->Input->get('file'));
}
}
}
}
return parent::generate();
}
protected function getModulesForThisPage()
{
if($this->cachePageModules)
return $this->cachePageModules;
global $objPage;
if($objPage->layout)
$objLayout = $this->Database->prepare('SELECT id,modules FROM tl_layout WHERE id=?')
->limit(1)
->execute($objPage->layout);
// Fallback layout
if (!$objPage->layout || $objLayout->numRows == 0)
{
$objLayout = $this->Database->prepare('SELECT id, modules FROM tl_layout WHERE fallback=?')
->limit(1)
->execute(1);
}
// check if there is a layout and fetch modules if so.
if ($objLayout->numRows)
{
$arrModules = deserialize($objLayout->modules);
} else {
$arrModules = array();
}
// fetch all content element modules from this page.
$objContent = $this->Database->prepare('SELECT module FROM tl_content WHERE pid IN (SELECT id FROM tl_article WHERE pid=?) AND type="module"')
->execute($objPage->id);
while($objContent->next())
{
$arrModules[] = array('mod' => $objContent->module);
}
$ids=array();
foreach ($arrModules as $arrModule)
{
$ids[] = $arrModule['mod'];
}
$this->cachePageModules=$ids;
return $this->cachePageModules;
}
protected function getCatalogFields($arrTypes=false)
{
if(!$arrTypes)
$arrTypes=$GLOBALS['BE_MOD']['content']['catalog']['typesCatalogFields'];
$fields = array();
$objFields = $this->Database->prepare("SELECT * FROM tl_catalog_fields WHERE pid=? ORDER BY sorting")
->execute($this->catalog);
while ($objFields->next())
{
if(!in_array($objFields->type, $arrTypes))
continue;
$fields[$objFields->colName] = array
(
'label' => $objFields->name,
'type' => $objFields->type,
);
}
return $fields;
}
protected function getTree()
{
if ($this->type != 'catalogfilter' || !$this->catalog_filter_enable)
{
return array();
}
$tree = array();
if($this->catalog_filters) {
$arrFilters = deserialize($this->catalog_filters, true);
foreach ($arrFilters as $key=>$fieldconfig)
{
list($field, $config) = each($fieldconfig);
if ($config['checkbox'] == 'tree')
{
$tree[] = $field;
}
}
}
return $tree;
}
protected function parseFilterUrl($searchFields=NULL)
{
$arrTree = $this->getTree();
$blnTree = (count($arrTree)>0);
$current = $this->convertAliasInput();
$searchFields = deserialize($searchFields);
// Setup Fields
$fields = $this->getCatalogFields();
if (!strlen($this->catalog_tags_mode))
{
$this->catalog_tags_mode = 'AND';
}
// Process POST redirect() settings
$doPost = false;
if ($this->Input->post('FORM_SUBMIT') == $this->strTable)
{
// search string POST
if (array_key_exists($this->strSearch, $_POST))
{
$doPost = true;
if ($this->Input->post($this->strSearch))
{
$current[$this->strSearch] = $this->Input->post($this->strSearch);
}
else
{
unset($current[$this->strSearch]);
}
}
// filters POST
foreach ($fields as $field=>$data)
{
// check if this is a filter
if (array_key_exists($field, $_POST))
{
$doPost = true;
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field];
// check if array posted (range and dates)
if (is_array($this->Input->post($field)))
{
$range = $this->Input->post($field);
$min = strlen($fieldConf['eval']['catalog']['minValue']) ?
max(min($range), $fieldConf['eval']['catalog']['minValue']) : min($range);
$max = strlen($fieldConf['eval']['catalog']['maxValue']) ?
min(max($range), $fieldConf['eval']['catalog']['maxValue']) : max($range);
if (strlen($max) && strlen($min))
{
$current[$field] = $min.'__'.$max;
}
if (in_array($fieldConf['eval']['catalog']['type'],array('number', 'decimal', 'date')))
{
$min='';
$max='';
if($fieldConf['eval']['catalog']['type'] == 'date')
{
if($range[0] && !is_numeric($range[0]))
$range[0] = strtotime($range[0]);
if($range[1] && !is_numeric($range[1]))
$range[1] = strtotime($range[1]);
}
if (strlen($range[0]))
$min=$range[0];
if (strlen($range[1]))
$max=$range[1];
if (strlen($max) || strlen($min))
{
$current[$field] = $min.'__'.$max;
}
}
}
// regular filter value
else
{
// use TL safe function
$v = $this->Input->post($field);
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field];
if (strlen($v) && $this->getAliasFieldConf($fieldConf) != 'id')
{
$arrAlias = array_flip($this->getAliasOptionList($fieldConf));
switch ($fieldConf['eval']['catalog']['type'])
{
case 'tags':
$tags = explode(',', $v);
$newtags = array();
foreach($tags as $tag)
{
$newtags[] = $arrAlias[$tag];
}
$v = implode(',', $newtags);
break;
case 'select':
$v = $arrAlias[$v];
break;
default:;
}
}
$current[$field] = $v;
}
}
}
// Redirect POST variables to GET, for [search] and [ranges]
if ($doPost)
{
$this->redirect($this->generateFilterUrl($current, false, false));
}
}
// return if no filter parameters in URL
if (!is_array($current) && !count($current))
{
return array();
}
// Setup Variables
$baseurl = $this->generateFilterUrl();
$procedure = array();
$values = array();
$procedure['search'] = null;
$values['search'] = null;
foreach ($fields as $field=>$data)
{
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field];
// GET search value
if ($this->Input->get($this->strSearch) && strlen($fieldConf['eval']['catalog']['type']))
{
if (is_array($searchFields) && !in_array($field, $searchFields))
{
continue;
}
switch ($fieldConf['eval']['catalog']['type'])
{
case 'text':
case 'longtext':
// explode the search by spaces and add the result to the query.
// this allows us to search for multiple words which do not have to be in the same order as searched by.
// Drawback is, we now can not search for exact phrases anymore (which is less required than searching for
// multiple words IMO).
// TODO: make this configable so users can decide which search algorithm to use.
$words=explode(' ', $this->Input->get($this->strSearch));
$proc=array();
$vals=array();
if(count($words))
{
$values['search'][$field]=array();
foreach($words as $word)
{
$proc[] = '('.$field.' LIKE ?)';
$values['search'][$field][] = '%'.$word.'%';
}
$procedure['search'][$field] = '('.implode(' AND ', $proc).')';
}
break;
case 'number':
case 'decimal':
$procedure['search'][$field] = '('.$field.' LIKE ?)';
$values['search'][$field] = '%'.$this->Input->get($this->strSearch).'%';
break;
case 'file':
case 'url':
$procedure['search'][$field] = '('.$field.' LIKE ?)';
// $values['search'][$field] = '%'.urldecode($this->Input->get($this->strSearch)).'%';
$values['search'][$field] = '%'.($this->Input->get($this->strSearch)).'%';
break;
case 'date':
// add month only search
if (!is_numeric($this->Input->get($this->strSearch)))
{
$procedure['search'][$field] = "CAST(MONTHNAME(FROM_UNIXTIME(".$field.")) AS CHAR) LIKE ?";
$values['search'][$field] = '%'.$this->Input->get($this->strSearch).'%';
}
// add numeric day, month, year search
else
{
foreach (array('YEAR','MONTH','DAY') as $function)
{
$tmpDate[] = "CAST(".$function."(FROM_UNIXTIME(".$field.")) AS CHAR) LIKE ?";
$values['search'][$field][] = '%'.$this->Input->get($this->strSearch).'%';
}
$procedure['search'][$field] = '('.implode(' OR ',$tmpDate).')';
}
break;
case 'checkbox' :
// search only if true
if (substr_count(strtolower($fieldConf['label']['0']),strtolower($this->Input->get($this->strSearch))))
{
$procedure['search'][$field] = '('.$field.'=?)';
$values['search'][$field] = '1';
}
break;
case 'select' :
list($itemTable, $valueCol) = explode('.', $fieldConf['eval']['catalog']['foreignKey']);
$procedure['search'][$field] = '('.$field.' IN (SELECT id FROM '.$itemTable.' WHERE '.$valueCol.' LIKE ?'.($fieldConf['options']? ' AND id IN ('.implode(',',array_keys($fieldConf['options'])).')':'').'))';
$values['search'][$field] = '%'.$this->Input->get($this->strSearch).'%';
break;
case 'tags' :
list($itemTable, $valueCol) = explode('.', $fieldConf['eval']['catalog']['foreignKey']);
// perform search by using a subselect over the tables.
$tagQuery = $this->Database->prepare(sprintf('SELECT DISTINCT(itemid) as id FROM tl_catalog_tag_rel WHERE fieldid=%s AND valueid IN (SELECT id FROM %s WHERE %s LIKE ? %s)',
$fieldConf['eval']['catalog']['fieldId'],
$itemTable,
$valueCol,
($fieldConf['options']? ' AND id IN ('.implode(',',array_keys($fieldConf['options'])).')':'')
))
->execute('%'.$this->Input->get($this->strSearch).'%');
if ($tagQuery->numRows)
{
$procedure['search'][$field] = 'id IN('.implode(',', $tagQuery->fetchEach('id')).')';
}
break;
default:;
// HOOK: Might be a custom field type, check if that one has registered a hook.
$fieldType=$GLOBALS['BE_MOD']['content']['catalog']['fieldTypes'][$fieldConf['eval']['catalog']['type']];
if(array_key_exists('generateFilter', $fieldType) && is_array($fieldType['generateFilter']))
{
foreach ($fieldType['generateFilter'] as $callback)
{
$this->import($callback[0]);
$tmp=$this->$callback[0]->$callback[1]($field, $fieldConf, $this->Input->get($this->strSearch));
$procedure['search'][$field] = $tmp['procedure'];
if(is_array($tmp['search']))
{
if(isset($values['search'][$field]))
$values['search'][$field]=array_merge($values['search'][$field], $tmp['search']);
else
$values['search'][$field]=$tmp['search'];
}
else
$values['search'][$field]=$tmp['search'];
}
}
}
} // of search
// GET range values
if (substr_count($this->Input->get($field),'__'))
{
$rangeValues = trimsplit('__', $this->Input->get($field), 2);
$rangeOptions[$field]['label'] = $fieldConf['label'][0];
$rangeOptions[$field]['min'] = $rangeValues[0];
$rangeOptions[$field]['max'] = $rangeValues[1];
$minValue = $rangeValues[0];
$maxValue = $rangeValues[1];
//$procedure['where'][] = '('.$field.' BETWEEN ? AND ?)';
$strSqlWhereClause = '('.$field.' BETWEEN ? AND ?)';
switch ($fieldConf['eval']['catalog']['type'])
{
case 'number':
$rangeValues[0] = intval($rangeValues[0]);
$rangeValues[1] = intval($rangeValues[1]);
break;
case 'decimal':
$rangeValues[0] = floatval($rangeValues[0]);
$rangeValues[1] = floatval($rangeValues[1]);
break;
case 'date':
$rangeValues[0] = strtotime($rangeValues[0]);
$rangeValues[1] = strtotime($rangeValues[1]);
break;
default:
}
if ($minValue!='')
$values['where'][] = $rangeValues[0];
else
$strSqlWhereClause = '('.$field.' < ?)';
if ($maxValue!='')
$values['where'][] = $rangeValues[1];
else
$strSqlWhereClause = '('.$field.' > ?)';
$procedure['where'][] = $strSqlWhereClause;
$current[$field] = $this->Input->get($field);
}
//GET filter values
elseif (strlen($this->Input->get($field)))
{
switch ($fieldConf['eval']['catalog']['type'])
{
case 'tags':
list($itemTable, $valueCol) = explode('.', $fieldConf['eval']['catalog']['foreignKey']);
// TODO: add support for string values here and get rid of the convertAliasInput call on the beginning.
foreach(explode(',', $current[$field]) as $tag)
$tags[] = (int)$tag;
if($this->catalog_tags_mode == 'AND')
{
$sql = sprintf('SELECT itemid FROM tl_catalog_tag_rel WHERE fieldid=%s AND valueid=%s',
$fieldConf['eval']['catalog']['fieldId'],
$tag);
foreach($tags as $tag)
$sql = sprintf('SELECT itemid FROM tl_catalog_tag_rel WHERE fieldid=%s AND valueid=%s AND itemid IN(%s)',
$fieldConf['eval']['catalog']['fieldId'],
$tag,
$sql);
$sql = sprintf('id IN (SELECT DISTINCT(itemid) FROM tl_catalog_tag_rel WHERE fieldid=%s AND itemid IN (%s) AND valueid IN (%s))',
$fieldConf['eval']['catalog']['fieldId'],
$sql,
implode(',', array_intersect($tags, ($fieldConf['options']?array_keys($fieldConf['options']):array())))
);
$tagQuery = $sql;
} else {
// perform search by using a subselect over the tables.
$tagQuery = 'id IN(SELECT DISTINCT(itemid) as id FROM tl_catalog_tag_rel WHERE fieldid='.$fieldConf['eval']['catalog']['fieldId'].' AND valueid IN ('.implode(',', array_intersect($tags, ($fieldConf['options']?array_keys($fieldConf['options']):array()))).'))';
}
$procedure['tags'][] = $tagQuery;
if ($blnTree && in_array($field, $arrTree))
{
$procedure['tree'][] = $tagQuery;
}
break;
case 'checkbox':
$procedure['where'][] = $field."=?";
$values['where'][] = ($this->Input->get($field) == 'true' ? 1 : 0);
//$current[$field] = $this->Input->get($field);
if ($blnTree && in_array($field, $arrTree))
{
$procedure['tree'][$field] = $field."=?";
$values['tree'][$field] = ($this->Input->get($field) == 'true' ? 1 : 0);
}
break;
case 'text':
case 'longtext':
case 'number':
case 'decimal':
case 'select':
$value = $current[$field];
if($value!==NULL)
{
$procedure['where'][] = $field."=?";
$values['where'][] = $value;
if ($blnTree && in_array($field, $arrTree))
{
$procedure['tree'][$field] = $field."=?";
$values['tree'][$field] = $value;
}
}
break;
case 'date':
$procedure['where'][] = $field."=?";
$values['where'][] = strtotime($this->Input->get($field));
//$current[$field] = $this->Input->get($field);
if ($blnTree && in_array($field, $arrTree))
{
$procedure['tree'][$field] = $field."=?";
$values['tree'][$field] = strtotime($this->Input->get($field));
}
break;
case 'file':
case 'url':
$procedure['where'][] = $field."=?";
$values['where'][] = urldecode($this->Input->get($field));
$current[$field] = $this->Input->get($field);
if ($blnTree && in_array($field, $arrTree))
{
$procedure['tree'][$field] = $field."=?";
$values['tree'][$field] = urldecode($this->Input->get($field));
}
break;
}
} // filter
// GET sort values
if ($this->Input->get($this->strOrderBy) == $field && in_array($this->Input->get($this->strSort), array('asc','desc')))
{
switch ($fieldConf['eval']['catalog']['type'])
{
case 'select':
list($itemTable, $valueCol) = explode('.', $fieldConf['eval']['catalog']['foreignKey']);
$procedure['orderby'] = '(SELECT '.$valueCol.' from '.$itemTable.' WHERE id='.$field.') '.$this->Input->get($this->strSort);
break;
default:
$procedure['orderby'] = $field.' '.$this->Input->get($this->strSort);
}
$current[$this->strOrderBy] = $this->Input->get($this->strOrderBy);
$current[$this->strSort] = $this->Input->get($this->strSort);
} //sort
} // foreach $filter
$settings = array
(
'current' => $current,
'procedure' => $procedure,
'values' => $values,
);
// HOOK: allow other extensions to manipulate the filter settings before passing it to the template
if(is_array($GLOBALS['TL_HOOKS']['filterCatalog']))
{
foreach ($GLOBALS['TL_HOOKS']['filterCatalog'] as $callback)
{
$this->import($callback[0]);
$settings = $this->$callback[0]->$callback[1]($settings);
}
}
return $settings;
}
/**
* Retrieve Alias field from table, checks if catalog
* @param string
* @return string
*/
public function getAliasField($sourceTable)
{
// check alias field
$objAlias = $this->Database->prepare("SELECT aliasField FROM tl_catalog_types WHERE tableName=?")
->execute($sourceTable);
$aliasField = ($objAlias->numRows && strlen($objAlias->aliasField)) ? $objAlias->aliasField : 'alias';
return ($this->Database->fieldExists($aliasField, $sourceTable) && !$GLOBALS['TL_CONFIG']['disableAlias']) ? $aliasField : 'id';
}
/**
* Retrieve alias field for current catalog field configuration
* @param array
* @return string
*/
private function getAliasFieldConf($fieldConf)
{
if (!$fieldConf['eval']['catalog']['foreignKey'])
{
return 'id';
}
// get alias column
list($itemTable, $valueCol) = explode('.', $fieldConf['eval']['catalog']['foreignKey']);
return $this->getAliasField($itemTable);
}
/**
* Retrieve alias values with id as index
* @param array
* @return array
*/
private function getAliasOptionList(&$fieldConf)
{
if($fieldConf['optionslist'])
return $fieldConf['optionslist'];
// get alias column
list($itemTable, $valueCol) = explode('.', $fieldConf['eval']['catalog']['foreignKey']);
$aliasField = $this->getAliasField($itemTable);
// determine item sorting.
$strSorting=($fieldConf['eval']['catalog']['sortCol']
?' ORDER BY '.$fieldConf['eval']['catalog']['sortCol']
:($this->Database->fieldExists('sorting', $itemTable)?' ORDER BY sorting':''));
// get existing alias values of options in DB
$objList = $this->Database->prepare('SELECT id,'.$aliasField.' FROM '.$itemTable .
($fieldConf['options'] ? ' WHERE id IN ('.implode(',',array_keys($fieldConf['options'])).')':'') . $strSorting)
->execute();
$return = array();
while ($objList->next())
{
// check if this is still ok to use id if alias is empty
$return[$objList->id] = strlen($objList->$aliasField) ? $objList->$aliasField : $objList->id;
}
$fieldConf['optionslist'] = $return;
return $return;
}
/**
* Detect input $_GET variables and convert alias values to id's, if table supports it
* @return array
*/
protected function convertAliasInput()
{
$return = array();
// convert $_GET filter parameters in Url
foreach ($_GET as $k=>$v)
{
// exclude page parameter
if (!in_array($k, array('page')))
{
$_GET[$k] = str_replace($GLOBALS['TL_CONFIG']['catalog']['safeReplace'], $GLOBALS['TL_CONFIG']['catalog']['safeCheck'], $v);
// use TL safe function
$v = $this->Input->get($k);
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$k];
if (strlen($v) && $this->getAliasFieldConf($fieldConf) != 'id')
{
$arrAlias = array_flip($this->getAliasOptionList($fieldConf));
switch ($fieldConf['eval']['catalog']['type'])
{
case 'tags':
$tags = explode(',', $v);
$newtags = array();
foreach($tags as $tag)
{
$newtags[] = $arrAlias[$tag];
}
$v = implode(',', $newtags);
break;
case 'select':
$v = $arrAlias[$v];
break;
default:;
}
}
$return[$k] = $v;
}
}
return $return;
}
protected function lastInTree($field, $current, $tree)
{
return (!count($tree) || in_array($field, $tree) && array_search($field, $tree)==(count($tree)-1));
}
protected function hideTree($field, $current, $tree)
{
if ($this->catalog_filter_hide && in_array($field, $tree))
{
$pos = array_search($field, $tree);
for ($i=0,$total=0;$i<=$pos;$i++)
{
$total += array_key_exists($tree[$i], $current);
}
return ($total<$pos);
}
return false;
}
protected function buildTreeQuery($field, $filterurl, $tree)
{
if (count($tree) && is_array($filterurl['procedure']['tree']))
{
$pos = array_search($field, $tree);
for ($i=0;$i<$pos;$i++)
{
if (strlen($filterurl['procedure']['tree'][$tree[$i]]))
$query[] = $filterurl['procedure']['tree'][$tree[$i]];
if (strlen($filterurl['values']['tree'][$tree[$i]]))
$params[] = $filterurl['values']['tree'][$tree[$i]];
}
}
return array (
'query' => (is_array($query) ? implode(' AND ', $query) : ''),
'params' => (is_array($params) ? $params : array()),
);
}
protected function clearTree($field, &$newcurrent, $tree)
{
if (in_array($field, $tree))
{
$pos = (array_search($field, $tree)+1);
for ($i=$pos;$i<=count($tree);$i++)
{
unset($newcurrent[$tree[$i]]);
}
}
}
protected function makeAllLabel($input, $label, $multi=false)
{
if ($input=='select' && !$multi)
{
return sprintf($GLOBALS['TL_LANG']['MSC']['selectNone'], $label);
}
return sprintf($GLOBALS['TL_LANG']['MSC']['clearAll'], $label);
}
protected function generateFilter()
{
$filterurl = $this->parseFilterUrl();
$current = $filterurl['current'];
$arrFilters = deserialize($this->catalog_filters, true);
if ($this->catalog_filter_enable && count($arrFilters))
{
// Get Tree View
$tree = $this->getTree();
// Setup filters and option values
$filterOptions = array();
foreach ($arrFilters as $fieldconfig)
{
list($field, $config) = each($fieldconfig);
$input = $config['radio'];
$blnTree = ($config['checkbox'] == 'tree');
if ($input == 'none' || $this->hideTree($field, $current, $tree))
{
continue;
}
$blnLast = $this->lastInTree($field, $current, $tree);
$query = $this->buildTreeQuery($field, $filterurl, $tree);
if(!BE_USER_LOGGED_IN && $this->publishField)
{
if(strlen($query['query']))
{
$query['query'].=' AND '.$this->publishField.'=1 ';
} else {
$query['query']=$this->publishField.'=1 ';
}
}
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field];
$fieldType = $fieldConf['eval']['catalog']['type'];
// HOOK: let custom fields mimic another fieldtype to generate a filter
if(array_key_exists($fieldType, $GLOBALS['BE_MOD']['content']['catalog']['fieldTypes']))
{
$fieldTypeArr=$GLOBALS['BE_MOD']['content']['catalog']['fieldTypes'][$fieldType];
if(array_key_exists('generateFilterWidget', $fieldTypeArr) && is_array($fieldTypeArr['generateFilterWidget']))
{
foreach ($fieldTypeArr['generateFilterWidget'] as $callback)
{
$this->import($callback[0]);
$tmp=$this->$callback[0]->$callback[1]($fieldType, $field, $config, $fieldConf, $filterurl, $query, $tree);
if($tmp)
{
$fieldType = $tmp;
}
}
}
}
$options = array();
switch ($fieldType)
{
case 'checkbox':
// Build Widget Options
$labels = array
(
'none' => $this->makeAllLabel($input, $fieldConf['label'][0]),
'true' => $GLOBALS['TL_LANG']['MSC']['true'],
'false' => $GLOBALS['TL_LANG']['MSC']['false'],
);
foreach ($labels as $key=>$label)
{
$newcurrent = $current;
$newcurrent[$field] = ($key == 'none' ? '' : $key);
$selected = $current[$field] == $newcurrent[$field];
$this->clearTree($field, $newcurrent, $tree);
$url = $this->generateFilterUrl($newcurrent, true, $blnLast);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $label;
$addOption['id'] = ($key == 'none' ? '' : $key);
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
}
$widget = array
(
'name' => $field,
'id' => 'filter_field_'.$field,
'label' => $fieldConf['label'][0],
'options' => serialize($options),
'value' => htmlentities($this->generateFilterUrl($current, true, $blnLast)),
'tableless' => true,
'inputType' => $input,
);
// parse Widget
$settings['filter'][] = $this->parseWidget($widget, false);
$widgets['filter'][] = $widget;
break;
case 'select':
if(($this instanceof ModuleCatalogFilter) && $this->catalog_filter_cond_from_lister)
{
$ids=$this->getModulesForThisPage();
$objModules = $this->Database->prepare('SELECT * FROM tl_module WHERE id IN (' . implode(', ', $ids) . ') AND deny_catalog_filter_cond_from_lister=0 AND type=\'cataloglist\' AND catalog='.$this->catalog)
->execute();
while($objModules->next())
{
$objModules->catalog_search=deserialize($objModules->catalog_search);
$moduleFilterUrl = $this->parseFilterUrl($objModules->catalog_search);
if (is_array($objModules->catalog_search) && strlen($objModules->catalog_search[0]) && is_array($moduleFilterUrl['procedure']['search']))
{
// reset arrays
$searchProcedure = array();
$searchValues = array();
foreach($objModules->catalog_search as $searchfield)
{
if (($searchfield != $field)
&& array_key_exists($searchfield, $moduleFilterUrl['current'])
&& array_key_exists($searchfield, $moduleFilterUrl['procedure']['search']))
{
$searchProcedure[] = $moduleFilterUrl['procedure']['search'][$searchfield];
if (is_array($moduleFilterUrl['values']['search'][$searchfield]))
{
foreach($moduleFilterUrl['values']['search'][$searchfield] as $item)
{
$searchValues[] = $item;
}
}
else
{
$searchValues[] = $moduleFilterUrl['values']['search'][$searchfield];
}
}
}
if(count($searchProcedure))
{
$moduleFilterUrl['procedure']['where'][] = ' ('.implode(' OR ', $searchProcedure).')';
$moduleFilterUrl['values']['where'] = is_array($moduleFilterUrl['values']['where']) ? (array_merge($moduleFilterUrl['values']['where'],$searchValues)) : $searchValues;
}
}
if(is_array($moduleFilterUrl['procedure']['where']))
{
foreach($moduleFilterUrl['procedure']['where'] as $key=>$value)
{
if(strpos($value, $field) !== false)
{
unset($moduleFilterUrl['procedure']['where'][$key]);
unset($moduleFilterUrl['values']['where'][$key]);
}
}
}
if(is_array($moduleFilterUrl['procedure']['tags']))
{
foreach($moduleFilterUrl['procedure']['tags'] as $key=>$value)
{
if(strpos($value, $field) !== false)
{
unset($moduleFilterUrl['procedure']['tags'][$key]);
unset($moduleFilterUrl['values']['tags'][$key]);
}
}
}
if (is_array($moduleFilterUrl['values']['where'])) {
$query['params'] = array_merge($query['params'], $moduleFilterUrl['values']['where']);
}
if (is_array($moduleFilterUrl['values']['tags'])) {
$query['params'] = array_merge($query['params'], $moduleFilterUrl['values']['tags']);
}
if($objModules->catalog_where)
{
$strCondition = $this->replaceInsertTags($objModules->catalog_where);
if(strlen($strCondition))
$query['query'] .= (strlen($query['query'])?' AND ':'').$strCondition;
}
if(count($moduleFilterUrl['procedure']['where']))
$query['query'] .=(strlen($query['query'])?' AND ':'').implode(' '.$objModules->catalog_query_mode.' ', $moduleFilterUrl['procedure']['where']);
if(count($moduleFilterUrl['procedure']['tags']))
$query['query'] .=(strlen($query['query'])?' AND ':'').implode(' '.$objModules->catalog_tags_mode.' ', $moduleFilterUrl['procedure']['tags']);
}
}
// get existing options in DB
$objFilter = $this->Database->prepare('SELECT DISTINCT('.$field.') FROM '.$this->strTable . ($query['query'] ? ' WHERE '.$query['query'] : '') )
->execute($query['params']);
if ($objFilter->numRows)
{
$selected = !strlen($current[$field]);
$newcurrent = $current;
unset($newcurrent[$field]);
$this->clearTree($field, $newcurrent, $tree);
$url = $this->generateFilterUrl($newcurrent, true, $blnLast);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $this->makeAllLabel($input, $fieldConf['label'][0]);
$addOption['id'] = '';
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
// get all rows
$rows = $objFilter->fetchEach($field);
if($fieldConf['options'])
{
$tmpTags = array();
foreach ($fieldConf['options'] as $id=>$option)
{
$tmpTags[] = 'SUM(FIND_IN_SET('.$id.','.$field.')) AS '.$field.$id;
}
$objResultCount = $this->Database->prepare('SELECT '.implode(', ',$tmpTags).' FROM '.$this->strTable. ($query['query'] ? ' WHERE '. $query['query'] : ''))
->execute($query['params']);
$arrResultCount = $objResultCount->row();
foreach ($fieldConf['options'] as $id=>$option)
{
if (in_array($id, $rows))
{
$selected = ($current[$field] == $id);
$newcurrent = $current;
$newcurrent[$field] = $id;
$this->clearTree($field, $newcurrent, $tree);
$url = $this->generateFilterUrl($newcurrent, true, $blnLast);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $option;
$addOption['id'] = $id;
$addOption['alias'] = $id;
$addOption['resultcount'] = $arrResultCount[$field.$id];
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
}
}
}
$widget = array
(
'name' => $field,
'id' => 'filter_field_'.$field,
'label' => $fieldConf['label'][0],
'value' => $this->generateFilterUrl($current, true, $blnLast),
'options' => serialize($options),
'tableless' => true,
'inputType' => $input,
);
// parse Widget
$settings['filter'][] = $this->parseWidget($widget, false);
$widgets['filter'][] = $widget;
}
break;
case 'tags' :
$query['query'] = '';
$query['params'] = is_array($filterurl['values']['where'])? $filterurl['values']['where'] : array();
if (is_array($filterurl['values']['tags']))
$query['params'] = array_merge($query['params'], $filterurl['values']['tags']);
if(count($filterurl['procedure']['where']))
$query['query'] .=(strlen($query['query'])?' AND ':'').implode(' AND ', $filterurl['procedure']['where']);
// TODO: we have a problem here if more than one tag field exists - we simply ignore the other one in here.
// if(count($filterurl['procedure']['tags']))
// $query['query'] .=(strlen($query['query'])?' AND ':'').implode(' AND ', $filterurl['procedure']['tags']);
// clear option
$selected = !strlen($current[$field]);
$newcurrent = $current;
unset($newcurrent[$field]);
$this->clearTree($field, $newcurrent, $tree);
$url = $this->generateFilterUrl($newcurrent, true, $blnLast);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $this->makeAllLabel($input, $fieldConf['label'][0], $this->catalog_tags_multi);
$addOption['id'] = $id;
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
$tmpTags = array();
// get ids of matches according to all other filters.
if($query['query'])
{
$objFilter = $this->Database->prepare('SELECT id FROM '.$this->strTable.' WHERE '.$query['query'])
->execute($query['params']);
$itemIds = implode(',',$objFilter->fetchEach('id'));
}
foreach ($fieldConf['options'] as $id=>$option)
{
$tmpTags[] = '(SELECT COUNT(itemid) FROM tl_catalog_tag_rel WHERE valueid='.$id.' AND fieldid='.$fieldConf['eval']['catalog']['fieldId'].($query['query'] && $itemIds?' AND itemid IN('.$itemIds.')':'').') AS '.$field.$id;
}
if(count($tmpTags)==0)
$tmpTags = array($field);
$objFilter = $this->Database->prepare('SELECT '.implode(', ',$tmpTags))
->execute($query['params']);
if ($objFilter->numRows)
{
$row = $objFilter->row();
foreach ($fieldConf['options'] as $id=>$option)
{
if ($row[$field.$id])
{
$selected = in_array($id, explode(',',$current[$field]));
$newcurrent = $current;
$newids = strlen($current[$field]) ? explode(',', $current[$field]) : array();
$newids = array_unique(!$selected ? array_merge($newids, array($id)) : array_diff($newids, array($id)));
$newcurrent[$field] = ($this->catalog_tags_multi ? implode(',',$newids) : $id);
$this->clearTree($field, $newcurrent, $tree);
$url = $this->generateFilterUrl($newcurrent, true, $blnLast);
$blnList = ($selected && $input=='list');
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $blnList ? sprintf($GLOBALS['TL_LANG']['MSC']['optionselected'], $option) : $option;
$addOption['id'] = $id;
$addOption['resultcount'] = $row[$field.$id];
$addOption['selected'] = $selected;
array_push($options, $addOption);
}
}
}
$widget = array
(
'name' => $field,
'id' => 'filter_field_'.$field,
'label' => $fieldConf['label'][0],
'value' => $this->generateFilterUrl($current, true, $blnLast),
'options' => serialize($options),
'tableless' => true,
'inputType' => ($this->catalog_tags_multi && $input=='radio' ? 'checkbox' : $input),
);
if ($this->catalog_tags_multi && $input=='select')
{
$widget = array_merge($widget, array('multiple'=>true));
}
$settings['filter'][] = $this->parseWidget($widget, false);
$widgets['filter'][] = $widget;
break;
case 'text':
case 'file':
case 'url':
case 'number':
case 'decimal':
case 'date':
$query['query'] = '';
// TODO: this is an evil hack - we DEFINATELY have to rewrite this lookup mechanism.
if(count($filterurl['procedure']['where']))
foreach($filterurl['procedure']['where'] as $k=>$v)
{
if(strpos($v, $field) !== false)
{
unset($filterurl['procedure']['where'][$k]);
unset($filterurl['values']['where'][$k]);
}
}
$query['params'] = is_array($filterurl['values']['where'])? $filterurl['values']['where'] : array();
if (is_array($filterurl['values']['tags']))
$query['params'] = array_merge($query['params'], $filterurl['values']['tags']);
if(count($filterurl['procedure']['where']))
$query['query'] .=(strlen($query['query'])?' AND ':'').implode(' AND ', $filterurl['procedure']['where']);
if(count($filterurl['procedure']['tags']))
$query['query'] .=(strlen($query['query'])?' AND ':'').implode(' AND ', $filterurl['procedure']['tags']);
// get existing options in DB
$options = array();
$objFilter = $this->Database->prepare("SELECT DISTINCT ".$field." FROM ".$this->strTable . ($query['query'] ? " WHERE ".$query['query'] : '') . " ORDER BY ".$field)
->execute($query['params']);
if ($objFilter->numRows)
{
// setup ALL option
$selected = !strlen($current[$field]);
$newcurrent = $current;
unset($newcurrent[$field]);
$this->clearTree($field, $newcurrent, $tree);
$url = $this->generateFilterUrl($newcurrent, true, $blnLast);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $this->makeAllLabel($input, $fieldConf['label'][0]);
$addOption['id'] = '';
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
while ($objFilter->next())
{
$row = $objFilter->row();
if (!strlen(trim($row[$field])))
{
continue;
}
$label = $this->formatValue(0, $field, $row[$field], false);
switch ($fieldConf['eval']['catalog']['type'])
{
case 'url':
$label = $row[$field];
$row[$field] = urlencode(urlencode($row[$field]));
break;
case 'file':
$label = implode(',',deserialize($row[$field],true));
$row[$field] = urlencode(urlencode($row[$field]));
break;
case 'date':
$row[$field] = $label;
break;
default:;
}
$newcurrent = $current;
$newcurrent[$field] = htmlspecialchars($row[$field]);
$selected = $current[$field] == $newcurrent[$field];
$this->clearTree($field, $newcurrent, $tree);
$url = $this->generateFilterUrl($newcurrent, true, $blnLast);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $label;
$addOption['id'] = $row[$field];
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
}
$widget = array
(
'name' => $field,
'id' => 'filter_field_'.$field,
'label' => $fieldConf['label'][0],
'options' => serialize($options),
'value' => htmlentities($this->generateFilterUrl($current, true, $blnLast)),
'tableless' => true,
'inputType' => $input,
);
// parse Widget
$settings['filter'][] = $this->parseWidget($widget, false);
$widgets['filter'][] = $widget;
}
break;
case 'longtext':
$options[] = array
(
'label' => &$GLOBALS['TL_LANG']['MSC']['invalidFilter'],
'value' => '',
);
$widget = array
(
'name' => $field,
'id' => 'filter_field_'.$field,
'label' => $fieldConf['label'][0],
'options' => serialize($options),
'value' => '',
'tableless' => true,
'inputType' => 'list',
);
// parse Widget
$settings['filter'][] = $this->parseWidget($widget, true);
$widgets['filter'][] = $widget;
break;
default:
// HOOK: let custom fields generate a filter widget
if($fieldType && array_key_exists($fieldType, $GLOBALS['BE_MOD']['content']['catalog']['fieldTypes']))
{
$fieldType=$GLOBALS['BE_MOD']['content']['catalog']['fieldTypes'][$fieldType];
if(array_key_exists('generateFilterForField', $fieldType) && is_array($fieldType['generateFilterForField']))
{
$callback = $fieldType['generateFilterForField'];
$tmp=$this->$callback[0]->$callback[1]($fieldType, $field, $config, $fieldConf, $filterurl, $query, $tree);
if($tmp)
{
$settings['filter'][] = $tmp['settings'];
$widgets['filter'][] = $tmp['widget'];
}
}
}
}
}
}
$arrRange = deserialize($this->catalog_range,true);
if ($this->catalog_range_enable && count($arrRange) && strlen($arrRange[0]))
{
// GET range values
$rangeOptions = array();
foreach ($arrRange as $field)
{
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field];
switch ($fieldConf['eval']['catalog']['type'])
{
case 'text':
case 'longtext':
case 'number':
case 'decimal':
case 'date':
$rangeValues = substr_count($this->Input->get($field),'__') ? trimsplit('__', $this->Input->get($field)) : array('','');
$current[$field] = $this->Input->get($field);
$widget = array
(
'name' => $field,
'id' => 'filter_range_'.$field,
'label' => ($i==0 ? $fieldConf['label'][0]:''),
'inputType' => 'range',
'value' => serialize($rangeValues),
'multiple' => true,
'size' => 2,
'tableless' => true,
'addSubmit' => true,
'slabel' => $GLOBALS['TL_LANG']['MSC']['catalogSearch'],
);
if ($fieldConf['eval']['catalog']['type'] == 'date')
{
$date = array
(
'maxlength' => 10,
'rgxp' => 'date'
);
// date picker was changed in 2.10
if (version_compare(VERSION, '2.10', '>='))
$date['datepicker'] = true;
else
$date['datepicker'] = $this->getDatePickerString();
$widget = array_merge($widget, $date);
}
$settings['range'][] = $this->parseWidget($widget, true);
$widgets['range'][] = $widget;
break;
default :;
}
}
}
// Setup date values
$arrDates = deserialize($this->catalog_dates,true);
$arrRanges = deserialize($this->catalog_date_ranges,true);
if ($this->catalog_date_enable && count($arrDates))
{
foreach ($arrDates as $fieldconfig)
{
list($field, $config) = each($fieldconfig);
$input = $config['radio'];
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field];
$options = array();
$selected = !strlen($current[$field]);
$newcurrent = $current;
unset($newcurrent[$field]);
$url = $this->generateFilterUrl($newcurrent, true);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $this->makeAllLabel($input, $fieldConf['label'][0]);
$addOption['id'] = '';
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
foreach ($arrRanges as $id=>$range)
{
$now = new Date();
$strToday = '';
$strYesterday = date($now->format, (strtotime('-1 days', $now->dayBegin) + 1));
$strTomorrow = date($now->format, (strtotime('+1 days', $now->dayBegin) + 1));
$strPast = '';
$strFuture = '';
switch ($range)
{
case 'd':
//$strPast = date($now->format, $now->dayEnd);
$strPast = date($now->format, (strtotime('-1 days', $now->dayBegin) + 1));
break;
case 'w':
$strPast = date($now->format, (strtotime('-7 days', $now->dayBegin) + 1));
break;
case 'm':
$strPast = date($now->format, (strtotime('-1 month', $now->dayBegin) + 1));
break;
case 'h':
$strPast = date($now->format, (strtotime('-6 month', $now->dayBegin) + 1));
break;
case 'y':
$strPast = date($now->format, (strtotime('-1 year', $now->dayBegin) + 1));
break;
case 't':
$strToday = date($now->format, $now->dayBegin);
break;
case 'df':
$strFuture = date($now->format, (strtotime('+1 days', $now->dayBegin) + 1));
break;
case 'wf':
$strFuture = date($now->format, (strtotime('+7 days', $now->dayBegin) + 1));
break;
case 'mf':
$strFuture = date($now->format, (strtotime('+1 month', $now->dayBegin) + 1));
break;
case 'hf':
$strFuture = date($now->format, (strtotime('+6 month', $now->dayBegin) + 1));
break;
case 'yf':
$strFuture = date($now->format, (strtotime('+1 year', $now->dayBegin) + 1));
break;
}
$newcurrent = $current;
$newcurrent[$field] = ($strPast ? $strPast . '__' .$strYesterday : '') . $strToday . ($strFuture ? $strTomorrow . '__' . $strFuture : '');
$selected = ($current[$field] == $newcurrent[$field]);
$url = $this->generateFilterUrl($newcurrent, true);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = &$GLOBALS['TL_LANG']['MSC']['daterange'][$range];
$addOption['id'] = $range;
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
}
$widget = array
(
'name' => $field,
'id' => 'filter_date_'.$field,
'label' => $fieldConf['label'][0],
'value' => $this->generateFilterUrl($current, true),
'tableless' => true,
'options' => serialize($options),
'inputType' => $input,
);
$settings['date'][] = $this->parseWidget($widget, false);
$widgets['date'][] = $widget;
}
}
$arrSort = deserialize($this->catalog_sort,true);
if ($this->catalog_sort_enable && count($arrSort))
{
// Setup sort values
$options = array();
if (count($arrSort) && $this->catalog_sort_type!='list')
{
$selected = !strlen($current[$field]);
$newcurrent = $current;
unset($newcurrent[$this->strOrderBy]);
unset($newcurrent[$this->strSort]);
$url = $this->generateFilterUrl($newcurrent, true);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $GLOBALS['TL_LANG']['MSC']['unsorted'];
$addOption['id'] = '';
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
}
foreach ($arrSort as $id=>$field)
{
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$field];
$newcurrent = $current;
$newcurrent[$this->strOrderBy] = $field;
foreach (array('asc','desc') as $order)
{
$newcurrent[$this->strSort] = $order;
$selected = ($current[$this->strSort] == $newcurrent[$this->strSort] && $current[$this->strOrderBy] == $newcurrent[$this->strOrderBy]);
$url = $this->generateFilterUrl($newcurrent, true);
$addOption = array();
$addOption['value'] = $url;
$addOption['label'] = $fieldConf['label'][0] . ' ' . $this->selectSortLabel($fieldConf['eval']['catalog']['type'], ($order=='asc'));
$addOption['id'] = $field.'__'.$order;
if ($selected)
{
$addOption['selected'] = true;
}
array_push($options, $addOption);
}
}
$widget = array
(
'name' => $this->strSort,
'id' => 'filter_'.$this->strSort,
'value' => $this->generateFilterUrl($current, true),
'tableless' => true,
'options' => serialize($options),
'inputType' => $this->catalog_sort_type,
);
$settings['sort'] = $this->parseWidget($widget, false);
$widgets['sort'] = $widget;
}
if ($this->catalog_search_enable)
{
$widget = array
(
'name' => $this->strSearch,
'id' => 'filter_'.$this->strSearch,
'inputType' => 'text',
'value' => $this->Input->get($this->strSearch),
'tableless' => true,
'addSubmit' => true,
'slabel' => $GLOBALS['TL_LANG']['MSC']['catalogSearch'],
);
$settings['search'] = $this->parseWidget($widget);
$widgets['search'] = $widget;
}
$settings['url'] = $this->generateFilterUrl();
$settings['action'] = $this->generateFilterUrl($current);
$settings['widgets'] = $widgets;
if(is_array($GLOBALS['TL_HOOKS']['generateFilterCatalog']))
{
foreach ($GLOBALS['TL_HOOKS']['generateFilterCatalog'] as $callback)
{
$this->import($callback[0]);
$settings = $this->$callback[0]->$callback[1]($this,$settings);
}
}
return $settings;
}
public function parseWidget(&$widget)
{
$this->addWidgetAttributes($widget);
$class = $widget['inputType'];
$options = deserialize($widget['options']);
$return = '';
$label = $widget['label'] ? sprintf(
'<h3><label for="%s">%s</label></h3>
',
'ctrl_'.$widget['id'],
$widget['label'])
: '';
switch ($widget['inputType'])
{
case 'list':
$return = sprintf(
'%s<div id="%s" class="list_container">
<ul class="list%s">',
$label,
'ctrl_'.$widget['id'],
(strlen($widget['class']) ? ' ' . $widget['class'] : '')
);
foreach ($options as $id=>$option)
{
$class = standardize($option['id']);
$class = ($class == '' ? 'none' : $class);
$selected = $option['selected'];
$return .= sprintf('
<li class="option%s%s"><a href="%s" title="%s">%s</a></li>',
' list_'.$class,
($selected ? ' active' : ''),
$option['value'],
$option['label'],
$option['label']
);
}
$return .= '
</ul></div>';
break;
case 'range':
$widget['value'] = deserialize($widget['value'], true);
$arrFields = array();
for ($i=0; $i<2; $i++)
{
$datepicker = ($widget['datepicker'] ? '
<script type="text/javascript"><!--//--><![CDATA[//><!--
window.addEvent(\'domready\', function() { ' . sprintf($widget['datepicker'], 'ctrl_' . $widget['id'] .'_'.$i) . ' });
//--><!]]></script>'
: '');
// adding a <label for=""> to the inputs
$strLabel = ($i == 0) ? $GLOBALS['TL_LANG']['MSC']['rangeFrom'] : $GLOBALS['TL_LANG']['MSC']['rangeTo'];
$arrFields[] = sprintf('%s<input type="text" name="%s[]" id="ctrl_%s" class="text%s" value="%s"%s />',
(strlen($strLabel)) ? '<label for="ctrl_' . $widget['id'].'_'.$i . '">' . $strLabel . '</label>' : '',
$widget['name'],
$widget['id'].'_'.$i,
(strlen($widget['class']) ? ' ' . $widget['class'] : ''),
specialchars($widget['value'][$i]),
$widget['attributes'])
. $datepicker . ($i==1 ? $this->addSubmit($widget) : '');
}
$return = sprintf('%s<div id="ctrl_%s"%s>%s</div>',
$label,
$widget['id'],
(strlen($widget['class']) ? ' class="' . $widget['class'] . '"' : ''),
implode(($widget['separator'] ? $widget['separator'] : ' '), $arrFields));
break;
case 'text':
$return = sprintf('<input type="text" name="%s" id="ctrl_%s" class="text%s" value="%s"%s />',
$widget['name'],
$widget['id'],
(strlen($widget['class']) ? ' ' . $widget['class'] : ''),
specialchars($widget['value']),
$widget['attributes'])
. $this->addSubmit($widget);
break;
case 'radio':
$arrOptions = array();
foreach ($options as $i=>$option)
{
$arrOptions[] = sprintf('<input type="radio" name="%s" id="opt_%s" class="radio%s" value="%s"%s%s /> <label for="opt_%s">%s</label>',
$widget['name'],
$widget['id'].'_'.$option['id'],
(strlen($widget['class']) ? ' ' . $widget['class'] : ''),
specialchars($option['value']),
($option['selected'] ? ' checked="checked"' : ''),
$widget['attributes'],
// $widget['id'].'_'.$i,
$widget['id'].'_'.$option['id'], // we have to use the proper ID otherwise the label won't be attached to the input.
$option['label']);
}
// Add a "no entries found" message if there are no options
if (!count($options))
{
$arrOptions[]= '<p class="tl_noopt">'.$GLOBALS['TL_LANG']['MSC']['noResult'].'</p>';
}
$return = sprintf('%s<div id="ctrl_%s" class="radio_container%s">%s</div>',
$label,
$widget['id'],
(strlen($widget['class']) ? ' ' . $widget['class'] : ''),
implode(($widget['separator'] ? $widget['separator'] : '<br />'), $arrOptions));
break;
case 'checkbox':
$arrOptions = array();
foreach ($options as $i=>$option)
{
$arrOptions[] = sprintf('<span><input type="checkbox" name="%s" id="opt_%s" class="checkbox%s" value="%s"%s%s /> <label for="opt_%s">%s</label></span>',
$widget['name'] . ($widget['multiple'] ? '[]' : ''), // name
$widget['id'].'_'.$option['id'], // id
(strlen($widget['class']) ? ' ' . $widget['class'] : ''), // class
($widget['multiple'] ? specialchars($option['value']) : 1), // value
($option['selected'] ? ' checked="checked"' : ''), // state
$widget['attributes'], // more attributes
// $widget['id'].'_'.$i, // the id again
$widget['id'].'_'.$option['id'], // the id again
$option['label']); // and the label
}
// Add a "no entries found" message if there are no options
if (!count($options))
{
$arrOptions[]= '<p class="noopt">'.$GLOBALS['TL_LANG']['MSC']['noResult'].'</p>';
}
$return = sprintf('%s<div id="ctrl_%s" class="%s%s">%s</div>',
$label,
$widget['id'],
($widget['multiple'] ? 'checkbox_container' : 'checkbox_single_container'),
(strlen($widget['class']) ? ' ' . $widget['class'] : ''),
// implode(($widget['separator'] ? $widget['separator'] : '<br />'), $arrOptions));
// c.schiffler - removed the separator as if none specified, we do not want one. <br /> is evil IMO.
implode(($widget['separator'] ? $widget['separator'] : ''), $arrOptions));
break;
case 'select':
// Add empty option (XHTML) if there are none
if (!count($options))
{
$options = array(array('value'=>'', 'label'=>'-'));
}
foreach ($options as $option)
{
$strOptions .= sprintf('<option value="%s"%s>%s</option>',
$option['value'],
($option['selected'] ? ' selected="selected"' : ''),
$option['label']);
}
$return = sprintf('%s<select name="%s" id="ctrl_%s" class="%s%s"%s%s>%s</select>',
$label,
$widget['name'] . ($widget['multiple'] ? '[]' : ''),
$widget['id'],
($widget['multiple'] ? 'multi'.$class : $class),
(strlen($widget['class']) ? ' ' . $widget['class'] : ''),
($widget['multiple'] ? ' multiple="multiple"' : ''),
$widget['attributes'],
$strOptions);
break;
default:;
}
return '<div class="widget'.(strlen($widget['id']) ? ' ' . $widget['id'] : '').'">
'.$return.'
</div>
';
}
protected function addSubmit($widget)
{
return (strlen($widget['slabel']) ? sprintf(' <input type="submit" id="ctrl_%s_submit" class="submit" value="%s" />',
$widget['id'],
specialchars($widget['slabel']))
: '');
}
private function addWidgetAttributes(&$widget)
{
switch ($widget['inputType'])
{
case 'radio':
$types = array
(
'attributes' => ' onclick="window.location=this.value"',
);
break;
case 'checkbox':
$types = array
(
'multiple' => true,
'attributes' => ' onclick="window.location=this.value"',
);
break;
case 'select':
$types = array
(
'attributes' => $widget['multiple']
? ' onclick="window.location=this.options[this.selectedIndex].value"'
: ' onchange="window.location=this.options[this.selectedIndex].value"',
);
break;
default:;
}
if (is_array($types))
{
$widget = array_merge($widget, $types);
}
}
private function checkArray($arrInput)
{
return is_array($arrInput) ? $arrInput : array();
}
protected function selectSortLabel($inputType, $blnAsc=true)
{
$labelSuffix = '';
switch ($inputType)
{
case 'text':
case 'longtext':
case 'select':
case 'tags':
case 'url':
case 'file':
$labelSuffix = $blnAsc ? $GLOBALS['TL_LANG']['MSC']['AtoZ']: $GLOBALS['TL_LANG']['MSC']['ZtoA'];
break;
case 'number':
case 'decimal':
$labelSuffix = $blnAsc ? $GLOBALS['TL_LANG']['MSC']['lowhigh']: $GLOBALS['TL_LANG']['MSC']['highlow'];
break;
case 'checkbox':
$labelSuffix = $blnAsc ? $GLOBALS['TL_LANG']['MSC']['falsetrue']: $GLOBALS['TL_LANG']['MSC']['truefalse'];
break;
case 'date':
$labelSuffix = $blnAsc ? $GLOBALS['TL_LANG']['MSC']['dateasc']: $GLOBALS['TL_LANG']['MSC']['datedesc'];
break;
default:
if($blnAsc)
{
if($GLOBALS['TL_LANG']['MSC'][$inputType.'asc'])
$labelSuffix=$GLOBALS['TL_LANG']['MSC'][$inputType.'asc'];
} else {
if($GLOBALS['TL_LANG']['MSC'][$inputType.'desc'])
$labelSuffix=$GLOBALS['TL_LANG']['MSC'][$inputType.'desc'];
}
}
return $labelSuffix;
}
private function getJumpTo($catalogJump, $blnJumpTo=true)
{
global $objPage;
if(!$blnJumpTo)
return $objPage->row();
if ($this->cacheJumpTo[$catalogJump])
{
return $this->cacheJumpTo[$catalogJump];
}
else
{
if ($catalogJump && $blnJumpTo)
{
// Get current "jumpTo" page
$objJump = $this->Database->prepare("SELECT * FROM tl_page WHERE id=?")
->execute($catalogJump);
if ($objJump->numRows)
{
$pageRow = $objJump->row();
}
}
else
{
$pageRow = $objPage->row();
}
// cacheJumpTo
$this->cacheJumpTo[$pageRow['id']] = $pageRow;
return $pageRow;
}
}
public function generateFilterUrl($arrGet = array(), $blnRoot=false, $blnJumpTo=true)
{
$arrPage=$this->getJumpTo($this->catalog_jumpTo, $blnJumpTo);
$strParams = '';
if (is_array($arrGet) && ($arrGet))
{
foreach ($arrGet as $k=>$v)
{
if (strlen($v))
{
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$k];
if (in_array($fieldConf['eval']['catalog']['type'], array('select', 'tags'))
&& $this->getAliasFieldConf($fieldConf) != 'id')
{
$arrAlias = $this->getAliasOptionList($fieldConf);
if ($fieldConf['eval']['catalog']['type'] == 'tags')
{
$tags = explode(',', $v);
$newtags = array();
foreach($tags as $tag)
{
$newtags[] = $arrAlias[$tag];
}
$v = implode(',', $newtags);
}
else
{
$v = $arrAlias[$v];
}
}
$v = str_replace($GLOBALS['TL_CONFIG']['catalog']['safeCheck'], $GLOBALS['TL_CONFIG']['catalog']['safeReplace'], $v);
$strParams .= $GLOBALS['TL_CONFIG']['disableAlias'] ? '&' . $k . '=' . $v : '/' . $k . '/' . $v;
}
}
}
return ($blnRoot ? $this->Environment->base : '') . $this->generateFrontEndUrl($arrPage, $strParams);
}
/**
* Translate SQL if needed (needed for calculated fields)
* @param array
* @return array
*/
protected function processFieldSQL($arrVisible)
{
$arrConverted = array();
// iterate all catalog fields
$objFields = $this->Database->prepare("SELECT * FROM tl_catalog_fields f WHERE f.pid=(SELECT c.id FROM tl_catalog_types c WHERE c.tableName=?)")
->execute($this->strTable);
$arrFields = array();
if ($objFields->numRows)
{
while ($objFields->next())
{
$row = $objFields->row();
$arrFields[$row['colName']] = $row;
}
foreach ($arrVisible as $id=>$field)
{
if (array_key_exists($field, $arrFields))
{
switch ($arrFields[$field]['type'])
{
case 'calc':
// set query value to forumla
$value = '('.$arrFields[$field]['calcValue'].') AS '.$field; //.'_calc';
$arrConverted[$id] = $value;
break;
default:
$arrConverted[$id] = $field;
}
}
// HOOK: allow third party extension developers to prepare the SQL data
if(is_array($GLOBALS['TL_HOOKS']['processFieldSQL']) && count($GLOBALS['TL_HOOKS']['processFieldSQL']))
{
foreach($GLOBALS['TL_HOOKS']['processFieldSQL'] as $callback)
{
$this->import($callback[0]);
$this->$callback[0]->$callback[1]($this->catalog, $id, $field, $arrFields, $arrConverted, $this->strTable);
}
}
}
}
return $arrConverted;
}
/**
* Generate one or more items and return them as array
* @param object
* @param boolean
* @return array
*/
protected function generateCatalog(Database_Result $objCatalog, $blnLink=true, $visible=null, $blnImageLink=false)
{
$i=0;
$arrCatalog = array();
$objCatalog->reset();
while ($objCatalog->next())
{
$arrCatalog[$i]['id'] = $objCatalog->id;
$arrCatalog[$i]['catalog_name'] = $objCatalog->catalog_name;
$arrCatalog[$i]['parentJumpTo'] = $objCatalog->parentJumpTo;
$arrCatalog[$i]['tablename'] = $this->strTable;
$arrCatalog[$i]['showLink'] = (!$this->catalog_link_override);
// get alias field WARNING -- check if blnLink is needed for edit mode where alias is also used
if ($i==0 && $blnLink)
{
$objArchive = $this->Database->prepare("SELECT aliasField FROM tl_catalog_types where id=?")
->limit(1)
->execute($objCatalog->pid);
$aliasField = $objArchive->numRows ? $objArchive->aliasField : 'alias';
}
$class = (($i == 0) ? ' first' : '') . ((($i + 1) == $objCatalog->numRows) ? ' last' : '') . ((($i % 2) == 0) ? ' even' : ' odd');
$arrCatalog[$i]['class'] = $class;
if ($blnLink)
{
$arrCatalog[$i]['link'] = $this->generateLink($objCatalog, $aliasField, $this->strTable, $this->catalog_link_window);
$arrCatalog[$i]['url'] = $this->generateCatalogUrl($objCatalog, $aliasField, $this->strTable);
}
$arrData = $objCatalog->row();
// check if editing of this record is disabled for frontend.
$editingallowedByFields=true;
foreach ($GLOBALS['TL_DCA'][$this->strTable]['fields'] as $fieldname=>$field)
{
if(is_array($visible) && !in_array($fieldname, $visible))
continue;
// HOOK: additional permission checks if this field allows editing of this record (for the current user).
$fieldType = $GLOBALS['BE_MOD']['content']['catalog']['fieldTypes'][$field['eval']['catalog']['type']];
if(is_array($fieldType) && array_key_exists('checkPermissionFERecordEdit', $fieldType) && is_array($fieldType['checkPermissionFERecordEdit']))
{
foreach ($fieldType['checkPermissionFERecordEdit'] as $callback)
{
$this->import($callback[0]);
// TODO: Do we need more parameters here?
if(!($this->$callback[0]->$callback[1]($this->strTable, $fieldname, $arrData)))
{
$editingallowedByFields=false;
break;
}
}
}
}
if ($this->catalog_edit_enable && $editingallowedByFields)
{
$arrCatalog[$i]['linkEdit'] = $this->generateLink($objCatalog, $aliasField, $this->strTable, $this->catalog_link_window, true);
$arrCatalog[$i]['urlEdit'] = $this->generateCatalogEditUrl($objCatalog, $aliasField, $this->strTable);
}
if (is_array($visible))
{
foreach($visible as $field)
{
$tmpData[$field] = $arrData[$field];
}
$arrData = $tmpData;
}
foreach ($arrData as $k=>$v)
{
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$k];
$blnParentCheckbox = $fieldConf['eval']['catalog']['parentCheckbox'] && !$arrData[$fieldConf['eval']['catalog']['parentCheckbox']];
if (in_array($k, array('id','pid','sorting','tstamp')) || $fieldConf['inputType'] == 'password' || $blnParentCheckbox)
continue;
$strLabel = strlen($label = $fieldConf['label'][0]) ? $label : $k;
$strType = $fieldConf['eval']['catalog']['type'];
$arrValues = $this->parseValue($this->type.$i, $k, $v, $blnImageLink, $objCatalog);
$linked = deserialize($this->catalog_islink, true);
if ($this->catalog_link_override && is_array($linked) && in_array($k, $linked))
{
$arrValues['html'] = $this->generateLink($objCatalog, $aliasField, $this->strTable, $this->catalog_link_window, false, $arrValues['html']);
}
$arrCatalog[$i]['data'][$k] = array
(
'label' => $strLabel,
'type' => $strType,
'raw' => $v,
'value' => ($arrValues['html'] ? $arrValues['html'] : '')
);
switch ($strType)
{
case 'select':
case 'tags':
list($refTable, $valueCol) = explode('.', $fieldConf['eval']['catalog']['foreignKey']);
if (strlen(trim($v)))
{
// set sort order
$sortCol = $fieldConf['eval']['catalog']['sortCol'];
if (!strlen($sortCol))
{
$sortCol = 'sorting';
}
$sortOrder = $this->Database->fieldExists($sortCol, $refTable) ? $sortCol : $refCol;
// Get referenced fields
$objRef = $this->Database->prepare('SELECT * FROM '.$refTable.' WHERE id IN ('.trim($v).')'.(strlen($sortOrder)?' ORDER BY '.$sortOrder:''))
->execute();
if ($objRef->numRows)
{
// Get Ref Catalog JumpTo
$objJump = $this->Database->prepare("SELECT tableName, aliasField, jumpTo FROM tl_catalog_types WHERE tableName=?")
->limit(1)
->execute($refTable);
// Add Ref Catalog Links
if ($objJump->numRows)
{
while ($objRef->next())
{
$objRef->parentJumpTo = $objJump->jumpTo;
$objRef->parentLink = $this->generateLink($objRef, $objJump->aliasField, $objJump->tableName, $this->catalog_link_window);
$objRef->parentUrl = $this->generateCatalogUrl($objRef, $objJump->aliasField, $objJump->tableName);
}
}
// add to reference array
$arrCatalog[$i]['data'][$k]['ref'] = $objRef->fetchAllAssoc();
}
}
break;
case 'file':
case 'image':
// add file and image information
$arrCatalog[$i]['data'][$k]['files'] = $arrValues['items'];
$arrCatalog[$i]['data'][$k]['meta'] = $arrValues['values'];
break;
default:
// per default we deliver all other keys aswell to allow custom field types to transport custom data.
foreach($arrValues as $kk => $vv)
{
if($kk != 'html')
$arrCatalog[$i]['data'][$k][$kk] = $vv;
}
}
}
// HOOK: allow other extensions to manipulate the item before returning them in the array
if(is_array($GLOBALS['TL_HOOKS']['generateCatalogItem']))
{
foreach ($GLOBALS['TL_HOOKS']['generateCatalogItem'] as $callback)
{
$this->import($callback[0]);
$arrCatalog[$i] = $this->$callback[0]->$callback[1]($arrCatalog[$i], $arrData, $this);
}
}
$i++;
}
return $arrCatalog;
}
/**
* Parse one or more items and pipe them through the given template
* @param object
* @param boolean
* @return array
*/
protected function parseCatalog(Database_Result $objCatalog, $blnLink=true, $template='catalog_full', $visible=null, $blnImageLink=false)
{
$objTemplate = new FrontendTemplate($template);
$arrCatalog = $this->generateCatalog($objCatalog, $blnLink, $visible, $blnImageLink);
// HOOK: allow other extensions to manipulate the items before passing it to the template
if(is_array($GLOBALS['TL_HOOKS']['parseCatalog']))
{
foreach ($GLOBALS['TL_HOOKS']['parseCatalog'] as $callback)
{
$this->import($callback[0]);
$arrCatalog = $this->$callback[0]->$callback[1]($arrCatalog, $objTemplate, $this);
}
}
$objTemplate->entries = $arrCatalog;
$objTemplate->moduleTemplate = $this->Template;
$objTemplate->noItemsMsg = $GLOBALS['TL_LANG']['MSC']['noItemsMsg'];
return $objTemplate->parse();
}
protected function generateCatalogUrl(Database_Result $objCatalog, $aliasField='alias', $strTable, $strPrependParams='')
{
if (!strlen($aliasField))
{
$aliasField = 'alias';
}
$useJump = ($this instanceof ModuleCatalogList || $this instanceof ModuleCatalogFeatured || $this instanceof ModuleCatalogRelated || $this instanceof ModuleCatalogReference || $this instanceof ModuleCatalogNavigation);
$jumpTo = ($useJump && $this->jumpTo) ? $this->jumpTo : $objCatalog->parentJumpTo;
$arrPage=$this->getJumpTo($jumpTo, true);
return ampersand($this->generateFrontendUrl($arrPage, $strPrependParams . '/items/' . (($this->Database->fieldExists($aliasField, $strTable) && strlen($objCatalog->$aliasField) && !$GLOBALS['TL_CONFIG']['disableAlias']) ? $objCatalog->$aliasField : $objCatalog->id)));
}
private function generateCatalogEditUrl(Database_Result $objCatalog, $aliasField='alias', $strTable)
{
if (!strlen($aliasField))
{
$aliasField = 'alias';
}
$arrPage=$this->getJumpTo($this->catalog_editJumpTo, true);
// Link to catalog edit
return ampersand($this->generateFrontendUrl($arrPage, '/items/' . (($this->Database->fieldExists($aliasField, $strTable) && strlen($objCatalog->$aliasField) && !$GLOBALS['TL_CONFIG']['disableAlias']) ? $objCatalog->$aliasField : $objCatalog->id)));
}
/**
* Generate a link and return it as string
* @param string
* @param object
* @param boolean
* @return string
*/
protected function generateLink(Database_Result $objCatalog, $aliasField, $strTable, $blnWindow, $blnEdit=false, $strLink='')
{
$linkUrl = (!$blnEdit ? $this->generateCatalogUrl($objCatalog, $aliasField, $strTable) : $this->generateCatalogEditUrl($objCatalog, $aliasField, $strTable));
$strLink = strlen($strLink) ? $strLink : (!$blnEdit ? $GLOBALS['TL_LANG']['MSC']['viewCatalog'] : $GLOBALS['TL_LANG']['MSC']['editCatalog']);
$strTitle = (!$blnEdit ? $GLOBALS['TL_LANG']['MSC']['viewCatalog'] : $GLOBALS['TL_LANG']['MSC']['editCatalog']);
return sprintf('<a href="%s" title="%s"%s>%s</a>',
$linkUrl,
$strTitle,
$blnWindow ? ' onclick="this.blur(); window.open(this.href); return false;"' : '',
$strLink
);
}
/**
* Format the catalog value according to its settings and return it as HTML string
* @param integer
* @param string
* @param variable
* @param boolean
* @return string
*/
protected function formatValue($id, $k, $value, $blnImageLink=true, $objCatalog=array())
{
$arrFormat = $this->parseValue($id, $k, $value, $blnImageLink, $objCatalog);
return $arrFormat['html'];
}
/**
* parse the catalog values and return information as an array
* @param integer
* @param string
* @param variable
* @param boolean
* @return array
*/
protected function parseValue($id, $k, $value, $blnImageLink=true, $objCatalog=array())
{
$raw = $value;
$arrItems = deserialize($value, true);
$arrValues = deserialize($value, true);
$strHtml = $value;
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$k];
if((version_compare(VERSION, '2.10', '>=')) && $fieldConf['eval']['rte'] && $GLOBALS['TL_CONFIG']['useRTE'])
{
global $objPage;
$this->import('String');
// reformat the RTE output to the proper format
if($objPage->outputFormat == 'xhtml')
{
$strHtml = $this->String->toXhtml($strHtml);
}
if($objPage->outputFormat == 'html5')
{
$strHtml = $this->String->toHtml5($strHtml);
}
}
switch ($fieldConf['eval']['catalog']['type'])
{
case 'select':
case 'tags':
if ($fieldConf['options'])
{
$arrItems = trimsplit(',',$raw);
$selectedValues = array_intersect_key($fieldConf['options'], array_flip($arrItems));
$arrValues = $selectedValues;
$strHtml = implode(', ', $arrValues);
}
break;
case 'file':
$files = $this->parseFiles($id, $k, $raw);
$arrItems = $files['files'];
$arrValues = $files['src'];
$strHtml = implode('', $files['html']);
break;
case 'url':
if (strlen($raw))
{
// E-mail addresses
if (preg_match_all('/^(mailto:)?(\w+([_\.-]*\w+)*@\w+([_\.-]*\w+)*\.[a-z]{2,6})$/i', $value, $matches))
{
$this->import('String');
$emailencode = $this->String->encodeEmail($matches[2][0]);
$arrValues[0] = $emailencode;
$strHtml = '<a href="mailto:' . $emailencode . '">' . $emailencode . '</a>';
}
elseif (preg_match_all('@^(https?://|ftp://)(\w+([_\.-]*\w+)*\.[a-z]{2,6})(/?)@i', $value, $matches))
{
$arrValues[0] = $raw;
$website = $matches[2][0];
$strHtml = '<a href="'.ampersand($raw).'"'.(preg_match('@^(https?://|ftp://)@i', $value) ? ' onclick="window.open(this.href); return false;"' : '').'>'.$website.'</a>';
}
}
break;
// allow custom fields.
default:
if(array_key_exists($fieldConf['eval']['catalog']['type'], $GLOBALS['BE_MOD']['content']['catalog']['fieldTypes']))
{
// HOOK: try to format the fieldtype as it is a custom added one.
$fieldType=$GLOBALS['BE_MOD']['content']['catalog']['fieldTypes'][$fieldConf['eval']['catalog']['type']];
if(array_key_exists('parseValue', $fieldType) && is_array($fieldType['parseValue']))
{
foreach ($fieldType['parseValue'] as $callback)
{
$this->import($callback[0]);
$ret=$this->$callback[0]->$callback[1]($id, $k, $value, $blnImageLink, $objCatalog, $this, $fieldConf);
$arrItems = $ret['items'];
$arrValues = $ret['values'];
$strHtml = $ret['html'];
}
}
}
}
// special formatting
$formatStr = $fieldConf['eval']['catalog']['formatStr'];
if (strlen($formatStr))
{
//$formatStr = htmlspecialchars_decode($fieldConf['eval']['catalog']['formatStr']);
$value = $arrValues[0];
switch ($fieldConf['eval']['catalog']['formatFunction'])
{
case 'string':
$value = sprintf($formatStr, $value);
break;
case 'number':
$decimalPlaces = is_numeric($formatStr) ? intval($formatStr) : 0;
$value = number_format($value, $decimalPlaces,
$GLOBALS['TL_LANG']['MSC']['decimalSeparator'],
$GLOBALS['TL_LANG']['MSC']['thousandsSeparator']);
break;
case 'date':
if (strlen($raw) && $raw !== 0)
{
$date = new Date($raw);
$value = $this->parseDate((strlen($formatStr) ? $formatStr : $GLOBALS['TL_CONFIG']['dateFormat']), $date->tstamp);
}
else
{
$value = '';
}
break;
default:;
}
$arrValues[0] = $value;
$strHtml = $value;
}
// add prefix and suffix format strings
if (is_array($fieldConf['eval']['catalog']['formatPrePost']) && count($fieldConf['eval']['catalog']['formatPrePost'])>0)
{
$strHtml = $fieldConf['eval']['catalog']['formatPrePost'][0].$strHtml.$fieldConf['eval']['catalog']['formatPrePost'][1];
// no $this->restoreBasicEntities() as this is done in the OutputTemplate
}
$return['items'] = $arrItems;
$return['values'] = $arrValues;
$return['html'] = $strHtml;
return $return;
}
/**
* parse files into HTML and other information and return as an array
* @param integer
* @param string
* @param variable
* @return array
*/
public function parseFiles($id, $k, $files)
{
$fieldConf = &$GLOBALS['TL_DCA'][$this->strTable]['fields'][$k];
$blnThumnailOverride = $this->catalog_thumbnails_override && ($this instanceof ModuleCatalogList || $this instanceof ModuleCatalogFeatured || $this instanceof ModuleCatalogRelated || $this instanceof ModuleCatalogReference);
// setup standard linking
$showLink = $fieldConf['eval']['catalog']['showLink'];
// image override
if ($blnThumnailOverride)
{
$showLink = $this->catalog_imagemain_field == $k ? $this->catalog_imagemain_fullsize :
($this->catalog_imagegallery_field == $k ? $this->catalog_imagegallery_fullsize : ''); // override default
}
$sortBy = $blnThumnailOverride ? $this->sortBy : $fieldConf['eval']['catalog']['sortBy'];
$files = deserialize($files,true);
$countFiles = count($files);
if (!is_array($files) || $countFiles < 1)
{
return array('files'=>array(),'src'=>array(),'html'=>array());
}
// required for parseMetaFile function (in FrontEnd)
$this->multiSRC = $files;
$this->arrAux = array();
// TODO: we also have to clean the array of already processed files here as otherwise $arrAux will not get populated again.
// We might find some better approach to this in the future rather than cleaning the cache arrays.
$this->arrProcessed=array();
$arrFiles = array();
$arrSource = array();
$arrValues = array();
$auxDate = array();
$counter = 0;
$allowedDownload = trimsplit(',', strtolower($GLOBALS['TL_CONFIG']['allowedDownload']));
if (strlen($fieldConf['eval']['extensions']))
{
$extensions = trimsplit(',', strtolower($fieldConf['eval']['extensions']));
$allowedDownload = array_intersect($allowedDownload, $extensions);
}
foreach ($files as $file)
{
if (!file_exists(TL_ROOT . '/' . $file))
{
continue;
}
if (is_file(TL_ROOT . '/' . $file))
{
$objFile = new File($file);
$showImage = $objFile->isGdImage && $fieldConf['eval']['catalog']['showImage'];
if (!$showImage && in_array($objFile->extension, $allowedDownload) || $showImage)
{
$class = (($counter == 0) ? ' first' : '') . (($counter == ($countFiles -1 )) ? ' last' : '') . ((($counter % 2) == 0) ? ' even' : ' odd');
$this->parseMetaFile(dirname($file), true);
$strBasename = strlen($this->arrMeta[$objFile->basename][0]) ? $this->arrMeta[$objFile->basename][0] : specialchars($objFile->basename);
$alt = (strlen($this->arrMeta[$objFile->basename][0]) ? $this->arrMeta[$objFile->basename][0] : ucfirst(str_replace('_', ' ', preg_replace('/^[0-9]+_/', '', $objFile->filename))));
$auxDate[] = $objFile->mtime;
// images
if ($showImage)
{
$w = $fieldConf['eval']['catalog']['imageSize'][0] ? $fieldConf['eval']['catalog']['imageSize'][0] : '';
$h = $fieldConf['eval']['catalog']['imageSize'][1] ? $fieldConf['eval']['catalog']['imageSize'][1] : '';
if ($blnThumnailOverride)
{
$newsize = deserialize($this->catalog_imagemain_field == $k ? $this->catalog_imagemain_size
: ($this->catalog_imagegallery_field == $k ? $this->catalog_imagegallery_size : array()) );
$w = ($newsize[0] ? $newsize[0] : '');
$h = ($newsize[1] ? $newsize[1] : '');
}
$src = $this->getImage($this->urlEncode($file), $w, $h, $fieldConf['eval']['catalog']['imageSize'][2]);
$size = getimagesize(TL_ROOT . '/' . $src);
$arrSource[$file] = array
(
'src' => $src,
'alt' => $alt,
'lb' => 'lb'.$id,
'w' => $size[0],
'h' => $size[1],
'wh' => $size[3],
'caption' => (strlen($this->arrMeta[$objFile->basename][2]) ? $this->arrMeta[$objFile->basename][2] : ''),
'metafile' => $this->arrMeta[$objFile->basename],
);
$tmpFile = '<img src="'.$src.'" alt="'.$alt.'" '.$size[3].' />';
if ($showLink)
{
// $tmpFile = '<a rel="lightbox[lb'.$id.']" href="'.$file.'" title="'.$alt.'">'.$tmpFile.'</a>';
// we have to supply the catalog id here as we might have more than one catalog with a field with the same name
// which will cause the lightbox to display the images for items with the same id in both.
$tmpFile = '<a data-lightbox="'. $this->strTable . $id . '" title="'.$alt.'" href="'.$file . '/' . $subfile.'">'.$tmpFile.'</a>';
}
}
// files
elseif ($showLink)
{
$text = $strBasename;
$url = $this->Environment->request . (($GLOBALS['TL_CONFIG']['disableAlias'] || strpos($this->Environment->request, '?') !== false) ? '&' : '?') . 'file=' . $this->urlEncode($file);
$icon = 'system/themes/' . $this->getTheme() . '/images/' . $objFile->icon;
$sizetext = '('.$this->getReadableSize($objFile->filesize, 1).')';
$arrSource[$file] = array
(
'title' => $strBasename,
'url' => $url,
'alt' => $alt,
'caption' => (strlen($this->arrMeta[$objFile->basename][2]) ? $this->arrMeta[$objFile->basename][2] : ''),
'size' => $objFile->filesize,
'sizetext' => $sizetext,
'icon' => $icon,
'metafile' => $this->arrMeta[$objFile->basename],
);
$iconfile = '<img src="'.$icon.'" alt="'.$alt.'" />';
$tmpFile = $iconfile.' <a href="'.$url.'" title="'.$alt.'">'.$text.' '.$sizetext.'</a>';
}
$arrFiles[$file] = $file;
$arrValues[$file] = '<span class="'.($showImage ? 'image' : 'file').$class.'">'.$tmpFile.'</span>';
$counter ++;
}
continue;
}
else if (is_dir(TL_ROOT . '/' . $file))
{
// Folders
$subfiles = scan(TL_ROOT . '/' . $file);
$this->parseMetaFile($file);
foreach ($subfiles as $subfile)
{
if (is_file(TL_ROOT . '/' . $file . '/' . $subfile))
{
$objFile = new File($file . '/' . $subfile);
$showImage = $objFile->isGdImage && $fieldConf['eval']['catalog']['showImage'];
if (!$showImage && in_array($objFile->extension, $allowedDownload) || $showImage)
{
$class = (($counter == 0) ? ' first' : '') . ((($counter % 2) == 0) ? ' even' : ' odd');
$strBasename = strlen($this->arrMeta[$objFile->basename][0]) ? $this->arrMeta[$objFile->basename][0] : specialchars($objFile->basename);
$alt = (strlen($this->arrMeta[$objFile->basename][0]) ? $this->arrMeta[$objFile->basename][0] : ucfirst(str_replace('_', ' ', preg_replace('/^[0-9]+_/', '', $objFile->filename))));
$auxDate[] = $objFile->mtime;
if ($showImage)
{
$w = $fieldConf['eval']['catalog']['imageSize'][0] ? $fieldConf['eval']['catalog']['imageSize'][0] : '';
$h = $fieldConf['eval']['catalog']['imageSize'][1] ? $fieldConf['eval']['catalog']['imageSize'][1] : '';
if ($blnThumnailOverride)
{
$newsize = deserialize($this->catalog_imagemain_field == $k ? $this->catalog_imagemain_size
: ($this->catalog_imagegallery_field == $k ? $this->catalog_imagegallery_size : array()) );
$w = ($newsize[0] ? $newsize[0] : '');
$h = ($newsize[1] ? $newsize[1] : '');
}
$src = $this->getImage($this->urlEncode($file . '/' . $subfile), $w, $h, $fieldConf['eval']['catalog']['imageSize'][2]);
$size = getimagesize(TL_ROOT . '/' . $src);
$arrSource[$file . '/' . $subfile] = array
(
'src' => $src,
'alt' => $alt,
'lb' => 'lb'.$id,
'w' => $size[0],
'h' => $size[1],
'wh' => $size[3],
'caption' => (strlen($this->arrMeta[$objFile->basename][2]) ? $this->arrMeta[$objFile->basename][2] : ''),
'metafile' => $this->arrMeta[$objFile->basename],
);
$tmpFile = '<img src="'.$src.'" alt="'.$alt.'" '.$size[3].' />';
if ($showLink)
{
// $tmpFile = '<a rel="lightbox[lb'.$id.']" title="'.$alt.'" href="'.$file . '/' . $subfile.'">'.$tmpFile.'</a>';
// we have to supply the catalog id here as we might have more than one catalog with a field with the same name here.
$tmpFile = '<a data-lightbox="'. $this->strTable . $id . '" title="'.$alt.'" href="'.$file . '/' . $subfile.'">'.$tmpFile.'</a>';
}
}
// files
elseif ($showLink)
{
$text = $strBasename;
$url = $this->Environment->request . (($GLOBALS['TL_CONFIG']['disableAlias'] || !$GLOBALS['TL_CONFIG']['rewriteURL']
&& count($_GET) || strlen($_GET['page'])) ? '&' : '?'). 'file=' . $this->urlEncode($file . '/' . $subfile);
$icon = 'system/themes/' . $this->getTheme() . '/images/' . $objFile->icon;
$sizetext = '('.number_format(($objFile->filesize/1024), 1, $GLOBALS['TL_LANG']['MSC']['decimalSeparator'], $GLOBALS['TL_LANG']['MSC']['thousandsSeparator']).' kB)';
$arrSource[$file . '/' . $subfile] = array
(
'title' => $strBasename,
'url' => $url,
'alt' => $alt,
'caption' => (strlen($this->arrMeta[$objFile->basename][2]) ? $this->arrMeta[$objFile->basename][2] : ''),
'size' => $objFile->filesize,
'sizetext' => $sizetext,
'icon' => $icon,
'metafile' => $this->arrMeta[$objFile->basename],
);
$iconfile = '<img src="'.$icon.'" alt="'.$alt.'" />';
$tmpFile = $iconfile.' <a href="'.$url.'" title="'.$alt.'">'.$text.' '.$sizetext.'</a>';
}
$arrFiles[$file . '/' . $subfile] = $file . '/' . $subfile;
$arrValues[$file . '/' . $subfile] = '<span class="'.($showImage ? 'image' : 'file').$class.'">'.$tmpFile.'</span>';
$counter ++;
}
}
}
}
}
// Sort array
$files = array();
$source = array();
$values = array();
switch ($sortBy)
{
default:
case 'name_asc':
uksort($arrFiles, 'basename_natcasecmp');
break;
case 'name_desc':
uksort($arrFiles, 'basename_natcasercmp');
break;
case 'date_asc':
array_multisort($arrFiles, SORT_NUMERIC, $auxDate, SORT_ASC);
break;
case 'date_desc':
array_multisort($arrFiles, SORT_NUMERIC, $auxDate, SORT_DESC);
break;
case 'meta':
foreach ($this->arrAux as $aux)
{
$k = array_search($aux, $arrFiles);
if ($k !== false)
{
$files[] = $arrFiles[$k];
$source[] = $arrSource[$k];
$values[] = $arrValues[$k];
}
}
break;
case 'random':
$keys = array_keys($arrFiles);
shuffle($keys);
foreach($keys as $key)
{
$files[$key] = $arrFiles[$key];
}
$arrFiles = $files;
break;
}
if ($sortBy != 'meta')
{
// re-sort the values
foreach($arrFiles as $k=>$v)
{
$files[] = $arrFiles[$k];
$source[] = $arrSource[$k];
$values[] = $arrValues[$k];
}
}
$return['files'] = $files;
$return['src'] = $source;
$return['html'] = $values;
return $return;
}
/**
* Replace Catalog InsertTags including TL InsertTags
* @param string
* @param array
* @return string
*/
protected function replaceCatalogTags($strValue, $arrCatalog)
{
$strValue = trim($strValue);
// search for catalog tags
$tags = array();
preg_match_all('/{{[^}]+}}/i', $strValue, $tags);
// Replace tags of type {{catalog::fieldname}}
foreach ($tags[0] as $tag)
{
$elements = explode('::', trim(str_replace(array('{{', '}}'), array('', ''), $tag)));
// {{catalog::fieldname}}
if (strtolower($elements[0]) == 'catalog')
{
$key = $elements[1];
if (array_key_exists($key, $arrCatalog))
{
$strValue = str_replace($tag, str_replace("\n", "<br>", $arrCatalog[$key]), $strValue);
}
}
}
// Replace standard insert tags
if (strlen($strValue))
{
$strValue = $this->replaceInsertTags($strValue);
}
return $strValue;
}
/**
* Generate Front-end Url with only catalog ID as parameter
* @param integer
* @param integer
* @return string
*/
public function generateCatalogNavigationUrl($field=false, $value=false)
{
$jumpTo= $this->catalog_jumpTo?$this->catalog_jumpTo:$this->jumpTo;
$pageRow=$this->getJumpTo($jumpTo, true);
if (!$pageRow)
{
return ampersand($this->Environment->request, ENCODE_AMPERSANDS);
}
if($field)
{
$strParams = $GLOBALS['TL_CONFIG']['disableAlias'] ? '&' . $field . '=' . $value : '/' . $field . '/' . $value;
} else {
$strParams = '';
}
// Return link to catalog reader page with item alias
return ampersand($this->generateFrontendUrl($pageRow, $strParams));
}
protected function renderCatalogNavigationItems($id, $level=1, $blnTags=false, $value='')
{
$aliasField = $this->getAliasField($this->strTable);
$strWhere = $blnTags ? $this->strTable.'.id=tl_catalog_tag_rel.itemid AND fieldid='.$GLOBALS['TL_DCA'][$this->strTable]['fields'][$this->catalog_navigation]['eval']['catalog']['fieldId'].' AND valueid='.$id.'' : $this->catalog_navigation."=?";
// TODO: add the parsed filter URL here.
if(!BE_USER_LOGGED_IN && $this->publishField)
{
$strWhere .=' AND '.$this->publishField.'=1';
}
// query database
$objNodes = $this->Database->prepare('SELECT DISTINCT '.$this->strTable.'.*, tl_catalog_types.jumpTo AS parentJumpTo FROM '.$this->strTable.($blnTags?', tl_catalog_tag_rel':'').', tl_catalog_types WHERE tl_catalog_types.id='.$this->strTable.'.pid AND '.$strWhere)
->execute($id);
if ($objNodes->numRows < 1)
{
return '';
}
$items = array();
// Determine the layout template
if (!strlen($this->navigationTpl))
{
$this->navigationTpl = 'nav_default';
}
// Overwrite template
$objTemplate = new FrontendTemplate($this->navigationTpl);
$objTemplate->type = get_class($this);
$objTemplate->level = 'level_' . $level++;
// Get page object
global $objPage;
$showField = $this->catalog_show_field;
// Browse items
while($objNodes->next())
{
$href = $this->generateCatalogUrl($objNodes, $aliasField, $this->strTable, sprintf('/%s/%s', $this->catalog_navigation, $value?$value:$id));
// Active field
if ($this->Input->get('items') == $objNodes->id || $this->Input->get('items') == $objNodes->$aliasField)
{
$strClass = trim('item' . (strlen($objJump->cssClass) ? ' ' . $objJump->cssClass : ''));
$items[] = array
(
'isActive' => true,
'subitems' => $subitems,
'class' => (strlen($strClass) ? $strClass : ''),
// 'pageTitle' => specialchars($objJump->pageTitle),
'title' => specialchars($objNodes->$showField),
'link' => $objNodes->$showField,
'href' => $href,
// 'alias' => $objJump->alias,
// 'target' => (($objJump->type == 'redirect' && $objJump->target) ? ' window.open(this.href); return false;' : ''),
// 'description' => str_replace(array("\n", "\r"), array(' ' , ''), $objJump->description),
// 'accesskey' => $objJump->accesskey,
// 'tabindex' => $objJump->tabindex
);
continue;
}
$strClass = trim('item' . (strlen($objJump->cssClass) ? ' ' . $objJump->cssClass : ''));
$items[] = array
(
'isActive' => false,
'subitems' => $subitems,
'class' => (strlen($strClass) ? $strClass : ''),
// 'pageTitle' => specialchars($objJump->pageTitle),
'title' => specialchars($objNodes->$showField),
'link' => $objNodes->$showField,
'href' => $href,
// 'alias' => $objJump->alias,
// 'target' => (($objJump->type == 'redirect' && $objJump->target) ? ' window.open(this.href); return false;' : ''),
// 'description' => str_replace(array("\n", "\r"), array(' ' , ''), $objJump->description),
// 'accesskey' => $objJump->accesskey,
// 'tabindex' => $objJump->tabindex
);
}
// Add classes first and last
if (count($items))
{
$last = count($items) - 1;
$items[0]['class'] = trim($items[0]['class'] . ' first');
$items[$last]['class'] = trim($items[$last]['class'] . ' last');
}
$objTemplate->items = $items;
return count($items) ? $objTemplate->parse() : '';
}
protected $arrTrail=array();
protected $objNavField=NULL;
/**
* Recursively compile the catalog navigation menu and return it as HTML string
* @param integer
* @param integer
* @return string
*/
protected function internalRenderCatalogNavigation($pid, $level, $strActive=NULL)
{
// Get internal page (from parent catalog)
$arrJump = $this->getJumpTo($this->jumpTo, false);
$ids = ($pid == 0) ? ($this->objNavField->limitItems && $this->objNavField->items ? $this->objNavField->items : array(0)) : array($pid);
$strRoot = ((!$this->objNavField->blnChildren && $level == 1) ? 'id' : 'pid');
if ($this->objNavField->treeView)
{
if($this->objNavField->type == 'tags')
{
$strItemCount = '(SELECT COUNT(t.id) AS count FROM tl_catalog_tag_rel AS t RIGHT JOIN ' . $this->strTable . ' AS c ON (t.itemid=c.id) WHERE t.valueid=o.id AND t.fieldid=' . $this->objNavField->id . (!BE_USER_LOGGED_IN && $this->publishField ? ' AND c.' . $this->publishField.'=1' : '') . ') AS itemCount';
} else {
$strItemCount = '(SELECT COUNT(t.id) AS count FROM ' . $this->strTable . ' AS t WHERE ' .
(!BE_USER_LOGGED_IN && $this->publishField ? 't.'.$this->publishField.'=1 AND ' : '') .
$this->catalog_navigation . ' IN (SELECT id FROM ' . $this->objNavField->sourceTable . ' AS t WHERE pid=o.id) OR '.$this->catalog_navigation.'=o.id) AS itemCount';
}
$objNodes = $this->Database->execute('SELECT '.$strRoot.', id, ' . $this->objNavField->valueField .
', (SELECT COUNT(*) FROM '. $this->objNavField->sourceTable .' i WHERE i.pid=o.id) AS childCount, ' .
$this->objNavField->sourceColumn . ' AS name, ' .
$strItemCount .
' FROM '. $this->objNavField->sourceTable. ' o WHERE '.$strRoot.' IN ('.implode(',',$ids).') ORDER BY '. $this->objNavField->sort);
}
if (!$this->objNavField->treeView || ($objNodes->numRows == 0 && $level == 1))
{
$objNodes = $this->Database->execute('SELECT id, '.$this->objNavField->valueField.', 0 AS childCount, '. $this->objNavField->sourceColumn .' AS name, 0 AS itemCount FROM '. $this->objNavField->sourceTable .' ORDER BY '.$this->objNavField->sort);
}
// no entries for the given pid
if (!$objNodes->numRows)
return '';
$valueField=$this->objNavField->valueField;
$items = array();
// Overwrite template
$objTemplate = new FrontendTemplate($this->navigationTpl);
$objTemplate->type = get_class($this);
$objTemplate->level = 'level_' . $level++;
// Browse field nodes
while($objNodes->next())
{
$subitems = '';
$strClass = '';
// setup field and value
$field = $this->catalog_navigation;
$value = $objNodes->$valueField;
$isTrail = in_array($objNodes->id, $this->arrTrail);
$href = $this->generateCatalogNavigationUrl($field, $value, sprintf('/%s/%s', $field, $value));
if (!$this->showLevel || $this->showLevel >= $level || (!$this->hardLimit && $isTrail))
{
// if current field value is selected, display children
if ($this->catalog_show_items && $strActive == $objNodes->$valueField)
{
$subitems .= $this->renderCatalogNavigationItems($objNodes->id, $level, ($this->objNavField->type == 'tags'), $value);
}
if (count($objNodes->childCount) && $this->objNavField->blnChildren)
{
$subitems .= $this->internalRenderCatalogNavigation($objNodes->id, $level, $strActive);
}
}
$strClass .= trim((strlen($subitems) ? 'submenu' : '')
. (strlen($arrJump['cssClass']) ? ' ' . $arrJump['cssClass'] : '')
. ($isTrail ? ' trail' : '')
. ' ' . (count($items)%2 ? 'odd' : 'even')
);
// Active field
if ($strActive == $value)
{
$items[] = array
(
'isActive' => !strlen($this->Input->get('items')),
'subitems' => $subitems,
'subitemcount' => $objNodes->itemCount,
'class' => (strlen($strClass) ? $strClass : ''),
'pageTitle' => specialchars($arrJump['pageTitle']),
'title' => specialchars($objNodes->name),
'link' => $objNodes->name,
'href' => $href,
'alias' => $arrJump['alias'],
'target' => (($arrJump['type'] == 'redirect' && $arrJump['target']) ? ' window.open(this.href); return false;' : ''),
'description' => str_replace(array("\n", "\r"), array(' ' , ''), $arrJump['description']),
'accesskey' => $arrJump['accesskey'],
'tabindex' => $arrJump['tabindex'],
'itemAlias' => $value
);
// move on to next childnode
continue;
}
if(!$this->objNavField->treeView || ($objNodes->itemCount || $objNodes->childCount))
{
$items[] = array
(
'isActive' => false,
'subitems' => $isTrail?$subitems:'',
'subitemcount' => $objNodes->itemCount,
'class' => (strlen($strClass) ? $strClass : ''),
'pageTitle' => specialchars($arrJump['pageTitle']),
'title' => specialchars($objNodes->name),
'link' => specialchars($objNodes->name),
'href' => $href,
'alias' => $arrJump['alias'],
'target' => (($arrJump['type'] == 'redirect' && $arrJump['target']) ? ' window.open(this.href); return false;' : ''),
'description' => str_replace(array("\n", "\r"), array(' ' , ''), $arrJump['description']),
'accesskey' => $arrJump['accesskey'],
'tabindex' => $arrJump['tabindex'],
'itemAlias' => $value
);
}
}
// Add classes first and last
if (count($items))
{
$last = count($items) - 1;
$items[0]['class'] = trim($items[0]['class'] . ' first');
$items[$last]['class'] = trim($items[$last]['class'] . ' last');
}
$objTemplate->items = $items;
return count($items) ? $objTemplate->parse() : '';
}
protected function determineNavRootFromReferer($strNavField, $skipRawReferer=false)
{
$HTTPReferer = (!$skipRawReferer) ? $this->Environment->httpReferer : $this->getReferer();
// We check the real HTTP referer first, as we might have multiple tabs open in
// this environment and therefore want to use the "real" referer.
if((!$skipRawReferer) && preg_match('#[\/?&]'.$strNavField.'#', $HTTPReferer))
{
$strRequest = str_replace($this->Environment->base, '', $HTTPReferer);
if(!$GLOBALS['TL_CONFIG']['disableAlias'])
{
$strRequest = preg_replace('/\?.*$/i', '', $strRequest);
$strRequest = preg_replace('/' . preg_quote($GLOBALS['TL_CONFIG']['urlSuffix'], '/') . '$/i', '', $strRequest);
$arrFragments = explode('/', $strRequest);
// Skip index.php
if (strtolower($arrFragments[0]) == 'index.php')
{
array_shift($arrFragments);
}
} else {
// TODO: handle disabled aliases here.
$arrFragments = array();
}
// skip page and search for our param.
for($i=1;$i<count($arrFragments);$i+=2)
{
if($arrFragments[$i]==$strNavField)
{
return $arrFragments[$i+1];
}
}
}
if(!$skipRawReferer)
{
return $this->determineNavRootFromReferer($strNavField, true);
}
}
/**
* Look up all needed stuff and then call the recursive function internalRenderCatalogNavigation
* @param integer
* @return string
*/
protected function renderCatalogNavigation($pid)
{
$this->arrTrail=array();
// get reference table and column
$objFields = $this->Database->prepare('SELECT * FROM tl_catalog_fields WHERE pid=? AND colName=?')
->limit(1)
->execute($this->catalog, $this->catalog_navigation);
if (!$objFields->numRows)
return '';
$this->objNavField = new stdClass();
$this->objNavField->id = $objFields->id;
$this->objNavField->sourceTable = $objFields->itemTable;
$this->objNavField->sourceColumn = $objFields->itemTableValueCol;
$this->objNavField->blnChildren = $objFields->childrenSelMode;
$this->objNavField->limitItems = $objFields->limitItems;
$this->objNavField->items = deserialize($objFields->items);
$this->objNavField->valueField = $this->getAliasField($this->objNavField->sourceTable);
$this->objNavField->type = $objFields->type;
// check if this tree has a pid or a flat table
$this->objNavField->treeView = $this->Database->fieldExists('pid', $this->objNavField->sourceTable);
$this->objNavField->sort = $this->Database->fieldExists('sorting', $this->objNavField->sourceTable) ? 'sorting' : $this->objNavField->sourceColumn;
// Determine the layout template
if (!strlen($this->navigationTpl))
{
$this->navigationTpl = 'nav_default';
}
// root is given via URL
if($this->Input->get($this->catalog_navigation))
{
$root=$this->Input->get($this->catalog_navigation);
}
// root not given via URL but we have an select field and an item is being requested.
else if($this->Input->get('items') && ($this->objNavField->type == 'select'))
{
// SELECT fields
$value=$this->Input->get('items');
$strAlias = $this->strAliasField ? $this->strAliasField : (is_numeric($value) ? 'id' : '');
if(strlen($strAlias))
{
$objItem = $this->Database->prepare('SELECT '.$this->objNavField->valueField.' AS alias FROM '. $this->objNavField->sourceTable. ' WHERE id=(SELECT '.$this->catalog_navigation.' FROM '.$this->strTable.' WHERE '.(!BE_USER_LOGGED_IN && $this->publishField ? $this->publishField.'=1 AND ' : ''). $strAlias . '=?)')
->limit(1)
->execute($value);
if ($objItem->numRows)
{
$root=$objItem->alias;
}
}
}
// no root via URL but an item requested, let's try to get the root from the referer.
else if($this->Input->get('items') && !$this->Input->get($this->catalog_navigation))
{
// check if in referer is something mentioned.
if(preg_match('#[\/?&]'.$this->catalog_navigation.'#', $this->Environment->httpReferer))
{
$root = $this->determineNavRootFromReferer($this->catalog_navigation);
}
}
if ($this->objNavField->treeView && $root)
{
// determine all parents
$objRoot = $this->Database->prepare('SELECT id,pid FROM '. $this->objNavField->sourceTable. ' WHERE '.$this->objNavField->valueField.'=?')
->limit(1)
->execute($root);
$parents=array($objRoot->id);
// root found, we can now find the parents.
while($objRoot->numRows && $objRoot->pid)
{
$parents[]=$objRoot->pid;
$objRoot = $this->Database->prepare('SELECT pid FROM '. $this->objNavField->sourceTable. ' WHERE id='.$objRoot->pid)
->limit(1)
->execute();
}
foreach(array_reverse($parents) as $k=>$v)
$this->arrTrail[$k+1]=$v;
}
if($this->levelOffset)
{
// start level specified but not in trail -> do not output
if(!$this->arrTrail[$this->levelOffset])
return '';
$pid = $this->arrTrail[$this->levelOffset];
}
return $this->internalRenderCatalogNavigation($pid, 1, $root);
}
/**
* List and generate comment form
* @param object
*/
public function processComments(Database_Result $objCatalog)
{
// Comments
$objArchive = $this->Database->prepare('SELECT * FROM tl_catalog_types WHERE id=?')
->limit(1)
->execute($objCatalog->pid);
if ($objArchive->numRows < 1 || !$objArchive->allowComments || !in_array('comments', $this->Config->getActiveModules()))
{
$this->Template->allowComments = false;
return;
}
$this->Template->allowComments = true;
$this->import('Comments');
$objConfig = new stdClass();
$objConfig->perPage = $objArchive->perPage;
$objConfig->order = $objArchive->sortOrder;
$objConfig->template = $objArchive->template;
$objConfig->requireLogin = $objArchive->requireLogin;
$objConfig->disableCaptcha = $objArchive->disableCaptcha;
$objConfig->bbcode = $objArchive->bbcode;
$objConfig->moderate = $objArchive->moderate;
// TODO: add notifies here.
$arrNotifies=array($GLOBALS['TL_ADMIN_EMAIL']);
// issue #1690 hide member details not working anymore.
if($objArchive->hideMember && ($this->Input->post('FORM_SUBMIT')=='com_'. $this->strTable .'_'. $objCatalog->id))
{
// we have to trick the comments module into beliving that the user inserted his name and email as those are hardcoded mandatory.
$this->import('FrontendUser', 'Member');
$this->Input->setPost('name', trim($this->Member->firstname . ' ' . $this->Member->lastname));
$this->Input->setPost('email', trim($this->Member->email));
}
$this->Comments->addCommentsToTemplate($this->Template, $objConfig, $this->strTable, $objCatalog->id, $arrNotifies);
if($objArchive->hideMember)
{
$fields = $this->Template->fields;
unset($fields['name']);
unset($fields['email']);
$this->Template->fields=$fields;
}
if($objArchive->disableWebsite)
{
$fields = $this->Template->fields;
unset($fields['website']);
$this->Template->fields=$fields;
}
}
}
?>
Noch ein Hinweis, wenn in den Ordnernamen wo die Bilder liegen Leerzeichen sind, verträgt es sich auch nicht mit dem Catalog.
Lesezeichen