<commit_before><?php
/**
 * Mascker
 *
 * LICENSE
 *
 * This source file is subject to the GNU General Public License 2.0
 * It is  available through the world-wide-web at this URL:
 * http://www.opensource.org/licenses/gpl-2.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to <EMAIL> so we can send you a copy immediately.
 *
 * @package    Mascker_Grid
 * @copyright  Copyright (c) Mascker (http://www.petala-azul.com)
 * @license    http://www.opensource.org/licenses/gpl-2.0.php   GNU General Public License 2.0
 * @version    0.1  mascker $
 * @author     Mascker (<NAME>) <<EMAIL> > 
 */

class Bvb_Grid_DataGrid {
	
	public static $_cache;
	
	/**
	 * [Onde temos as classes]
	 *
	 * @var array
	 */
	protected $template = array ();
	
	/**
	 * [O tipo de templates que pode ser utilizado]
	 *
	 * @var unknown_type
	 */
	protected $_templates;
	
	/**
	 * [PT] A lista de dir e prifx a ser utilizadores para formatar campos
	 *
	 * @var unknown_type
	 */
	protected $_formatter = array ();
	
	/**
	 * [PT] A instancia do cache
	 *
	 * @var unknown_type
	 */
	public $cacheInstance;
	
	/**
	 * Os tipode de exportação que podemos utilizar
	 *
	 * @var unknown_type
	 */
	public $export = array ('pdf', 'word', 'excel', 'print', 'wordx' );
	# public $export = array('pdf','word','excel','print','xml','csv');
	

	/**
	 * [PT] Toda a informação que não está ligada com a base de dados
	 * 
	 * [EN] All info that is not directly related to the database
	 *
	 * @var array
	 */
	public $info = array ();
	
	/**
	 * [PT] Vamos guardar as tabelas a que já foram aplicadas a query describe table
	 * [EN] Save the result of the describeTables
	 *
	 * @var array
	 */
	protected $_describeTables = array ();
	
	/**
	 * [PT] Armazenar as primrys keys para não termos que fazer o loop sempre 
	 * [PT] Que precisarmos
	 * [EN] Registry for PK
	 * 
	 *
	 * @var array
	 */
	
	protected $_getPrimaryKey = array ();
	
	/**
	 * [PT] A parte where da query // 
	 * [EN] Where part from query
	 *
	 * @var string
	 */
	protected $_queryWhere = '';
	
	/**
	 * [PT] O adpater da BD
	 * [EN] DB Adapter
	 *
	 * @var object
	 */
	protected $_db;
	
	/**
	 * [PT] A url base para constriuir a url final
	 * [EN] Baseurl
	 *
	 * @var string
	 */
	protected $_baseUrl;
	
	/**
	 * [PT] O array com o resultado da consulta à base de dados
	 * [EN] Array containing the query result from table(s)
	 *
	 * @var array
	 */
	protected $_result;
	
	/**
	 * [PT]Total de registos encontrados
	 * [EN]Total records from db query
	 *
	 * @var int
	 */
	protected $_totalRecords;
	
	/**
	 * [PT] Array com os titulos a ser aplicados
	 * [EN] Array containing filed titles
	 *
	 * @var array
	 */
	protected $_titles;
	
	/**
	 * [PT] Array contento os campos da(s) tablea(s)
	 * [EN] Array containing table(s) fields
	 *
	 * @var array
	 */
	protected $_fields = array ();
	
	/**
	 * [PT] Where definido pelo utilizador a acrescentar ao criado pela Grid
	 * [EN] Where initially defined by user
	 *
	 * @var string
	 */
	protected $_where;
	
	/**
	 * [PT] A lista de filtros
	 *
	 * @var unknown_type
	 */
	public $filters;
	
	/**
	 * [PT] Valores dos filtros que o utiliador inseriu
	 * [EN] Filters values inserted by the user
	 *
	 * @var array
	 */
	protected $_filtersValues;
	
	/**
	 * [PT] Toda a informação relacionada com a base de dados
	 * [EN] All information databse related
	 *
	 * @var array
	 */
	public $data = array ();
	
	/**
	 * [PT] Lista de filtros a ser aplicado
	 * [EN] Filters List to be applied
	 *
	 * @var array
	 */
	
	public $params = array ();
	
	/**
	 * [PT] Url parametros
	 * [EN] URL params
	 *
	 * @var string
	 */
	public $ctrlParams;
	
	/**
	 * [PT] Campos extra
	 * [EN] Extra fields array
	 *
	 * @var array
	 */
	public $extra_fields;
	
	/**
	 * [PT]A lista final de campos. Pode parecer estranho, mas é assim
	 *
	 * @var unknown_type
	 */
	protected $_finalFields;
	
	/**
	 * [PT] O colspan a aplicar na tabela
	 *
	 * @var unknown_type
	 */
	
	public $_colspan;
	
	/**
	 * [PT] O númeor de campos ocultos que existem
	 * [PT] Para depois podermos calcular o colspan
	 *
	 * @var int
	 */
	public $totalHiddenFields;
	
	/**
	 * [PT] COnfirmar que foi tudo analisado aantes de processar a query
	 *
	 * @var unknown_type
	 */
	private $consolidated = 0;
	
	/**
	 * [PT] Se vamos utilizar o cache ou não
	 * [PT] De futuro quando utilizar-mos o cache ele é limpo automaticamente nas funções CRUD
	 *
	 * @var bool
	 */
	public $cache = false;
	
	/**
	 * [PT] A lista de dirs onde procurar por "valideiros" e filtros
	 *
	 * @var array
	 */
	protected $_elements = array (0 => array () );
	
	/**
	 * [PT] TIpos dpermitidos na aplicação aos elementos do formulários
	 *
	 * @var array
	 */
	private $_elementsAllowed = array ('filter', 'validator' );
	
	/**
	 * [PT] A instancia do tradutor
	 *
	 * @var unknown_type
	 */
	public $translator;
	
	/**
	 * [PT] O campo para depos podermos ordenar caso exista a separação horizontal
	 *
	 * @var string
	 */
	private $fieldHorizontalRow;
	
	public $locale = 'en_US';
	
	/**
	 * [PT] The template instance
	 *
	 * @var unknown_type
	 */
	protected $temp;
	
	/**
	 * [PT] AS classes dos diferentes tipos de templates que já foram instanciados
	 *
	 * @var unknown_type
	 */
	public $activeTemplates = array ();
	
	/**
	 * [PT] A função __construct recebe o adapter para se liga à base de dados
	 * [PT] É também tratada toda a informação relacionada com a url e os params
	 * [PT] É também instaciada o Auth do Zend_Auth. A autenticação deve ter sido
	 * [PT] Efectuada utilizando o método store, para agora poder validar o user
	 * 
	 * [EN] The __construct function receives the db adapter. All information related to the
	 * [EN] URL is also processed here
	 * [EN] To edit, add, or delete records, a user must be authenticated, so we instanciate 
	 * [EN] it here. Remember to uses the method write when autenticating a user, so we can know 
	 * [EN] if its logged or not
	 *
	 * @param array $data
	 */
	function __construct($db) {
		
		//[PT] Iniciar o adapter da base de dados
		$this->_db = $db;
		$this->_db->setFetchMode ( Zend_Db::FETCH_OBJ );
		
		$this->ctrlParams = Zend_Controller_Front::getInstance ()->getRequest ()->getParams ();
		$this->_baseUrl = Zend_Controller_Front::getInstance ()->getBaseUrl ();
		
		$this->setPagination ();
		
		//[PT] Valos adicionar os elementos da Zend
		$this->addElementDir ( 'Zend/Filter', 'Zend_Filter', 'filter' );
		$this->addElementDir ( 'Zend/Validate', 'Zend_Validate', 'validator' );
		
		$this->addFormatterDir ( 'Bvb/Grid/Formatter', 'Bvb_Grid_Formatter' );
		
		$this->addTemplateDir ( 'Bvb/Grid/Template/Table', 'Bvb_Grid_Template_Table', 'table' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Pdf', 'Bvb_Grid_Template_Pdf', 'pdf' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Print', 'Bvb_Grid_Template_Print', 'print' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Word', 'Bvb_Grid_Template_Word', 'word' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Wordx', 'Bvb_Grid_Template_Wordx', 'wordx' );
	
	}
	
	/**
	 * [PT] Traduzir as mensagens
	 *
	 * @param string $message
	 * @return string
	 */
	function __($message) {
		
		if (Zend_Registry::isRegistered ( 'Zend_Translate' )) {
			$this->translator = Zend_Registry::get ( 'Zend_Translate' );
			$this->translator->setLocale ( $this->locale );
			$message = $this->translator->translate ( $message );
		}
		
		return $message;
	
	}
	
	/**
	 * [PT] não faz mais nada senão passar o valor para __set
	 * [PT] só que assim podemos utilizar o para∏¿≈}{⁄{}{⁄ (não me lembro do nome)
	 * [PT] %grid->from('barcelos')
	 *             ->noFilters(1)->
	 *             ->noorder(1);
	 *
	 * @param unknown_type $name
	 * @param unknown_type $value
	 * @return unknown
	 */
	function __call($name, $value) {
		if (substr ( $name, 0, 3 ) == 'set') {
			$name = substr ( $name, 3 );
		}
		
		$this->__set ( $name, $value [0] );
		
		return $this;
	}
	
	/**
	 * [Para podemros utiliza]
	 *
	 * @param string $var
	 * @param string $value
	 */
	
	function __set($var, $value) {
		
		$data = array ('from', 'order', 'where', 'primaryKey', 'table', 'fields', 'hide' );
		
		if (in_array ( $var, $data )) {
			if ($var == 'from' && ! strpos ( " ", trim ( $value ) )) {
				$this->data ['from'] = trim ( $value );
				$this->data ['table'] = trim ( $value );
			} else {
				$this->data [$var] = $value;
			}
		
		} else {
			$this->info [$var] = $value;
		}
	
	}
	
	/**
	 * [PT] Buscar a copmposição da tabela
	 * [PT] Depois disso vamos meter a tabela num array. se precisar-mos amis tarde,
	 * [PT] Ela já está lá
	 * 
	 * [EN] Get table description and then save it to a array.
	 *
	 * @param array|string $table
	 * @return array
	 */
	function getDiscribeTable($table) {
		
		if (! @is_array ( $this->_describeTables [$table] )) {
			$describe = $this->_db->describeTable ( $table );
			$this->_describeTables [$table] = $describe;
		}
		
		return $this->_describeTables [$table];
	}
	
	/**
	 * [PT]Adicionar um campo à lista ja existente
	 *
	 * @param string $field
	 * @param array $options
	 * @return unknown
	 */
	function addColumn($field, $options = array()) {
		
		if (@is_array ( $this->data ['fields'] )) {
			
			if (! array_key_exists ( $field, $this->data ['fields'] )) {
				$this->data ['fields'] [$field] = $options;
				
				if (isset ( $options ['hRow'] )) {
					
					if ($options ['hRow'] == 1) {
						$this->fieldHorizontalRow = $field;
						$this->info ['hRow'] = array ('field' => $field, 'title' => $options ['title'] );
					}
				
				}
			}
		} else {
			$this->data ['fields'] [$field] = $options;
		
		}
		return $this;
	}
	
	/**
	 * [PT] Adicionar mais uma opção para encontro de formatadores de cnteúdo
	 * desde data, numeros, imagens, etc, etc
	 *
	 * @param string $dir
	 * @param string $prefix
	 * @return $this
	 */
	function addFormatterDir($dir, $prefix) {
		$this->_formatter [] = array ('dir' => trim ( $dir, "/" ) . '/', 'prefix' => trim ( $prefix, "_" ) );
		
		return $this;
	}
	
	function addElementDir($dir, $prefix, $type = 'filter') {
		
		if (! in_array ( strtolower ( $type ), $this->_elementsAllowed )) {
			throw new Exception ( 'Type not recognized' );
		}
		
		$this->_elements [$type] [] = array ('dir' => trim ( $dir, "/" ) . '/', 'prefix' => trim ( $prefix, "_" ) );
		
		return $this;
	}
	
	/**
	 * [PT] Formatar o valor de cada campo que vem da base de dados
	 *
	 * @param unknown_type $value
	 * @param unknown_type $formatter
	 * @param unknown_type $options
	 * @return unknown
	 */
	function applyFormat($value, $formatter) {
		
		//[PT] Para simplificar as coisas vemos se é um array.
		//[PT] Pode não ser necessários argumentos adicionais
		if (is_array ( $formatter )) {
			$result = $formatter [0];
			$options = $formatter [1];
		} else {
			$result = $formatter;
			$options = null;
		}
		
		//[Vamos meter isto por ordem reversa para os ultimos serem os primeiros
		//Faz sentido e acho que é uq eo que é mais utilizado
		$format = array_reverse ( $this->_formatter );
		
		foreach ( $format as $find ) {
			$file = $find ['dir'] . ucfirst ( $result ) . '.php';
			$class = $find ['prefix'] . '_' . ucfirst ( $result );
			
			require_once ($file);
			
			if (class_exists ( $class )) {
				$t = new $class ( $options );
				$return = $t->format ( $value );
				$apply = 1;
			}
		
		}
		
		if ($apply !== 1) {
			$return = $value;
		}
		return $return;
	
	}
	
	/**
	 * [PT] Todos os dados relacionados com a conneção á base de dados
	 * [PT] Os filtros, campos extra, etc, etc
	 * 
	 * [EN] All information related with database.
	 *[EN] Filters, extra fields, etc, etc
	 * @param string $data
	 * 
	 * */
	
	function setData($data) {
		
		$this->data = $data ['data'];
		
		$this->info = $data;
		
		if (! is_array ( $this->data ['table'] )) {
			$this->data ['table'] = $this->data ['from'];
		}
	
	}
	
	/**
	 * Criar a fgrid através de uma ficheiro XML
	 * 
	 * 
	 * TESTE
	 * TEST
	 * TESTE
	 * TEST
	 * TESTE
	 * TEST
	 * TESTE
	 * TEST
	 *
	 */
	function setDataFromXml($file) {
		
		$fc = Zend_Controller_Front::getInstance ();
		$modulo = strtolower ( $fc->getRequest ()->getModuleName () );
		
		if (strpos ( $file, '_' ) === false) {
			$file = $modulo . "_" . ucfirst ( $file );
		}
		//Temos que
		$file = rtrim ( str_replace ( "_", "/grids/", $file ), ".xml" ) . ".xml";
		
		$xml = $this->object2array ( simplexml_load_file ( $file ) );
		
		if (strlen ( $xml ['data'] ['where'] ) > 0) {
			$final = $xml ['data'] ['where'];
			$final1 = preg_match_all ( "/{eval}(.*?){\/eval}/", $final, $t );
			$t2 = $t;
			
			$i = 0;
			foreach ( $t2 [1] as $value ) {
				$h = eval ( "return " . $value . ";" );
				$final = str_replace ( $t [0] [$i], $h, $final );
				$i ++;
			}
			
			$xml ['data'] ['where'] = $final;
		
		}
		
		foreach ( $xml ['data'] ['fields'] as $key => $final ) {
			if (is_array ( $final ['@attributes'] )) {
				unset ( $xml ['data'] ['fields'] [$key] );
				$xml ['data'] ['fields'] [$key . " AS " . $final ['@attributes'] ['as']] = $final;
			}
		}
		
		self::setData ( $xml );
	}
	
	/**
	 * [PT]  Os campos permitidos para uma tabela
	 * [EN] The allowed fields from a table
	 *
	 * @param string $mode
	 * @param string $table
	 * @return string
	 */
	function getFields($mode = 'edit', $table) {
		
		$get = $this->info [$mode] ['fields'];
		
		if (! is_array ( $get )) {
			$get = $this->getTableFields ( $table );
		}
		
		return $get;
	
	}
	
	/**
	 * [PT] Os campos de uma tabela
	 * [EN] Get table fields
	 *
	 * @param string $table
	 * @return string
	 */
	function getTableFields($table) {
		
		$table = $this->getDiscribeTable ( $table );
		
		foreach ( array_keys ( $table ) as $key ) {
			$val [$key] = $key;
		}
		
		return $val;
	}
	
	/**
	 * Definir a paginação
	 *
	 */
	function setPagination($number = 15) {
		$this->data ['pagination'] ['per_page'] = ( int ) $number;
		
		return $this;
	}
	
	/**
	 * [PT] Calcular o colspan para a paginação e topo
	 * [EN] Calculate colspan for pagination and top
	 *
	 * @return int
	 */
	function colspan() {
		
		$totalFields = count ( $this->_fields );
		
		$a = 0;
		$i = 0;
		foreach ( $this->data ['fields'] as $value ) {
			
			if (isset ( $value ['hide'] )) {
				if ($value ['hide'] == 1) {
					$i ++;
				}
			}
			if (isset ( $value ['hRow'] )) {
				if ($value ['hRow'] == 1) {
					$totalFields --;
				}
			}
		}
		
		$totalFields = $totalFields - $i;
		
		if (@$this->info ['delete'] ['allow'] == 1) {
			$a ++;
		}
		if (@$this->info ['edit'] ['allow'] == 1) {
			$a ++;
		}
		
		$totalFields = $totalFields + $a;
		
		$colspan = $totalFields + count ( $this->extra_fields );
		$this->temp [$this->output]->colSpan = $colspan;
		
		return $colspan;
		#return count ( $this->_fields ) - $this->totalHiddenFields + count($this->extra_fields);
	}
	
	/**
	 * [PT] Aplicar o quoteidentifier aos campos da base de dados
	 * [EN] Apply quoteidentifier to the table fields
	 *
	 * @return string
	 */
	function buildSelectFields($values) {
		
		foreach ( $values as $value ) {
			
			if (isset ( $this->data ['fields'] [$value] ['sqlexp'] )) {
				
				$sqlExp = $this->data ['fields'] [$value] ['sqlexp'];
				
				if (stripos ( $value, ' AS ' )) {
					$asFinal = substr ( $value, stripos ( $value, ' as' ) + 4 );
					$asValue = substr ( $value, 0, stripos ( $value, ' as' ) );
				} else {
					$asFinal = substr ( $value, stripos ( $value, ' as' ) + 5 );
					$asValue = $value;
				}
				
				if (strpos ( $value, "." )) {
					$ini = substr ( $value, 0, (strpos ( $value, "." )) );
					
					$fields [] = $sqlExp . '(' . $this->_db->quoteIdentifier ( $asValue ) . ') AS ' . $asFinal;
				
				} else {
					
					$fields [] = $sqlExp . '(' . $this->_db->quoteIdentifier ( $asValue ) . ') AS ' . $this->_db->quoteIdentifier ( $asValue );
				}
			
			} else {
				
				if (strpos ( $value, "." )) {
					$ini = substr ( $value, 0, (strpos ( $value, "." )) );
					
					$fields [] = $this->_db->quoteIdentifier ( $ini ) . substr ( $value, strpos ( $value, "." ) );
				} else {
					
					$fields [] = $this->_db->quoteIdentifier ( $value );
				}
			
			}
		
		}
		
		return implode ( ', ', $fields );
	}
	
	/**
	 * [PT] O tipo de procura que vai ser utilizado nos filtros
	 * [PT] Por defeito é LIKE, mas não funciona em numeros onde se quero 12 também vai
	 * [PT] considerar o 1123, 1232, etc, 
	 *
	 * @param unknown_type $filtro
	 * @param unknown_type $key
	 * @return unknown
	 */
	function buildSearchType($filtro, $key) {
		$fieldsSemAsFinal = $this->removeAsFromFields ();
		
		if (@$fieldsSemAsFinal [$key] ['searchType'] != "") {
			$op = @$fieldsSemAsFinal [$key] ['searchType'];
		}
		
		$op = @strtolower ( $op );
		
		switch ($op) {
			case 'equal' :
			case '=' :
				$return = " = {$this->_db->quote ($filtro)}  ";
				break;
			case 'rlike' :
				$return = " LIKE {$this->_db->quote ( $filtro . "%" )} ";
				break;
			case 'llike' :
				$return = " LIKE {$this->_db->quote ( "%" . $filtro )} ";
				break;
			case '>=' :
				$return = " >= {$this->_db->quote ($filtro )} ";
				break;
			case '>' :
				$return = " > {$this->_db->quote ($filtro )}  ";
				break;
			case '<>' :
			case '!=' :
				$return = " <> {$this->_db->quote ($filtro )}  ";
				break;
			case '<=' :
				$return = " <= {$this->_db->quote ($filtro )}  ";
				break;
			case '<' :
				$return = " < {$this->_db->quote ($filtro )}  ";
				break;
			default :
				$return = " LIKE  {$this->_db->quote ( "%" . $filtro . "%" )} ";
				break;
		
		}
		
		return $return;
	}
	
	/**
	 * [PT] Construir o WHERE da query
	 * [EN] Build the query WHERE
	 *
	 * @return string
	 */
	
	function buildQueryWhere() {
		if (strlen ( $this->_queryWhere ) > 1) {
			return $this->_queryWhere;
		}
		
		if (strlen ( trim ( $this->_where ) ) > 1) {
			$query_where = " WHERE " . $this->_where . "  ";
			$tem_where_1 = true;
		} else {
			
			$query_where = '';
			$tem_where_1 = false;
		}
		
		$query_final = '';
		$new_where = '';
		$tem_where = false;
		
		//Vamos criar a aray para sabermos o valor dos filtro
		$valor_filters = array ();
		
		$filters = @urldecode ( $this->ctrlParams ['filters'] );
		$filters = str_replace ( "filter_", "", $filters );
		$filters = Zend_Json::decode ( $filters );
		
		$fieldsSemAsFinal = $this->removeAsFromFields ();
		
		if (is_array ( $filters )) {
			
			foreach ( $filters as $key => $filtro ) {
				
				$key = str_replace ( "bvbdot", ".", $key );
				
				if (strlen ( $filtro ) == 0 || ! in_array ( $key, $this->map_array ( $this->_fields, 'replace_AS' ) )) {
					unset ( $filters [$key] );
				
				} else {
					
					$oldKey = $key;
					
					if (@$fieldsSemAsFinal [$key] ['searchField'] != "") {
						$key = $this->replaceAsString ( $fieldsSemAsFinal [$key] ['searchField'] );
					}
					
					$new_where .= " AND $key " . $this->buildSearchType ( $filtro, $oldKey ) . "  ";
					$tem_where = true;
					$valor_filters [$key] = $filtro;
				
				}
			}
		}
		
		$this->_filtersValues = $valor_filters;
		
		if ($tem_where) {
			$query_final = "  " . $query_where;
			
			if ($tem_where && $tem_where_1) {
				$query_final = $query_final . " AND ";
			}
			
			$query_final .= "(" . substr ( $new_where, 4 ) . ")";
			
			if (! $tem_where_1) {
				$query_final = " WHERE " . $query_final;
			}
		
		} else {
			$query_final = $query_where;
		}
		
		$this->_queryWhere = $query_final;
		
		echo $this->_queryWhere;
		die ();
		return $this->_queryWhere;
	
	}
	
	/**
	 * [PT] compor a query. Apenas o order e o limit
	 * [EN] Build query. only LIMIT and ORDER
	 *
	 * @return string
	 */
	function buildQuery() {
		
		@$inicio = ( int ) $this->ctrlParams ['start'];
		
		$order = @$this->ctrlParams ['order'];
		
		$order1 = explode ( "_", $order );
		
		$orderf = strtoupper ( end ( $order1 ) );
		
		if ($orderf != 'DESC' && $orderf != 'ASC') {
			$orderf = 'ASC';
			
			$order_field = $order;
			
			$query_order = " ORDER BY " . $this->_db->quoteIdentifier ( $order_field ) . " $orderf ";
		
		} else {
			
			array_pop ( $order1 );<commit_msg>$grid->where not working. Solved. (forgot to remove the die();)<commit_after><?php
/**
 * Mascker
 *
 * LICENSE
 *
 * This source file is subject to the GNU General Public License 2.0
 * It is  available through the world-wide-web at this URL:
 * http://www.opensource.org/licenses/gpl-2.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to <EMAIL> so we can send you a copy immediately.
 *
 * @package    Mascker_Grid
 * @copyright  Copyright (c) Mascker (http://www.petala-azul.com)
 * @license    http://www.opensource.org/licenses/gpl-2.0.php   GNU General Public License 2.0
 * @version    0.1  mascker $
 * @author     Mascker (<NAME>) <<EMAIL> > 
 */

class Bvb_Grid_DataGrid {
	
	public static $_cache;
	
	/**
	 * [Onde temos as classes]
	 *
	 * @var array
	 */
	protected $template = array ();
	
	/**
	 * [O tipo de templates que pode ser utilizado]
	 *
	 * @var unknown_type
	 */
	protected $_templates;
	
	/**
	 * [PT] A lista de dir e prifx a ser utilizadores para formatar campos
	 *
	 * @var unknown_type
	 */
	protected $_formatter = array ();
	
	/**
	 * [PT] A instancia do cache
	 *
	 * @var unknown_type
	 */
	public $cacheInstance;
	
	/**
	 * Os tipode de exportação que podemos utilizar
	 *
	 * @var unknown_type
	 */
	public $export = array ('pdf', 'word', 'excel', 'print', 'wordx' );
	# public $export = array('pdf','word','excel','print','xml','csv');
	

	/**
	 * [PT] Toda a informação que não está ligada com a base de dados
	 * 
	 * [EN] All info that is not directly related to the database
	 *
	 * @var array
	 */
	public $info = array ();
	
	/**
	 * [PT] Vamos guardar as tabelas a que já foram aplicadas a query describe table
	 * [EN] Save the result of the describeTables
	 *
	 * @var array
	 */
	protected $_describeTables = array ();
	
	/**
	 * [PT] Armazenar as primrys keys para não termos que fazer o loop sempre 
	 * [PT] Que precisarmos
	 * [EN] Registry for PK
	 * 
	 *
	 * @var array
	 */
	
	protected $_getPrimaryKey = array ();
	
	/**
	 * [PT] A parte where da query // 
	 * [EN] Where part from query
	 *
	 * @var string
	 */
	protected $_queryWhere = '';
	
	/**
	 * [PT] O adpater da BD
	 * [EN] DB Adapter
	 *
	 * @var object
	 */
	protected $_db;
	
	/**
	 * [PT] A url base para constriuir a url final
	 * [EN] Baseurl
	 *
	 * @var string
	 */
	protected $_baseUrl;
	
	/**
	 * [PT] O array com o resultado da consulta à base de dados
	 * [EN] Array containing the query result from table(s)
	 *
	 * @var array
	 */
	protected $_result;
	
	/**
	 * [PT]Total de registos encontrados
	 * [EN]Total records from db query
	 *
	 * @var int
	 */
	protected $_totalRecords;
	
	/**
	 * [PT] Array com os titulos a ser aplicados
	 * [EN] Array containing filed titles
	 *
	 * @var array
	 */
	protected $_titles;
	
	/**
	 * [PT] Array contento os campos da(s) tablea(s)
	 * [EN] Array containing table(s) fields
	 *
	 * @var array
	 */
	protected $_fields = array ();
	
	/**
	 * [PT] Where definido pelo utilizador a acrescentar ao criado pela Grid
	 * [EN] Where initially defined by user
	 *
	 * @var string
	 */
	protected $_where;
	
	/**
	 * [PT] A lista de filtros
	 *
	 * @var unknown_type
	 */
	public $filters;
	
	/**
	 * [PT] Valores dos filtros que o utiliador inseriu
	 * [EN] Filters values inserted by the user
	 *
	 * @var array
	 */
	protected $_filtersValues;
	
	/**
	 * [PT] Toda a informação relacionada com a base de dados
	 * [EN] All information databse related
	 *
	 * @var array
	 */
	public $data = array ();
	
	/**
	 * [PT] Lista de filtros a ser aplicado
	 * [EN] Filters List to be applied
	 *
	 * @var array
	 */
	
	public $params = array ();
	
	/**
	 * [PT] Url parametros
	 * [EN] URL params
	 *
	 * @var string
	 */
	public $ctrlParams;
	
	/**
	 * [PT] Campos extra
	 * [EN] Extra fields array
	 *
	 * @var array
	 */
	public $extra_fields;
	
	/**
	 * [PT]A lista final de campos. Pode parecer estranho, mas é assim
	 *
	 * @var unknown_type
	 */
	protected $_finalFields;
	
	/**
	 * [PT] O colspan a aplicar na tabela
	 *
	 * @var unknown_type
	 */
	
	public $_colspan;
	
	/**
	 * [PT] O númeor de campos ocultos que existem
	 * [PT] Para depois podermos calcular o colspan
	 *
	 * @var int
	 */
	public $totalHiddenFields;
	
	/**
	 * [PT] COnfirmar que foi tudo analisado aantes de processar a query
	 *
	 * @var unknown_type
	 */
	private $consolidated = 0;
	
	/**
	 * [PT] Se vamos utilizar o cache ou não
	 * [PT] De futuro quando utilizar-mos o cache ele é limpo automaticamente nas funções CRUD
	 *
	 * @var bool
	 */
	public $cache = false;
	
	/**
	 * [PT] A lista de dirs onde procurar por "valideiros" e filtros
	 *
	 * @var array
	 */
	protected $_elements = array (0 => array () );
	
	/**
	 * [PT] TIpos dpermitidos na aplicação aos elementos do formulários
	 *
	 * @var array
	 */
	private $_elementsAllowed = array ('filter', 'validator' );
	
	/**
	 * [PT] A instancia do tradutor
	 *
	 * @var unknown_type
	 */
	public $translator;
	
	/**
	 * [PT] O campo para depos podermos ordenar caso exista a separação horizontal
	 *
	 * @var string
	 */
	private $fieldHorizontalRow;
	
	public $locale = 'en_US';
	
	/**
	 * [PT] The template instance
	 *
	 * @var unknown_type
	 */
	protected $temp;
	
	/**
	 * [PT] AS classes dos diferentes tipos de templates que já foram instanciados
	 *
	 * @var unknown_type
	 */
	public $activeTemplates = array ();
	
	/**
	 * [PT] A função __construct recebe o adapter para se liga à base de dados
	 * [PT] É também tratada toda a informação relacionada com a url e os params
	 * [PT] É também instaciada o Auth do Zend_Auth. A autenticação deve ter sido
	 * [PT] Efectuada utilizando o método store, para agora poder validar o user
	 * 
	 * [EN] The __construct function receives the db adapter. All information related to the
	 * [EN] URL is also processed here
	 * [EN] To edit, add, or delete records, a user must be authenticated, so we instanciate 
	 * [EN] it here. Remember to uses the method write when autenticating a user, so we can know 
	 * [EN] if its logged or not
	 *
	 * @param array $data
	 */
	function __construct($db) {
		
		//[PT] Iniciar o adapter da base de dados
		$this->_db = $db;
		$this->_db->setFetchMode ( Zend_Db::FETCH_OBJ );
		
		$this->ctrlParams = Zend_Controller_Front::getInstance ()->getRequest ()->getParams ();
		$this->_baseUrl = Zend_Controller_Front::getInstance ()->getBaseUrl ();
		
		$this->setPagination ();
		
		//[PT] Valos adicionar os elementos da Zend
		$this->addElementDir ( 'Zend/Filter', 'Zend_Filter', 'filter' );
		$this->addElementDir ( 'Zend/Validate', 'Zend_Validate', 'validator' );
		
		$this->addFormatterDir ( 'Bvb/Grid/Formatter', 'Bvb_Grid_Formatter' );
		
		$this->addTemplateDir ( 'Bvb/Grid/Template/Table', 'Bvb_Grid_Template_Table', 'table' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Pdf', 'Bvb_Grid_Template_Pdf', 'pdf' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Print', 'Bvb_Grid_Template_Print', 'print' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Word', 'Bvb_Grid_Template_Word', 'word' );
		$this->addTemplateDir ( 'Bvb/Grid/Template/Wordx', 'Bvb_Grid_Template_Wordx', 'wordx' );
	
	}
	
	/**
	 * [PT] Traduzir as mensagens
	 *
	 * @param string $message
	 * @return string
	 */
	function __($message) {
		
		if (Zend_Registry::isRegistered ( 'Zend_Translate' )) {
			$this->translator = Zend_Registry::get ( 'Zend_Translate' );
			$this->translator->setLocale ( $this->locale );
			$message = $this->translator->translate ( $message );
		}
		
		return $message;
	
	}
	
	/**
	 * [PT] não faz mais nada senão passar o valor para __set
	 * [PT] só que assim podemos utilizar o para∏¿≈}{⁄{}{⁄ (não me lembro do nome)
	 * [PT] %grid->from('barcelos')
	 *             ->noFilters(1)->
	 *             ->noorder(1);
	 *
	 * @param unknown_type $name
	 * @param unknown_type $value
	 * @return unknown
	 */
	function __call($name, $value) {
		if (substr ( $name, 0, 3 ) == 'set') {
			$name = substr ( $name, 3 );
		}
		
		$this->__set ( $name, $value [0] );
		
		return $this;
	}
	
	/**
	 * [Para podemros utiliza]
	 *
	 * @param string $var
	 * @param string $value
	 */
	
	function __set($var, $value) {
		
		$data = array ('from', 'order', 'where', 'primaryKey', 'table', 'fields', 'hide' );
		
		if (in_array ( $var, $data )) {
			if ($var == 'from' && ! strpos ( " ", trim ( $value ) )) {
				$this->data ['from'] = trim ( $value );
				$this->data ['table'] = trim ( $value );
			} else {
				$this->data [$var] = $value;
			}
		
		} else {
			$this->info [$var] = $value;
		}
	
	}
	
	/**
	 * [PT] Buscar a copmposição da tabela
	 * [PT] Depois disso vamos meter a tabela num array. se precisar-mos amis tarde,
	 * [PT] Ela já está lá
	 * 
	 * [EN] Get table description and then save it to a array.
	 *
	 * @param array|string $table
	 * @return array
	 */
	function getDiscribeTable($table) {
		
		if (! @is_array ( $this->_describeTables [$table] )) {
			$describe = $this->_db->describeTable ( $table );
			$this->_describeTables [$table] = $describe;
		}
		
		return $this->_describeTables [$table];
	}
	
	/**
	 * [PT]Adicionar um campo à lista ja existente
	 *
	 * @param string $field
	 * @param array $options
	 * @return unknown
	 */
	function addColumn($field, $options = array()) {
		
		if (@is_array ( $this->data ['fields'] )) {
			
			if (! array_key_exists ( $field, $this->data ['fields'] )) {
				$this->data ['fields'] [$field] = $options;
				
				if (isset ( $options ['hRow'] )) {
					
					if ($options ['hRow'] == 1) {
						$this->fieldHorizontalRow = $field;
						$this->info ['hRow'] = array ('field' => $field, 'title' => $options ['title'] );
					}
				
				}
			}
		} else {
			$this->data ['fields'] [$field] = $options;
		
		}
		return $this;
	}
	
	/**
	 * [PT] Adicionar mais uma opção para encontro de formatadores de cnteúdo
	 * desde data, numeros, imagens, etc, etc
	 *
	 * @param string $dir
	 * @param string $prefix
	 * @return $this
	 */
	function addFormatterDir($dir, $prefix) {
		$this->_formatter [] = array ('dir' => trim ( $dir, "/" ) . '/', 'prefix' => trim ( $prefix, "_" ) );
		
		return $this;
	}
	
	function addElementDir($dir, $prefix, $type = 'filter') {
		
		if (! in_array ( strtolower ( $type ), $this->_elementsAllowed )) {
			throw new Exception ( 'Type not recognized' );
		}
		
		$this->_elements [$type] [] = array ('dir' => trim ( $dir, "/" ) . '/', 'prefix' => trim ( $prefix, "_" ) );
		
		return $this;
	}
	
	/**
	 * [PT] Formatar o valor de cada campo que vem da base de dados
	 *
	 * @param unknown_type $value
	 * @param unknown_type $formatter
	 * @param unknown_type $options
	 * @return unknown
	 */
	function applyFormat($value, $formatter) {
		
		//[PT] Para simplificar as coisas vemos se é um array.
		//[PT] Pode não ser necessários argumentos adicionais
		if (is_array ( $formatter )) {
			$result = $formatter [0];
			$options = $formatter [1];
		} else {
			$result = $formatter;
			$options = null;
		}
		
		//[Vamos meter isto por ordem reversa para os ultimos serem os primeiros
		//Faz sentido e acho que é uq eo que é mais utilizado
		$format = array_reverse ( $this->_formatter );
		
		foreach ( $format as $find ) {
			$file = $find ['dir'] . ucfirst ( $result ) . '.php';
			$class = $find ['prefix'] . '_' . ucfirst ( $result );
			
			require_once ($file);
			
			if (class_exists ( $class )) {
				$t = new $class ( $options );
				$return = $t->format ( $value );
				$apply = 1;
			}
		
		}
		
		if ($apply !== 1) {
			$return = $value;
		}
		return $return;
	
	}
	
	/**
	 * [PT] Todos os dados relacionados com a conneção á base de dados
	 * [PT] Os filtros, campos extra, etc, etc
	 * 
	 * [EN] All information related with database.
	 *[EN] Filters, extra fields, etc, etc
	 * @param string $data
	 * 
	 * */
	
	function setData($data) {
		
		$this->data = $data ['data'];
		
		$this->info = $data;
		
		if (! is_array ( $this->data ['table'] )) {
			$this->data ['table'] = $this->data ['from'];
		}
	
	}
	
	/**
	 * Criar a fgrid através de uma ficheiro XML
	 * 
	 * 
	 * TESTE
	 * TEST
	 * TESTE
	 * TEST
	 * TESTE
	 * TEST
	 * TESTE
	 * TEST
	 *
	 */
	function setDataFromXml($file) {
		
		$fc = Zend_Controller_Front::getInstance ();
		$modulo = strtolower ( $fc->getRequest ()->getModuleName () );
		
		if (strpos ( $file, '_' ) === false) {
			$file = $modulo . "_" . ucfirst ( $file );
		}
		//Temos que
		$file = rtrim ( str_replace ( "_", "/grids/", $file ), ".xml" ) . ".xml";
		
		$xml = $this->object2array ( simplexml_load_file ( $file ) );
		
		if (strlen ( $xml ['data'] ['where'] ) > 0) {
			$final = $xml ['data'] ['where'];
			$final1 = preg_match_all ( "/{eval}(.*?){\/eval}/", $final, $t );
			$t2 = $t;
			
			$i = 0;
			foreach ( $t2 [1] as $value ) {
				$h = eval ( "return " . $value . ";" );
				$final = str_replace ( $t [0] [$i], $h, $final );
				$i ++;
			}
			
			$xml ['data'] ['where'] = $final;
		
		}
		
		foreach ( $xml ['data'] ['fields'] as $key => $final ) {
			if (is_array ( $final ['@attributes'] )) {
				unset ( $xml ['data'] ['fields'] [$key] );
				$xml ['data'] ['fields'] [$key . " AS " . $final ['@attributes'] ['as']] = $final;
			}
		}
		
		self::setData ( $xml );
	}
	
	/**
	 * [PT]  Os campos permitidos para uma tabela
	 * [EN] The allowed fields from a table
	 *
	 * @param string $mode
	 * @param string $table
	 * @return string
	 */
	function getFields($mode = 'edit', $table) {
		
		$get = $this->info [$mode] ['fields'];
		
		if (! is_array ( $get )) {
			$get = $this->getTableFields ( $table );
		}
		
		return $get;
	
	}
	
	/**
	 * [PT] Os campos de uma tabela
	 * [EN] Get table fields
	 *
	 * @param string $table
	 * @return string
	 */
	function getTableFields($table) {
		
		$table = $this->getDiscribeTable ( $table );
		
		foreach ( array_keys ( $table ) as $key ) {
			$val [$key] = $key;
		}
		
		return $val;
	}
	
	/**
	 * Definir a paginação
	 *
	 */
	function setPagination($number = 15) {
		$this->data ['pagination'] ['per_page'] = ( int ) $number;
		
		return $this;
	}
	
	/**
	 * [PT] Calcular o colspan para a paginação e topo
	 * [EN] Calculate colspan for pagination and top
	 *
	 * @return int
	 */
	function colspan() {
		
		$totalFields = count ( $this->_fields );
		
		$a = 0;
		$i = 0;
		foreach ( $this->data ['fields'] as $value ) {
			
			if (isset ( $value ['hide'] )) {
				if ($value ['hide'] == 1) {
					$i ++;
				}
			}
			if (isset ( $value ['hRow'] )) {
				if ($value ['hRow'] == 1) {
					$totalFields --;
				}
			}
		}
		
		$totalFields = $totalFields - $i;
		
		if (@$this->info ['delete'] ['allow'] == 1) {
			$a ++;
		}
		if (@$this->info ['edit'] ['allow'] == 1) {
			$a ++;
		}
		
		$totalFields = $totalFields + $a;
		
		$colspan = $totalFields + count ( $this->extra_fields );
		$this->temp [$this->output]->colSpan = $colspan;
		
		return $colspan;
		#return count ( $this->_fields ) - $this->totalHiddenFields + count($this->extra_fields);
	}
	
	/**
	 * [PT] Aplicar o quoteidentifier aos campos da base de dados
	 * [EN] Apply quoteidentifier to the table fields
	 *
	 * @return string
	 */
	function buildSelectFields($values) {
		
		foreach ( $values as $value ) {
			
			if (isset ( $this->data ['fields'] [$value] ['sqlexp'] )) {
				
				$sqlExp = $this->data ['fields'] [$value] ['sqlexp'];
				
				if (stripos ( $value, ' AS ' )) {
					$asFinal = substr ( $value, stripos ( $value, ' as' ) + 4 );
					$asValue = substr ( $value, 0, stripos ( $value, ' as' ) );
				} else {
					$asFinal = substr ( $value, stripos ( $value, ' as' ) + 5 );
					$asValue = $value;
				}
				
				if (strpos ( $value, "." )) {
					$ini = substr ( $value, 0, (strpos ( $value, "." )) );
					
					$fields [] = $sqlExp . '(' . $this->_db->quoteIdentifier ( $asValue ) . ') AS ' . $asFinal;
				
				} else {
					
					$fields [] = $sqlExp . '(' . $this->_db->quoteIdentifier ( $asValue ) . ') AS ' . $this->_db->quoteIdentifier ( $asValue );
				}
			
			} else {
				
				if (strpos ( $value, "." )) {
					$ini = substr ( $value, 0, (strpos ( $value, "." )) );
					
					$fields [] = $this->_db->quoteIdentifier ( $ini ) . substr ( $value, strpos ( $value, "." ) );
				} else {
					
					$fields [] = $this->_db->quoteIdentifier ( $value );
				}
			
			}
		
		}
		
		return implode ( ', ', $fields );
	}
	
	/**
	 * [PT] O tipo de procura que vai ser utilizado nos filtros
	 * [PT] Por defeito é LIKE, mas não funciona em numeros onde se quero 12 também vai
	 * [PT] considerar o 1123, 1232, etc, 
	 *
	 * @param unknown_type $filtro
	 * @param unknown_type $key
	 * @return unknown
	 */
	function buildSearchType($filtro, $key) {
		$fieldsSemAsFinal = $this->removeAsFromFields ();
		
		if (@$fieldsSemAsFinal [$key] ['searchType'] != "") {
			$op = @$fieldsSemAsFinal [$key] ['searchType'];
		}
		
		$op = @strtolower ( $op );
		
		switch ($op) {
			case 'equal' :
			case '=' :
				$return = " = {$this->_db->quote ($filtro)}  ";
				break;
			case 'rlike' :
				$return = " LIKE {$this->_db->quote ( $filtro . "%" )} ";
				break;
			case 'llike' :
				$return = " LIKE {$this->_db->quote ( "%" . $filtro )} ";
				break;
			case '>=' :
				$return = " >= {$this->_db->quote ($filtro )} ";
				break;
			case '>' :
				$return = " > {$this->_db->quote ($filtro )}  ";
				break;
			case '<>' :
			case '!=' :
				$return = " <> {$this->_db->quote ($filtro )}  ";
				break;
			case '<=' :
				$return = " <= {$this->_db->quote ($filtro )}  ";
				break;
			case '<' :
				$return = " < {$this->_db->quote ($filtro )}  ";
				break;
			default :
				$return = " LIKE  {$this->_db->quote ( "%" . $filtro . "%" )} ";
				break;
		
		}
		
		return $return;
	}
	
	/**
	 * [PT] Construir o WHERE da query
	 * [EN] Build the query WHERE
	 *
	 * @return string
	 */
	
	function buildQueryWhere() {
		if (strlen ( $this->_queryWhere ) > 1) {
			return $this->_queryWhere;
		}
		
		if (strlen ( trim ( $this->_where ) ) > 1) {
			$query_where = " WHERE " . $this->_where . "  ";
			$tem_where_1 = true;
		} else {
			
			$query_where = '';
			$tem_where_1 = false;
		}
		
		$query_final = '';
		$new_where = '';
		$tem_where = false;
		
		//Vamos criar a aray para sabermos o valor dos filtro
		$valor_filters = array ();
		
		$filters = @urldecode ( $this->ctrlParams ['filters'] );
		$filters = str_replace ( "filter_", "", $filters );
		$filters = Zend_Json::decode ( $filters );
		
		$fieldsSemAsFinal = $this->removeAsFromFields ();
		
		if (is_array ( $filters )) {
			
			foreach ( $filters as $key => $filtro ) {
				
				$key = str_replace ( "bvbdot", ".", $key );
				
				if (strlen ( $filtro ) == 0 || ! in_array ( $key, $this->map_array ( $this->_fields, 'replace_AS' ) )) {
					unset ( $filters [$key] );
				
				} else {
					
					$oldKey = $key;
					
					if (@$fieldsSemAsFinal [$key] ['searchField'] != "") {
						$key = $this->replaceAsString ( $fieldsSemAsFinal [$key] ['searchField'] );
					}
					
					$new_where .= " AND $key " . $this->buildSearchType ( $filtro, $oldKey ) . "  ";
					$tem_where = true;
					$valor_filters [$key] = $filtro;
				
				}
			}
		}
		
		$this->_filtersValues = $valor_filters;
		
		if ($tem_where) {
			$query_final = "  " . $query_where;
			
			if ($tem_where && $tem_where_1) {
				$query_final = $query_final . " AND ";
			}
			
			$query_final .= "(" . substr ( $new_where, 4 ) . ")";
			
			if (! $tem_where_1) {
				$query_final = " WHERE " . $query_final;
			}
		
		} else {
			$query_final = $query_where;
		}
		
		$this->_queryWhere = $query_final;
		
	
		return $this->_queryWhere;
	
	}
	
	/**
	 * [PT] compor a query. Apenas o order e o limit
	 * [EN] Build query. only LIMIT and ORDER
	 *
	 * @return string
	 */
	function buildQuery() {
		
		@$inicio = ( int ) $this->ctrlParams ['start'];
		
		$order = @$this->ctrlParams ['order'];
		
		$order1 = explode ( "_", $order );
		
		$orderf = strtoupper ( end ( $order1 ) );
		
		if ($orderf != 'DESC' && $orderf != 'ASC') {
			$orderf = 'ASC';
			
			$order_field = $order;
			
			$query_order = " ORDER BY " . $this->_db->quoteIdentifier ( $order_field ) . " $orderf ";
		
		} else {
			
			array_pop ( $order1 );
{'fraction_non_alphanumeric': 0.1717728910423655, 'fraction_numerical': 0.0032302149335321157, 'mean_word_length': 2.6111260655002244, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 287, 'https://': 0, 'lorem ipsum': 0, 'www.': 6, 'xml': 24}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1658477', 'n_tokens_mistral': 16642, 'n_tokens_neox': 13792, 'n_words': 3943}