Location: PHPKode > scripts > Piragibe > piragibe/PGBDataBlock.php
<?php
/**
 * A PGBDataBlock is a window to your actual ( database ) data. It can be used
 * to show, create, update and delete data on its associated {@link PGBRecordSet}.
 * 
 * The data block is the glue that ties all the framework's components together.
 * It links relational tables to the browser screen; coordinates the rendering
 * work, translating data from internal to visual formats; materialises, in a
 * visual fashion, the relationships among objects and data hierarchies; and
 * controls user interaction with the application in almost every aspect. It can
 * be seen as a crucial part of PIRAGIBE's persistency scheme backbone.
 * 
 * Each screen in an application built with PIRAGIBE is a set of coordinated,
 * master x slave style, data blocks.
 * @package Piragibe
 * @author Francisco Piragibe
 * @version 1.08
 * @copyright Copyright &copy; 2006, Francisco Piragibe
 * This file is part of The PIRAGIBE Framework.
 *
 * The PIRAGIBE Framework is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * The PIRAGIBE Framework is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with The PIRAGIBE Framework; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
class PGBDataBlock {

    /**
     * Is the block operating in query mode?
     * @var boolean query mode flag
     */
    var $m_queryMode;       // bloco esta em query mode?
    /**
     * The block properties array. 
     * 
     * This is a very complex structure, containing all of the block behaviour
     * description. As many keys have been named in portuguese, an additional layer
     * of difficulty has been posed to the foreign developer. For this, I must apologise.
     * The array entries are: 
     * - Nome - > (string) the block name (this is mandatory).
     * - Titulo -> (string) the block title (this will be rendered on the top of the block - no defaults).
     * - Pagina -> (integer) how many records should be shown on the screen (default is ALL records).
     * - Seletor -> (boolean) should a pin be rendered next to each record, allowing for record selection?
     * (a pin will be rendered anyway if the block has coordinated slaves) (default is context dependent).
     * - Rs -> (PGBRecordSet) the {@link PGBRecordSet} that should be linked to the block (only for
     * MASTER data blocks; slave blocks should have the SlaveRs entry set, instead of this one) (no defaults).
     * - InsertOk -> (boolean) can the block be used to create new records? (default is NO)
     * - UpdateOk -> (boolean) can the block be used to update records? (default is NO)
     * - DeleteOk -> (boolean) can the block be used to delete records? (default is NO)
     * - QueryOk -> (boolean) should the block support an automatic "query by example" screen for data
     * filtering? (default is NO).
     * - SingleRecord -> (boolean) should the block contain ONLY ONE record? (default is NO).
     * - AgenteNls -> ({@link PGBNlsAgent}) the national language support agent object that should be used when
     * rendering NLS dependent data (no default).
     * - SlaveBlocks -> (array of PGBDataBlocks) the set of blocks that are slaves to this one (default is
     * NO SLAVES).
     * - SlaveRs -> (string) if the block is a slave block, the slave recordset NAME. The name should point
     * to one of the master data block's recordset's slave. This entry is ignored for master data blocks.
     * - RenderDefs -> (array of {@link PGBRenderDefs}) rendering definitions for the block fields. Only fields
     * having a rendering definition will be rendered. This array has the field names (case sensitive) as keys and
     * PGBRenderDefs as values.
     * 
     */
    var $m_props;           // array com definicoes do bloco
    //      ['Nome']        -> nome do bloco
    //      ['Titulo']      -> titulo, para renderizacao
    //      ['Pagina']      -> numero de registros a exibir por vez
    //      ['Seletor']     -> renderiza ou nao um pino de selecao de registros a esquerda
    //      ['rs']          -> REFERENCIA ao recordset principal base para o bloco
    //      ['InsertOk']    -> podem ser criados registros no bloco?
    //      ['UpdateOk']    -> podem ser atualizados registros no bloco?
    //      ['DeleteOk']    -> podem ser excluidos registros no bloco?
    //      ['QueryOk']     -> podem ser feitas pesquisas por exemplo?
    //      ['SingleRecord'] -> bloco deve conter apenas UM registro?
    //      ['AgenteNls']   -> agente NLS a utilizar nas edicoes
    //      ['RenderDefs']  -> array com definicoes de renderizacao para os campos do bloco
    //          apenas os campos constantes deste array serao renderizados
    //          formato:
    //          [<nome do campo>] -> PGBRenderingDefinition referente ao campo
    //      ['SlaveBlocks'] -> array com definicoes de blocos escravos deste
    //          cada definicao segue o mesmo layout das definicoes de blocos, EXCETO:
    //          ['SlaveRs'] -> nome do recordset escravo
    //          ['rs']      -> nunca esta definido
    /**
     * The message error array.
     * @var array array of message errors to be displayed
     */
    var $m_erros;           // mensagens de erro do bloco
    /**
     * This array maps block positions to recordset positions (saying record 354 of the
     * recordset occupies position 5 in the block, for instance).
     * @var array the block x recordset map array
     */
    var $m_pos;             // array mapeando ordem de aparicao no bloco x numero do registro
    // ex. $m_pos[1] = 356 => o 2o. registro do bloco eh o 356o. do recordset
    /**
     * This array records whether a given record should be shown with additional details
     * visible or hidden.
     * 
     * This only makes sense for multi-line records or tree rendering.
     * @var array additional details status for each record
     */
    var $m_deta;            // array mapeando numero de registro -> detalhes adicionais abertos
    /**
     * This array stores field contents for the block. The first subscript is the field name;
     * the second is the block record number. Each node has a {@link PGBField} object.
     * @var array field contents array
     */
    var $m_fields;          // array com campos representados
    //  [<nome do campo>] -> array com valores por posicao no bloco
    //      [n] -> objeto PGBField que representa o campo
    /**
     * Block status.
     * 
     * Possible status are:
     * - 0 -> created (and possibly empty)
     * - 1 -> queried (contains data retrieved from a query, unchanged)
     * - 2 -> changed (contains modified data - should be saved)
     * @var integer the block status code
     */
    var $m_status;          // estado do bloco
    /**
     * Auxiliary array to control rendering of multiline records in a tabular fashion.
     * 
     * As there is no obligation to define block fields ordered by row, this is used to
     * perform ordering before the actual rendering begins.
     * @var array map of fields that should be rendered in each row.
     */
    var $m_rendsl;          // array auxiliar para renderizacao de tabelas multilinha
    /**
     * The record position IN THE UNDELYING RECORDSET that will be taken as the starting
     * one.
     * 
     * The block can show only a section of the recordset's records (10 records, for example).
     * This variable controls where in the recordset we are.
     * @var integer the absolute (recordset) record position of the block's first record. 
     */
    var $m_abspos;          // posicao absoluta, para controle de exibicao
    
    /**
     * Standard constructor.
     * @param array $pParms the array of definitions
     */
    function PGBDataBlock( $pParms ) {
        $this->m_erros  = array();
        $this->m_status = 0;
        $this->m_abspos = 0;
        $pProps         = new PGBPropertyBag( $pParms );
        $this->m_props  = $pProps->getBag();
        $this->m_pos    = array();
        $this->m_fields = array();
        $this->m_rendsl = array();
        if ( !isset( $this->m_props['agentenls'] ) ) {
            $this->setNlsAgent( new PGBNlsAgent('pt') );
        }
        if ( !isset( $this->m_props['nome'] ) ) {
            $this->setName( 'xpto' );
        }
        if ( !isset( $this->m_props['titulo'] ) ) {
            $this->setTitle( '' );
        }
        if ( !isset( $this->m_props['pagina'] ) ) {
            $this->setPageSize( 0 );
        }
        if ( !isset( $this->m_props['seletor'] ) ) {
            $this->setSeletor( false );
        }
        if ( !isset( $this->m_props['renderdefs'] ) ) {
            $this->m_props['renderdefs'] = array();
        }
        if ( !isset( $this->m_props['slaveblocks'] ) ) {
            $this->m_props['slaveblocks'] = array();
        }
        else {
            $this->setSlaveBlocks( $pProps->get('slaveblocks') );
        }
        if ( !isset( $this->m_props['slavers'] ) ) {
            $this->m_props['slavers'] = NULL;
        }
        if ( !isset( $this->m_props['insertok'] ) ) {
            $this->m_props['insertok'] = false;
        }
        if ( !isset( $this->m_props['updateok'] ) ) {
            $this->m_props['updateok'] = false;
        }
        if ( !isset( $this->m_props['deleteok'] ) ) {
            $this->m_props['deleteok'] = false;
        }
        if ( !isset( $this->m_props['queryok'] ) ) {
            $this->m_props['queryok'] = false;
        }
        if ( !isset( $this->m_props['singlerecord'] ) ) {
            $this->m_props['singlerecord'] = false;
        }
        $this->m_queryMode = false;
    }
    
    /**
     * Validates a field.
     * 
     * Should be implemented by each heir.
     * @return mixed the error message to be displayed, or NULL, if validation is successful
     * @param PGBField &$pField the field to be validated
     */
    function validate( &$pField ) {
    // valida o campo que corresponde ao objeto PGBField dado - deve ser implementado pelo herdeiro
    // se houver erro, deve retornar o texto da mensagem; senao, deve retornar NULL
        return NULL;
    }
    
    /**
     * Validates a record (a set of related fields).
     * 
     * Should be implemented by each heir, and shouldn't return anything. If there are error
     * messages to show, heir should call newError() as many times as necessary.
     * @param integer $pRn the record position within the block
     * @param array &$pFields array of {@link PGBField}s that make up the record
     */
    function validateRecord( $pRn, &$pFields ) {
    // valida um registro, definido como um array de campos, cada qual identificado por seu nome, 
    // passado como parametro - ser implementado pelo herdeiro e deve preencher o bloco de erros
    // nao retorna nada
    }
    
    /**
     * Validates the entire data block. This is a hook for inter-record validation rules.
     * 
     * Should be implemented by each heir, and shouldn't return anything. If there are error
     * messages to show, heir should call newError() as many times as necessary. Access to the
     * records and fields can be obtained by calling suitable getters.
     */
    function validateBlock() {
    // valida relacoes inter registros que o bloco por acaso deva ter - o herdeiro implementa esta -
    // deve alimentar o array de erros e nao retornar nada        
    }
    
    /**
     * Block validation coordinator.
     * 
     * This calls validate() for every field, validateRecord() for every record in the block 
     * and, finally, calls validateBlock(). These three methods should be implemented by each
     * heir; their default implementation have null bodies (i.e. always return OK). It's worth
     * saying that record and block validations are called only if there are no errors in
     * field validations. In other words, record and block validation methods can be sure they
     * will receive only valid fields.
     */
    function validateBlockArray() {
    // valida o bloco inteiro e seus registros - deve ser implementado pelo herdeiro quando houver
    //  necessidade de validacoes intercampos ou interregistros - deve preencher o bloco de erros
    // em primeiro lugar, valida os registros...
        $rs         = &$this->getRecordSet();
        if ( !is_a( $rs, 'PGBRecordSet' ) ) {
            return;
        }
        $w_contador = count( $this->m_pos );
        $w_kont     = 0;
        for( $j=0; $j < $w_contador; $j++ ) {
            if ( !$rs->isDeleted( $this->m_pos[ $j ] ) ) {
                $a_fields = array();
                reset( $this->m_fields );
                while( list( $fn, $af ) = each( $this->m_fields ) ) {
                    //print '<pre>' . $fn . ' => ' . $this->m_fields[ $fn ][ $j ] . '</pre>';
                    $a_fields[ $fn ] = &$this->m_fields[ $fn ][ $j ];
                    if ( is_a( $a_fields[ $fn ], 'PGBField' ) ) {
                    // campos de eco de registros novos nao estao preenchidos ainda...
                        $m  = $this->validate( $a_fields[ $fn ] );
                        if ( $m ) {
                            $this->newError( $j, $m );
                            $w_kont++;
                        }
                    }
                }
                if ( $w_kont == 0 ) {
                // so validamos registro e bloco se nao houver erros a nivel de campo
                    //print '<pre> validando registro ' . $j . '</pre>';
                    $this->validateRecord( $j, $a_fields );
                }
            }
        }
    // finalmente, valida o bloco como um todo (o herdeiro deve implementar esta)
        if ( $w_kont == 0 ) {
        // so validamos registro e bloco se nao houver erros a nivel de campo
            $this->validateBlock();
        }    
    }
    
    /**
     * Getter for the NLS agent associated to the block.
     * @return PGBNlsAgent the NLS agent associated to this block
     */
    function &getNlsAgent() {
        return $this->m_props['agentenls'];
    }
    
    /**
     * Setter for the NLS agent.
     * @param PGBNlsAgent $pNls the NLS agent to associate
     */
    function setNlsAgent( $pNls ) {
        if ( is_a( $pNls, 'PGBNlsAgent' ) ) {
            $this->m_props['agentenls'] = $pNls;
        }
    }
    
    /**
     * Getter for the message error array.
     * @return array the message error array
     */
    function getErrors() {
        return $this->m_erros;
    }

    /**
     * Standard beautifier for a given record.
     * 
     * beautify() acts according to the set of rendering definitions associated to the data
     * block. The method is responsible for retrieving echo fields (names related to a given
     * code, for instance) and preparing the record for rendering. Its services are consumed
     * mainly by the rendering methods (renderForm and renderTabular, for example).
     * @param PGBRecord &$pRecord the record to be beautified
     */
    function &beautify( &$pRecord ) {
    // complementa o registro, formando campos dependentes e outros - deve ser complementado pelo
    // herdeiro, e retornar o proprio registro, finalizado
        if ( !is_a( $pRecord, 'PGBRecord' ) ) {
            return $pRecord;
        }
        $o_rsb      = &$this->getRecordSet();
        $o_conn     = &$o_rsb->getConnection();
        $p_rowdef   = $this->getRenderingDefinitions();
        reset( $p_rowdef );
        while( list( $k, $c ) = each( $p_rowdef ) ) {
            //print '<pre>' . $k . ' -> '; print var_dump( $c ) . '</pre>';
            $w_f  = &$pRecord->getField( $k );
            $w_ed = $c->get( 'ExtraDefs' );
            if ( $w_ed ) {
                $w_pc = $w_ed->get( 'PostChange' );
                if ( $w_pc ) {
                    // substitui ? pelo valor do campo corrente
                    $w_sqlr = str_replace( '?', $w_f->getValue(), $w_pc );
                    //print '<pre>F1: ' . $w_sqlr . '</pre>';
                    // substitui campos :XXX: pelos valores correspondentes
                    $a_sqlr = explode( ':', $w_sqlr );
                    reset( $a_sqlr );
                    while( list( $pp, $ps ) = each( $a_sqlr ) ) {
                        if ( isset( $p_rowdef[ $ps ] ) ) {
                            $w_f1           = $pRecord->getField( $ps );
                            $a_sqlr[ $pp ]  = $w_f1->getValue();
                        }
                    }
                    $w_sqlr = implode( '', $a_sqlr );
                    //print '<pre>F2: ' . $w_sqlr . '</pre>';
                    // executa o comando
                    $o_rs  = $o_conn->query( $w_sqlr );
                    //print '<pre>' . $w_sqlr . ' - ' . $o_rs->rowCount() . "</pre>";
                    if ( $o_rs->rowCount() > 0 ) {
                        $o_rec  = $o_rs->getCurrentRecord();
                        $a_flds = $o_rec->getFields();
                        reset( $a_flds );
                        while( list( $fn, $ff ) = each( $a_flds ) ) {
                            $pRecord->updateField( $ff );
                        }
                    }
                }
            }
        }
        return $pRecord;
    }

    /**
     * Getter for the block x recordset map array.
     * @return array the block x recordset map array
     */
    function getPosArray() {
    // retorna o array mapa de posicoes bloco x registro
        return $this->m_pos;
    }
    
    /**
     * Getter for the block fields array.
     * @return array the block fields array
     */
    function &getBlockFieldsArray() {
        return $this->m_fields;
    }

    /**
     * Getter for a specific block field, identified by name and record position.
     * @return PGBField the field
     * @param string $pNome the field name
     * @param integer $pPos the record position
     */
    function &getBlockField( $pNome, $pPos ) {
    // retorna o PGBField que ocupa a posicao dada
        if ( isset( $this->m_fields[$pNome][$pPos] ) ) {
            return $this->m_fields[$pNome][$pPos];
        }
        else {
            return NULL;
        }
    }
    
    /**
     * Counts errors in this block and in its slave blocks, that is, in the entire
     * block hierarchy.
     * @return integer the error count
     */
    function countErrors() {
        $w_resp = count( $this->getErrors() );
        foreach( $this->m_props['slaveblocks'] as $sbi ) {
            $w_resp += $sbi->countErrors();
        }
        return $w_resp;
    }
    
    /**
     * Getter for the block title.
     * @return string the block title.
     */
    function getTitle() {
        return $this->m_props['titulo'];
    }
    
    /**
     * Setter for the block title.
     * @param string $pTit the new block title.
     */
    function setTitle( $pTit ) {
        $this->m_props['titulo'] = $pTit;
    }

    /**
     * Adds an error message to the error message array.
     *
     * Error messages are linked to the block records through the record number parameter.
     * Record number -1 means the block as a whole, not any particular record. This allows
     * for messages related to the entire set of records, rather than to a single one.
     * @param integer $pRn the record position the message is related to.
     * @param string $pMens the message text.
     */
    function newError( $pRn, $pMens ) {
    // armazena uma nova mensagem de erro
        if ( ! is_null( $pMens ) ) {
            //print '<pre> erro ' . $pRn . ' => ' . $pMens . '</pre>';
            $this->m_erros[$pRn][] = $pMens;
        }
    }
    
    /**
     * Getter for the block status.
     * @return integer the block status code.
     */
    function getStatus() {
        return $this->m_status;
    }

    /**
     * Is the block in query mode?
     * @return boolean the answer.
     */
    function isInQueryMode() {
        return $this->m_queryMode;
    }

    /**
     * Can the block be used to create new records?
     * @return boolean the answer.
     */
    function canInsert() {
        return $this->m_props['insertok'];
    }

    /**
     * Setter for the insert allowed flag.
     * @param boolean $pFlag the new insert allowed flag
     */
    function setInsertOk( $pFlag ) {
        $this->m_props['insertok'] = ($pFlag === true ? true : false);
    }

    /**
     * Can records be updated through the block?
     * @return boolean the answer.
     */
    function canUpdate() {
        return $this->m_props['updateok'];
    }

    /**
     * Setter for the update allowed flag.
     * @param boolean $pFlag the new update allowed flag.
     */
    function setUpdateOk( $pFlag ) {
        $this->m_props['updateok'] = ($pFlag === true ? true : false);
    }

    /**
     * Can records be deleted through this block?
     * @return boolean the answer.
     */
    function canDelete() {
        return $this->m_props['deleteok'];
    }

    /**
     * Setter for the delete allowed flag.
     * @param boolean $pFlag the new delete allowed flag.
     */
    function setDeleteOk( $pFlag ) {
        $this->m_props['deleteok'] = ($pFlag === true ? true : false);
    }

    /**
     * Should query mode be provided for this block?
     * 
     * Query mode allows the user to ask for the entering of filtering criteria that
     * should be applied to the underlying recordset, thus retrieving only a subset
     * of its original records.
     * @return boolean the answer.
     */
    function canQuery() {
        return $this->m_props['queryok'];
    }

    /**
     * Setter for the query allowed flag.
     * @param boolean $pFlag the new query allowed flag.
     */
    function setQueryOk( $pFlag ) {
        $this->m_props['queryok'] = ($pFlag === true ? true : false);
    }
    
    /**
     * Should the block contain only a single record?
     * 
     * Single recorded blocks are useful for representing 1:1 relationships, for instance,
     * or control blocks, where more than one record doesn't make any sense.
     * @return boolean the answer.
     */
    function isSingleRecord() {
        return $this->m_props['singlerecord'];
    }
    
    /**
     * Setter for the single record flag.
     * @param boolean $pFlag the new single record flag.
     */
    function setSingleRecord( $pFlag ) {
        $this->m_props['singlerecord'] = ($pFlag === true ? true : false);
    }

    /**
     * Setter for the slave recordset name.
     * 
     * Only makes sense if the block is a slave block.
     * @param string $pRsName the slave recordset name.
     */
    function setSlaveRsName( $pRsName ) {
        $this->m_props['slavers'] = $pRsName;
    }

    /**
     * Getter for the slave recordset name.
     * @return string the slave recordset name.
     */
    function getSlaveRsName() {
        return $this->m_props['slavers'];
    }

    /**
     * Getter for the array of slave blocks (blocks that are slaves to this one).
     * @return array the entire array of slave blocks.
     */
    function &getSlaveBlocks() {
        return $this->m_props['slaveblocks'];
    }

    /**
     * Setter for the slave block array.
     * @param array $pSlaves the new slave block array.
     */
    function setSlaveBlocks( $pSlaves ) {
        if ( ! is_array( $pSlaves ) ) {
            print 'FATAL: slave block array is not an array';
            die;
        }
        $this->m_props['slaveblocks'] = array();
        foreach( $pSlaves as $ps ) {
            $this->setSlaveBlock( $ps->getName(), $ps );
        }
    }

    /**
     * Getter for a single, named, slave block.
     * @return PGBDataBlock the requested slave block, or NULL, if the block doesn't exist.
     * @param string $pName the requested slave block name.
     */
    function &getSlaveBlock( $pName ) {
        if ( isset( $this->m_props['slaveblocks'][ strtolower( $pName ) ] ) ) {
            $sb   = &$this->m_props['slaveblocks'][ strtolower( $pName ) ];
            $w_rs = &$this->getRecordSet();
            $wr   = &$w_rs->getCurrentRecord();
            //print $this->getName() . ': ' . $sb->getName() . ': ' . $w_rs->getRecordPointer() . '<br>';
            //var_dump( $wr );
            if ( $wr ) {
                $srs = &$wr->getSlave( $sb->getSlaveRsName() );
                $sb->setRecordSet( $srs );
            }
            //$this->m_props['slaveblocks'][ strtolower( $pName ) ] = $sb;
            return $sb;
        }
        else {
            return NULL;
        }
    }

    /**
     * Setter for a new, named slave block.
     * @param string $pNome the new slave block name.
     * @param PGBDataBlock $pBloco the slave block object.
     */
    function setSlaveBlock( $pNome, $pBloco ) {
        if ( !is_a( $pBloco, 'PGBDataBlock' ) ) {
            print 'FATAL: slave block ' . $pNome . ' is not a PGBDataBlock';
            die;
        }
        $this->m_props['slaveblocks'][ strtolower($pNome) ] = $pBloco;
    }

    /**
     * Getter for the block name.
     * @return string the data block name.
     */
    function getName() {
    // retorna o nome do data block
        return $this->m_props['nome'];
    }

    /**
     * Setter for the data block name.
     * @param string $pNome the data block name.
     */
    function setName( $pNome ) {
    // seta o nome do data block
        $this->m_props['nome']   = strtolower( $pNome );
    }

    /**
     * Getter for the block's underlying recordset.
     * @return PGBRecordSet the underlying recordset.
     */
    function &getRecordSet() {
    // retorna o recordset
        return $this->m_props['rs'];
    }

    /**
     * Setter for the block's underlying recordset.
     * @param PGBRecordSet &$pRS the recordset to attach to the block.
     */
    function setRecordSet( &$pRS ) {
    // seta o recordset do bloco, se o tipo do objeto estiver correto
        if ( is_a( $pRS, 'PGBRecordSet' ) ) {
            $this->m_props['rs'] = &$pRS;
        }
        else {
            print 'FATAL: ' . $this->getName() . ' recordset do bloco nao e record set';
           // die;
        }
    }

    /**
     * Getter for the block's rendering definitions.
     * @return array the set of rendering definitions associated to the block.
     */
    function &getRenderingDefinitions() {
    // retorna todas as definicoes de renderizacao
        return $this->m_props['renderdefs'];
    }

    /**
     * Setter for the entire set of block rendering definitions.
     * @param array $pDefs the new set of rendering definitions.
     */
    function setRenderingDefinitions( $pDefs ) {
    // atualiza todas as definicoes de renderizacao
        if ( is_array( $pDefs ) ) {
            while( list($k,$c) = each($pDefs) ) {
                if ( !is_a( $c, 'PGBRenderDefs' ) ) {
                    print 'FATAL: definicoes nao estao em um conjunto de definicoes de renderizacao';
                    die;
                }
            }
            $this->m_props['renderdefs'] = $pDefs;
        }
    }

    /**
     * Getter for a specific, named, rendering definition.
     * @return PGBRenderDefs the requested definition (or NULL, if it's not defined)
     * @param string $pnome the rendering definition name.
     */
    function &getRenderingDefinition( $pnome ) {
    // retorna UMA definicao de renderizacao, dado o nome da coluna
        if ( isset( $this->m_props['renderdefs'][$pnome] ) ) {
            return $this->m_props['renderdefs'][$pnome];
        }
        else {
            return NULL;
        }
    }

    /**
     * Setter for a specific, named, rendering definition.
     * 
     * If there is no rendering definition bearing the given name, one will be created.
     * @param string $pnome the definition name.
     * @param PGBRenderDefs $prd the rendering definition.
     */
    function setRenderingDefinition( $pnome, $prd ) {
    // seta UMA definicao de renderizacao
        if ( is_a( $prd, 'PGBRenderDefs' ) ) {
            $this->m_props['renderdefs'][ $pnome ] = $prd;
        }
    }

    /**
     * Getter for the page size (the number of records to be displayed)
     * @return integer the page size.
     */
    function getPageSize() {
    // retorna o tamanho da pagina logica de exibicao, em registros/ pagina
        return $this->m_props['pagina'];
    }

    /**
     * Setter for the page size.
     * @param integer $p_maxrec the new page size.
     */
    function setPageSize( $p_maxrec ) {
    // seta o tamanho da pagina logica de exibicao
        if ( is_integer( $p_maxrec ) ) {
            $this->m_props['pagina'] = $p_maxrec;
        }
    }
    
    /**
     * Should a selector pin be rendered for each record?
     * 
     * A selector pin allows a record to be selected, that is, to be made the current
     * record. It's rendered anyway if the block has slave blocks, once a record's slave
     * records can only be reached for the block's current record (even the tree renderer
     * will only render updateable slaves for a record that is current).
     * @return boolean flag for selector pin. 
     */
    function hasSelectorPin() {
    // retorna flag de presenca ou nao do pino seletor
        if ( $this->canDelete() or count($this->m_props['slaveblocks']) > 0 ) {
        // blocos com capacidade de exclusao ou que tem escravos SEMPRE tem pino seletor...
            $this->setSeletor( true );
        }
        return $this->m_props['seletor'];
    }

    /**
     * Setter for the selector pin flag.
     * @param boolean $pSeletor the new selector pin flag.
     */
    function setSeletor( $pSeletor ) {
    // seta indicador de presenca ou nao do pino seletor
        $this->m_props['seletor'] = $pSeletor;
    }

    /**
     * Renders the error messages associated to a given record position.
     * 
     * Record position -1 means "the block as a whole"; these messages are rendered on
     * the top of the block.
     * @param integer $pRn the record position.
     */
    function renderErrors( $pRn ) {
    // renderiza tabela de erros
        //print '<pre> recuperando erro ' . $pRn . '</pre>';
        if ( count( $this->m_erros ) <= 0 ) {
            //print '<pre> sem erros definidos </pre>';
            return NULL;
        }
        elseif ( count( $this->m_erros[$pRn] ) <= 0 ) {
            //print '<pre> sem erros definidos na posicao</pre>';
            return NULL;
        }
        $r = '<table width="100%">';
        while( list( $k, $m ) = each( $this->m_erros[$pRn] ) ) {
            $r .= '<tr class="erro"><td>';
            $r .= $m;
            $r .= '</td></tr>';
        }
        $r .= '</table>';
        //print $r;
        $this->m_erros[$pRn] = array();
        return $r;
    }
    
    /**
     * Clears the entire error message array.
     */
    function clearErrorArray() {
        //print '<pre>reset errors</pre>';
        $this->m_erros = array();
    }

    /**
     * Renders a toolbar on the top of the block's visual area.
     * 
     * A toolbar can be "flat" or "full" (the default). A flat toolbar doesn't have support
     * for recording the current record position, nor it has navigational controls.
     * @param boolean $pFlat should the toolbar be flat?
     */
    function renderToolbar( $pFlat = false ) {
    // renderiza uma barra de navegacao, dependendo do caso
        $p_recordset      = &$this->getRecordSet();
        $p_maxrec         = $this->getPageSize();
        $m_recordpointer  = $p_recordset->getRecordPointer();
        $m_recordcount    = $p_recordset->rowCount();
        $x = split( '/', $_SERVER['REQUEST_URI'] );
        $w_prefix         = '/' . $x[1] . '/images';
        print '<table width="100%"><tr>';
        print '<td>';
        $n                = $this->getName();
        if ( !$pFlat ) {
            print '<input type="hidden" name="__' . $n . '_p" value="-1">';
        }
        $ma = array(
                'exeqry' => array( 'pt' => 'executa a pesquisa',
                                  'en' => 'execute query'
                                ),
                'entqry' => array( 'pt' => 'registra critério de busca',
                                  'en' => 'enter query criteria'
                                ),
                'down' => array( 'pt' => 'próxima página',
                                'en' => 'next page'
                              ),
                'up'   => array( 'pt' => 'página anterior',
                                'en' => 'previous page'
                              ),
                'last' => array( 'pt' => 'última página',
                                'en' => 'last page'
                              ),
                'first' => array( 'pt' => 'primeira página',
                                'en' => 'first page'
                              ),
                'delrec' => array( 'pt' => 'exclui o registro corrente',
                                  'en' => 'delete current record'
                                ),
                'crerec' => array( 'pt' => 'cria um novo registro',
                                  'en' => 'add new record'
                                ),
                'commit' => array( 'pt' => 'salva tudo',
                                  'en' => 'commit all changes to database'
                                ),
                'de' => array( 'pt' => ' de ',
                                'en' => ' of '
                            )
                );
        $oIN = $this->getNlsAgent();
        $oIN->setMessageArray( $ma );
        if ( $this->isInQueryMode() ) {
            print '<input type="image" id="__' . $n . '_exeqry" name="__' . $n . '_exeqry" src="' . $w_prefix . '/exeqry.gif" border=0 title="' . $oIN->getMessage('exeqry') . '" value="1">';
        }
        else {
            if ( $p_maxrec > 0 and !$this->isInQueryMode() and !$pFlat ) {
                print '<input type="image" src="' . $w_prefix . '/aflup.gif" border=0 title="' . $oIN->getMessage('first') . '" onClick="javascript: document.__prg_form.__' . $n . '_p.value=0">';
                print '<input type="image" src="' . $w_prefix . '/aflleft.gif" border=0 title="' . $oIN->getMessage('up') . '" onClick="javascript: document.__prg_form.__' . $n . '_p.value=' . max($m_recordpointer - $p_maxrec,0) . '">';
                print '<input type="image" src="' . $w_prefix . '/aflright.gif" border=0 title="' . $oIN->getMessage('down') . '" onClick="javascript: document.__prg_form.__' . $n . '_p.value=' . max($m_recordpointer + $p_maxrec,0) . '">';
                print '<input type="image" src="' . $w_prefix . '/afldown.gif" border=0 title="' . $oIN->getMessage('last') . '" onClick="javascript: document.__prg_form.__' . $n . '_p.value=' . max($m_recordcount - ($m_recordcount-(floor($m_recordcount/$p_maxrec)*$p_maxrec)),0) . '">';
            }
            // so renderiza botoes de interacao com o BD se o recordset tiver vindo do DBX
            if ( $this->canDelete() and !$pFlat ) {
                print '<input type="image" id="__' . $n . '_delrec" name="__' . $n . '_delrec" src="' . $w_prefix . '/afflddel.gif" border=0 title="' . $oIN->getMessage('delrec') . '" value="1" onClick="javascript: delecao();">';
            }
            if ( $this->canInsert() and !$pFlat and ( !$this->isSingleRecord() or $m_recordcount == 0) ) {
                print '<input type="image" id="__' . $n . '_crerec" name="__' . $n . '_crerec" src="' . $w_prefix . '/affldnew.gif" border=0 title="' . $oIN->getMessage('crerec') . '" value="1">';
            }
            if ( !$pFlat and ($this->canInsert() or $this->canUpdate() or $this->canDelete()) ) {
                print '<input type="image" id="__commit" name="__commit" src="' . $w_prefix . '/affldsav.gif" border=0 title="' . $oIN->getMessage('commit') . '" value="1">';
            }
            if ( is_a( $p_recordset, 'PGBDbxRecordSet' ) and $this->canQuery() and !$pFlat ) {
                print '<input type="image" id="__' . $n . '_entqry" name="__' . $n . '_entqry" src="' . $w_prefix . '/entqry.gif" border=0 title="' . $oIN->getMessage('entqry') . '" value="1">';
            }
        }
        print '</td>';
        print '<td align="right" valign="top">&nbsp;<small>';
        if ( $p_maxrec > 0 and !$this->isInQueryMode() and !$pFlat ) {
            print '<select name="__' . $n . '_px" size="1" onChange="javascript: document.__prg_form.__' . $n . '_p.value=-1000; document.__prg_form.submit();">';
            for( $i=1; $i <= $m_recordcount; $i += $p_maxrec ) {
                print '<option value="' . ( $i - 1 ) .'" ';
                $if = min($i+$p_maxrec-1,$m_recordcount);
                if ( $m_recordpointer >= ($i-1) and $m_recordpointer <= ($if-1) ) {
                    print ' selected ';
                } 
                print '>';
                print $i . '-' . $if . $oIN->getMessage('de') . $m_recordcount;
                print '</option>';
            }
            print '</select>&nbsp;';
            print '[ ' . ($m_recordpointer+1) . ' ]';
        }
        else {
            print '<input type="text" name="dummy" disabled size="10" value="' . ($m_recordpointer+1) . ' / ' . $m_recordcount . '">';
        }
        print '</small></td></tr></table>';
    }

    /**
     * Stores the recordset's record pointer in the block x recordset position map.
     * 
     * A null parameter means (store the CURRENT record pointer).
     * @param integer $pPos the recordset's record pointer to store.
     */
    function recordPosition( $pPos = NULL ) {
        $w_pk   = $pPos;
        if ( !$w_pk ) {
            $w_rs   = &$this->getRecordSet();
            $w_pk   = $w_rs->getRecordPointer();
        }
        $this->m_pos[] = $w_pk;
    }

    /**
     * Renders a form like screen, that shows one record at a time.
     * 
     * A form like screen show the fields one below the other, captions to the left of
     * content. An optional title can be given. If it's given, it'll override the block
     * title.
     * @param string $pTitulo the block title.
     */
    function renderForm( $pTitulo = NULL ) {
    // renderiza um recordset inteiro no formato de um form simples (um registro por vez)
        $this->setSeletor( true ); // fingimos que o pino esta exibido...
        $p_rowdef         = &$this->getRenderingDefinitions();
        $p_maxrec         = $this->getPageSize();
        $w_titulo         = $pTitulo;
        if ( is_null( $pTitulo ) ) {
        	$w_titulo = $this->getTitle();
        }
        $this->setPageSize( 1 );
        $o_rend           = new PGBRenderer( $this );
        $p_recordset      = &$this->getRecordSet();
        if ( ! $p_recordset ) {
            return;
        }
        $m_rowcount       = 0;
        $m_recordpointer  = $p_recordset->getRecordPointer();
        $rp               = $m_recordpointer;
        $m_recordcount    = $p_recordset->rowCount();
        $this->m_pos      = array();
        $this->m_fields   = array();
        $e = $this->renderErrors( -1 );
        if ( !is_null( $e ) ) {
            print $e;
        }
        $this->renderToolbar();
        print '<table width="100%" border="1">';
        print '<tr>';
        print '<th colspan="2">';
        print $w_titulo;
        print '</th>';
        print '</tr>';
        $e = $this->renderErrors( 0 );
        if ( !is_null( $e ) ) {
            print '<tr>';
            print '<td colspan="2">';
            print $e;
            print '</td>';
            print '</tr>';
        }
        reset( $p_rowdef );
        $this->clearErrorArray();
        $r = &$this->beautify( $p_recordset->getCurrentRecord() );
        if ( $r ) {
            $this->recordPosition();
            $o_rend->setIndex( count( $this->m_pos ) - 1 );
            // os cabecalhos e seus campos
            while( list( $k, $c ) = each( $p_rowdef ) ) {
                if ( $this->isInQueryMode() ) {
                    print '<tr class="query">';
                }
                elseif ( $p_recordset->isCurrentRecordDeleted() ) {
                    print '<tr class="erased">';
                }
                else {
                    print '<tr class="formH">';
                }
                $w_field            = $r->getField( $k );
                $w_sc               = $c->get('StyleClass');
                print '<td valign="top" class="dados">';
                print $c->get('Caption') . ':';
                print '</td>';
                print '<td valign="top" class="dados">';
                if ( $this->isInQueryMode() ) {
                    $o_rend->renderQueryField( $c->get('Renderer'), $w_field, $c->get('ExtraDefs') );
                }
                else {
                    $this->m_fields[ $w_field->getName() ][] = new PGBField($w_field->getName(), $w_field->getValue());
                    $o_rend->renderField( $c->get('Renderer'), $w_field, $c->get('ExtraDefs') );
                }
                print '</td>';
                print '</tr>';
            }
        }
        print '</table>';
        $this->setPageSize( $p_maxrec );
    }

    /**
     * Renders data in a tabular fashion. The number of records displayed and the aspect
     * are controlled by the block properties.
     * 
     * There are several subformats for tabular blocks, as implied by the parameters. A
     * block can be rendered:
     * - alone (i.e. containing only the block records) or nested (i.e. containing the slave 
     * records associated to each record, too);
     * - readonly (no record updating capabilities supported) or updateable;
     * - in <b>report mode</b> (no updating capabilities and no record selection support) or
     *  standard mode.
     * @param string $pTitulo the block title (if given, overrides standard title).
     * @param boolean $pTree should the block be rendered nested (tree like structure, containing record and its slaves)?
     * @param boolean $pReadOnly should the block be a readonly unit (no updateable fields)?
     * @param boolean $pReportMode should report mode be used?
     */
    function renderTabular( $pTitulo = NULL, $pTree = false, $pReadOnly = false, $pReportMode = false ) {
    // renderiza um recordset inteiro no formato de uma tabela, com opcao para paginar em
    //  grupos de registros (neste caso, uma barra de navegacao aparece)
        if ( $this->isInQueryMode() ) {
            $this->renderForm( $pTitulo );
            return;
        }
        $p_recordset      = &$this->getRecordSet();
        if ( ! $p_recordset ) {
            return;
        }
        $w_titulo         = $pTitulo;
        if ( is_null( $pTitulo ) ) {
            $w_titulo = $this->getTitle();
        }
        $x = split( '/', $_SERVER['REQUEST_URI'] );
        $w_prefix         = '/' . $x[1] . '/images';
        $p_rowdef         = &$this->getRenderingDefinitions();
        $p_maxrec         = $this->getPageSize();
        if ( $pReadOnly or $pReportMode ) {
        // se o bloco estiver sendo renderizado read only, nao ha paginacao
            $p_maxrec = 0;
        }
        else {
            $this->m_fields   = array();
            $this->m_pos      = array();
        }
        $o_rend           = new PGBRenderer( $this );
        $m_rowcount       = 0;
        $m_recordpointer  = $p_recordset->getRecordPointer();
        $rp               = $m_recordpointer;
        $m_recordcount    = $p_recordset->rowCount();
        // monta uma estrutura auxiliar para renderizar tabelas multilinha, a menos que
        // ela ja esteja montada
        if ( count( $this->m_rendsl ) <= 0 ) {
            $a_rends = array();
            reset( $p_rowdef );
            while( list( $k, $c ) = each( $p_rowdef ) ) {
                $w_li = $c->get('Row');
                if ( !$w_li ) {
                    $w_li = 1;
                }
                if ( ! is_array( $a_rends[ $w_li ] ) ) {
                    $a_rends[ $w_li ] = array();
                }
                $a_rends[ $w_li ][$k] = $c;
            }
            $this->m_rendsl = $a_rends;
        }
        $cs = 0;
        reset( $this->m_rendsl );
        while( list( $k, $tl ) = each( $this->m_rendsl ) ) {
            $cs = max( $cs, count( $tl ) );
        }
        // continua
        if ( !$pReadOnly ) {
            $e = $this->renderErrors( -1 );
            if ( !is_null( $e ) ) {
                print $e;
            }
        }
        $this->renderToolbar( $pReadOnly or $pReportMode );
        print '<table width="100%" border="1">';
        reset( $p_rowdef );
        // os cabecalhos
        if ( $this->hasSelectorPin() ) {
            $cs++;
        }
        if ( $w_titulo and !$pReportMode ) {
            print '<tr>';
            print '<th colspan="' . ($cs+1) . '">';
            print $w_titulo;
            print '</th>';
            print '</tr>';
        }
        print '<tr>';
        // temos seletor de registros ? (pino seletor)
        if ( $this->hasSelectorPin() ) {
            print '<th>&nbsp;';
            print '</th>';
        }
        // renderiza cabecalhos
        reset( $this->m_rendsl );
        $tl = $this->m_rendsl[ 1 ];
        reset( $tl );
        $w_lspan    = $cs - count( $tl ) + 1;
        $w_cont     = 0;
        while( list( $k2, $c ) = each( $tl ) ) {
            $w_cont++;
            if ( $w_cont == count( $tl ) ) {
                print '<th colspan="' . $w_lspan . '">';
            }
            else {
                print '<th>';
            }
            print $c->get('Caption');
            print '</th>';
        }
        print '</tr>';
        // o conteudo
        $w_posrec = 0;
        if ( $p_maxrec <= 0 ) {
            //print '<pre>reposicionando no 1o registro</pre>';
            $p_recordset->firstRecord();
            $m_recordpointer = $p_recordset->getRecordPointer();
        }
        else {
            $w_posrec = ($p_maxrec * floor( $p_recordset->getRecordPointer() / $p_maxrec ));
        }
        while( ($r = &$this->beautify( $p_recordset->getRecord($w_posrec) ) ) and ($m_rowcount < $p_maxrec or $p_maxrec <= 0 ) ) {
            $pCurrent = ( $w_posrec == $rp );
            //var_dump($r);
            $m_rowcount++;
            if ( $pReportMode ) {
                print '<tr class="branco">';
            }
            elseif ( $pCurrent ) {
                print '<tr class="current">';
            }
            elseif ( $p_recordset->isDeleted( $w_posrec ) ) {
                print '<tr class="erased">';
            }
            elseif ( $m_rowcount % 2 ) {
                print '<tr class="odd">';
            }
            else {
                print '<tr class="even">';
            }
            $m_cellcount = 0;
            if ( !isset( $this->m_deta[ $w_posrec ] ) ) {
                $this->m_deta[ $w_posrec ] = 0;
            }
            if ( $this->hasSelectorPin() ) {
                $ma = array(
                        'pin' => array( 'pt' => 'seleciona este registro',
                                        'en' => 'make this record current'
                                        ),
                        'mais' => array( 'pt' => 'exibe/ oculta detalhes adicionais',
                                         'en' => 'show/ hide additional details'
                                       )
                        );
                $oIN = $this->getNlsAgent();
                $oIN->setMessageArray( $ma );
                print '<td valign="top" class="dados">';
                if ( !$pReadOnly and !$pReportMode ) {
                    print '<input type="image" src="' . $w_prefix . '/aspin.gif" border=0 title="' . $oIN->getMessage('pin') . '" onClick="javascript: document.__prg_form.__' . $this->getName() . '_p.value=' . $w_posrec . '">';
                }
                print '<input type="hidden" id="__' . $this->getName() . '_lgdeta_' . $w_posrec . '" name="__' . $this->getName() . '_lgdeta_' . $w_posrec . '" value="' . $this->m_deta[ $w_posrec ] . '">';
                if ( count( $this->m_rendsl ) > 1 or ($pTree and count( $this->getSlaveBlocks() ) > 0 ) ) {
                // ha mais de uma linha por linha detalhe; precisamos ver se hah dados nestas linhas adicionais
                // ou nao, renderizando o simbolo apropriado
                    $this->m_abspos++;
                    reset( $this->m_rendsl );
                    $w_cont = 0;
                    while( list( $k2, $tl ) = each( $this->m_rendsl ) ) {
                        reset( $tl );
                        if ( $k2 > 1 ) {
                            while( list( $k, $c ) = each( $tl ) ) {
                                $w_field            = $r->getField( $k );
                                if ( is_a( $w_field, 'PGBField' ) ) {
                                    if ( !is_null( $w_field->getValue() ) and $w_field->getValue() <> '' ) {
                                        $w_cont++;
                                    } 
                                }
                            }
                        }
                    }
                    if ( $w_cont == 0 and !$pTree ) {
                    // nenhum dado esta preenchido
                        print '<img src="' . $w_prefix . '/mais_vazio.gif" border=0 title="' . $oIN->getMessage('mais') . '" onClick="javascript: toggleDisplay( \'' . $this->getName() . '\', ' . $this->m_abspos  . ', ' . $w_posrec . ');">';
                    }
                    else {
                    // ha dados preenchidos
                        print '<img src="' . $w_prefix . '/mais.gif" border=0 title="' . $oIN->getMessage('mais') . '" onClick="javascript: toggleDisplay( \'' . $this->getName() . '\', ' . $this->m_abspos  . ', ' . $w_posrec . ');">';
                    }
                }
                print '</td>';
            }
            if ( !$pReadOnly and !$pReportMode ) {
                $this->recordPosition( $w_posrec );
            }
            $o_rend->setIndex( count( $this->m_pos ) - 1 );
            if ( $p_recordset->isDeleted( $w_posrec ) ) {
                $o_rend->renderField( 'hiddentext', new PGBField( '__lg_deleted', 'X' ) );
            }
            reset( $this->m_rendsl );
            $w_k2save = -1;
            while( list( $k2, $tl ) = each( $this->m_rendsl ) ) {
                $w_lspan = $cs - count( $tl ) + 1;
                $w_k2save= $k2 - 2;
                if ( $k2 > 1 ) {
                // se nao estamos na primeira linha, devemos renderizar cabecalhos para linhas de
                // continuacao e marca de divisão...
                    print '</tr>';
                    $w_lgsec = '';
                    if ( $this->m_deta[ $w_posrec ] == 1 ) {
                        print '<tr id="__' . $this->getName() . '_dmt_' . $this->m_abspos . '" >';
                    }
                    else {
                        print '<tr id="__' . $this->getName() . '_dmt_' . $this->m_abspos . '" style="display: none">';
                        $w_lgsec = 'sec';
                    }
                    if ( $this->hasSelectorPin() ) {
                        print '<td valign="top" class="dados">&nbsp;</td>';
                    }
                    reset( $tl );
                    $w_cont = 0;
                    while( list( $k, $c ) = each( $tl ) ) {
                        $w_cont++;
                        if ( $w_cont == count( $tl ) ) {
                            print '<th class="subc" colspan="' . $w_lspan . '">';
                        }
                        else {
                            print '<th class="subc">';
                        }
                        print $c->get('Caption');
                        print '</th>';
                    }
                    print '</tr>';
                    if ( $pReportMode ) {
                        print '<tr id="__' . $this->getName() . '_dm_' . ($w_k2save) . '_' . $this->m_abspos . '" class="branco' . $w_lgsec . '">';
                    }
                    elseif ( $pCurrent ) {
                        print '<tr id="__' . $this->getName() . '_dm_' . ($w_k2save) . '_' . $this->m_abspos . '" class="current' . $w_lgsec . '">';
                    }
                    elseif ( $p_recordset->isDeleted( $w_posrec ) ) {
                        print '<tr id="__' . $this->getName() . '_dm_' . ($w_k2save) . '_' . $this->m_abspos . '" class="erased' . $w_lgsec . '">';
                    }
                    elseif ( $m_rowcount % 2 ) {
                        print '<tr id="__' . $this->getName() . '_dm_' . ($w_k2save) . '_' . $this->m_abspos . '" class="odd' . $w_lgsec . '">';
                    }
                    else {
                        print '<tr id="__' . $this->getName() . '_dm_' . ($w_k2save) . '_' . $this->m_abspos . '" class="even' . $w_lgsec . '">';
                    }
                    if ( $this->hasSelectorPin() ) {
                        print '<td valign="top" class="dados">&nbsp;</td>';
                    }
                }
                reset( $tl );
                $w_cont = 0;
                while( list( $k, $f ) = each( $tl ) ) {
                    $w_cont++;
                    //print '<h2>' . $k . ' / ' . $f . '</h2>';
                    $w_field            = $r->getField( $k );
                    if ( is_a( $w_field, 'PGBField' ) ) {
                        $w_sc               = $f->get('StyleClass');
                        if ( is_null( $w_sc ) ) {
                            print '<td valign="top" class="dados"';
                        }
                        else {
                            print '<td valign="top" class="' . $w_sc . '"';
                        }
                        if ( $w_cont == count( $tl ) ) {
                            print ' colspan="' . $w_lspan . '"';
                        }
                        print '>';
                        if ( !$pReadOnly and !$pReportMode ) {
                            $this->m_fields[ $w_field->getName() ][] = new PGBField($w_field->getName(), $w_field->getValue());
                            $o_rend->renderField( $f->get('Renderer'), $w_field, $f->get('ExtraDefs') );
                        }
                        else {
                            // se estamos renderizando em arvore e este NAO E o registro corrente, nao podemos permitir
                            // edicao - neste caso, renderizamos tudo como plaintext
                            $o_rend->renderField( 'plaintext', $w_field, $f->get('ExtraDefs') );
                        }
                        print '&nbsp;</td>';
                    }
                    $m_cellcount++;
                }
            }
            print '</tr>';
            if ( !$pReadOnly ) {
                $e = $this->renderErrors( $m_rowcount - 1 );
                if ( !is_null( $e ) ) {
                    print '<tr>';
                    print '<td colspan="' . ($cs+1) . '">';
                    print $e;
                    print '</td>';
                    print '</tr>';
                }
            }
            if ( $pTree ) {
            // se se pediu renderizacao em ARVORE, renderiza os blocos escravos dentro de uma linha adicional da
            // tabela de renderizacao deste bloco
                foreach( $this->m_props['slaveblocks'] as $sbi ) {
                    //print '<pre>' . $sb->getName() . '</pre>';
                    $p_recordset->setRecordPointer( $w_posrec );
                    $sb = &$this->getSlaveBlock( $sbi->getName() );
                    $w_k2save++;
                    if ( $this->m_deta[ $w_posrec ] == 1 ) {
                        print '<tr id="__' . $this->getName() . '_dm_' . ($w_k2save) . '_' . $this->m_abspos . '" class="branco" >';
                        $sb->openAll( $pReportMode );
                    }
                    else {
                        print '<tr id="__' . $this->getName() . '_dm_' . ($w_k2save) . '_' . $this->m_abspos . '" class="brancosec" >';
                    }
                    print '<td colspan="' . ($cs+1) . '" align="center">';
                    $sb->renderTree( !$pCurrent or $pReadOnly, $pReportMode );
                    print '&nbsp;</td>';
                    print '</tr>';
                }
            }
            $w_posrec++;
        }
        print '</table>';
        if ( !$pReadOnly ) {
            $this->clearErrorArray();
        }
        $p_recordset->setRecordPointer( $rp );
    }
    
    /**
     * Renders data in a nested tabular fashion.
     * 
     * In a nested tabular rendering environment, the user can control which records he/ she
     * wants to see through special buttons, rendered next to each record. They allow them
     * to show or hide the slave records, thus "opening" or "closing" a tree of records.
     * This is a convenience alias for {@link renderTabular}.
     * @param boolean $pReadOnly should the block be a readonly unit (no updateable fields)?
     * @param boolean $pReportMode should report mode be used?
     */
    function renderTree( $pReadOnly = false, $pReportMode = false ) {
        $this->renderTabular( NULL, true, $pReadOnly, $pReportMode );
    }
    
    /**
     * Renders data in a nested tabular fashion using report mode.
     * 
     * This is a convenience alias for {@link renderTree}.
     */
    function renderTreeReport() {
        $this->openAll( true );
        $this->renderTree( true, true );
    }
    
    /**
     * Marks all nodes in a tree like tabular structure as "opened".
     * 
     * This will show all records and, within them, all their slave records. If
     * report mode is stated, all records will be opened regardless of previous
     * user decisions; if not, only the records not explicitly acted upon will be
     * marked as opened.
     * @param boolean $pReportMode the report mode flag.
     */
    function openAll( $pReportMode = false ) {
        $p_recordset      = &$this->getRecordSet();
        for( $i=0; $i <= $p_recordset->rowCount(); $i++ ) {
            if ( !isset( $this->m_deta[ $i ] ) or $pReportMode ) {
            // so mudamos o que nao estiver explicitamente setado, para nao violar as escolhas
            // que o usuario fez depois de abrir a tela pela primeira vez
                $this->m_deta[ $i ] = 1;
            }
        }
    }
    
    /**
     * PIRAGIBE will execute this code before anything else when "posting". 
     * 
     * This should be implemented by each heir. It's originally empty (i.e. will do nothing).
     * It's worth saying that the method will be called <b>BEFORE THE DATA CAPTURING PHASE</b>;
     * so, no new data is available yet.
     */
    function prePost() {
    }
    
    /**
     * Captures and processes user input for a block, including recordset updates.
     * 
     * This should be called from pages rendering the block. Calling post for a block
     * implicitly calls post for each slave block attached to the block, and so on (i.e.
     * if a slave block has slaves of its own, post will be called for them, too), in the
     * proper order. POST processing is divided in several steps, which are:
     * 1. <b>Pre Posting</b> -> an optional step, that each heir should implement.
     * 2. <b>Data Collecting</b> -> the HTML form fields are captured and translated to 
     * their internal representation.
     * 3. <b>Validation</b> -> data is validated, at field, record and block levels.
     * 4. <b>Recordset Update</b> -> valid data are applied to the underlying recordsets.
     * 5. <b>Repositioning</b> -> positional commands are executed, and a new record positioning
     * context is obtained.
     * 
     * This is by far the most complex, error prone and sensitive method of the entire 
     * framework. So, be very careful if you choose to override it! 
     */
    function post() {
    // aplica mudancas feitas pelo usuario
        $this->prePost();
        //print '<pre>' . $this->getName(); print '</pre>';
        $n          = $this->getName();
        $rs         = &$this->getRecordSet();
        if ( !is_a( $rs, 'PGBRecordSet' ) ) {
            return;
        }
        $rpoi       = $rs->getRecordPointer();
        $p_rowdef   = $this->getRenderingDefinitions();
        $this->clearErrorArray();
        $o_rend     = new PGBRenderer( $this );
        // salva o array de exibicao de detalhes adicionais em blocos multilinha
        reset( $this->m_pos );
        while( list( $bpos, $rpos ) = each( $this->m_pos ) ) {
            if ( isset( $_POST[ '__' . $n . '_lgdeta_' . $rpos ] ) ) {
                $this->m_deta[ $rpos ] = $_POST[ '__' . $n . '_lgdeta_' . $rpos ];
            }
        }
        // salva, no array de campos do bloco, tudo o que foi digitado
        if ( (!$this->isInQueryMode()) and $_SERVER[ 'REQUEST_METHOD' ] == 'POST' ) {
            reset( $p_rowdef );
            //print '<pre> colhendo post de ' . $this->getName() . '</pre>';
            while( list( $fn, $rd ) = each( $p_rowdef ) ) {
                //print '<pre> colheita ' . $this->getName() . ': ' . $fn . '</pre>';
                if ( is_array( $this->m_fields[ $fn ] ) and $rd->get('Renderer') != 'plaintext' ) {
                    //print '<pre> colheita II ' . $this->getName() . ': ' . $fn . '</pre>';
                    reset( $this->m_fields[ $fn ] );
                    while( list( $i, $f ) = each( $this->m_fields[$fn] ) ) {
                        $fv     = $f->getValue();
                        //print '<pre> colheita III ' . $this->getName() . ': ' . $fn . '[' . $i . '] = ' . $fv . '</pre>';
                        $fvn    = NULL;
                        if ( isset( $_POST[ '__' . $n . '_' . $fn ][ $i ] ) ) {
                            $fvn    = $_POST[ '__' . $n . '_' . $fn ][ $i ];
                            // se houver conversor definido, aplica a conversao
                            $edefs  = $rd->get( 'ExtraDefs' );
                            if ( $edefs ) {
                                $fvn = $o_rend->unformat( $fvn, $edefs );
                            }
                        }
                        // seta o novo valor, se o registro nao estiver excluido e se o campo nao
                        // for "read only"
                        $w_ed = $rd->get('ExtraDefs');
                        $w_ro = false;
                        if ( is_a( $w_ed, 'PGBPropertyBag' ) ) {
                            $w_ro = $w_ed->get('ReadOnly');
                            if ( ! ( $w_ro === true ) ) {
                                $w_ro = false;
                            }
                        }
                        if ( isset( $_POST[ '__' . $n . '_' . '__lg_deleted' ][ $i ] ) ) { 
                            //print '<pre>' . $fn . ': deleted</pre>';
                            $this->m_status = 2;
                        }
                        elseif ( ! $w_ro ) {
                            //print '<pre>' . $fn . ': ' . $fv . ' => ' . $fvn . '</pre>';
                            if ( $fv != $fvn ) {
                                $this->m_status = 2;
                            }
                            $f->setValue( $fvn );
                            $this->m_fields[ $fn ][ $i ] = $f;
                        }
                    }
                }
                else {
                    //print '<pre>'; var_dump( $this->m_fields ); //var_dump( $rd ) ; 
                    //print '</pre>';
                }
            }
            if ( count( $this->getErrors() ) == 0 and !isset( $_POST['__' . $n . '_delrec_y'] ) ) {
                //print '<h2>' . $n . ' validando block array</h2>';
                $this->validateBlockArray();
            }
        }
        //print '<h1>' . $n . ' processando</h1>';
        // o salvamento sempre e geral (todos os blocos salvam o que tem...)
        if ( count( $this->getErrors() ) == 0 ) {
            if ( is_a( $rs, 'PGBRecordSet' ) and ( $this->canInsert() or $this->canUpdate() or $this->canDelete() ) ) {
                //print '<pre>' . $this->getName() . ' atualizando o rs</pre>';
                $rs->updateFromDataBlock( $this );
            }
            //print '<pre>' . $this->getName() . ' verificando escravos</pre>';
            foreach( $this->m_props['slaveblocks'] as $sbi ) {
            //print '<pre>' . $sbi->getName() . '</pre>';
                $sb = &$this->getSlaveBlock( $sbi->getName() );
                $sb->post();
            }
        }
        $w_erros = $this->countErrors();
        //print '<pre> erros: ' . $n . ': ' . $w_erros . '</pre>';
        // se ha erros, paramos por aqui...
        if ( $w_erros > 0 ) {
            //print '<pre>'; var_dump( $this->m_erros ); print '</pre>';
            return;
        }
        // os controles posicionais sao por bloco
        //print '<h1>' . $n . ' analisando comandos</h1>';
        //print '<pre>'; var_dump( $_POST ); print '</pre>';
        if ( isset( $_POST['__' . $n . '_delrec_y'] ) ) {
            $this->m_status = 2;
            $rs->deleteRecord( $rpoi );
        }
        elseif ( isset( $_POST['__' . $n . '_crerec_y'] ) ) {
            $this->m_status = 2;
            $rs->insertBlank();
            $rs->lastRecord();
        }
        elseif ( isset( $_POST['__' . $n . '_entqry_y'] ) ) {
            $this->m_status = 1;
            $this->m_queryMode = true;
        }
        elseif ( isset( $_POST['__' . $n . '_exeqry_y'] ) ) {
            $this->m_status = 1;
            $this->m_queryMode  = false;
            $p_rowdef           = &$this->getRenderingDefinitions();
            $filtro             = array();
            reset( $p_rowdef );
            while( list( $k, $f ) = each( $p_rowdef ) ) {
                $filtro[ $k ] = $_POST['__' . $n . '_' . $k][0];
            }
            $this->newError( -1, $rs->applyFilter( $filtro ) );
        }
        elseif ( isset( $_POST['__' . $n . '_p'] ) ) {
            $p  = $_POST[ '__' . $n . '_p' ];
            if ( $p >= 0 ) {
                //print '<pre>' . $n . ' I: posicionando em ' . $p . '</pre>';
                $rs->setRecordPointer( $p );
            }
            elseif ( isset( $_POST['__' . $n . '_px'] ) and $p == -1000 ) {
                $p  = $_POST[ '__' . $n . '_px' ];
                if ( $p >= 0 ) {
                    //print '<pre>' . $n . ' II: posicionando em ' . $p . '</pre>';
                    $rs->setRecordPointer( $p );
                }
            }
            else {
                //print '<pre>'; var_dump($_POST); print '</pre>';
            }
        }
        //print '<pre>' . $this->getName() . ' -> ' . $w_erros . '</pre>';
        if ( isset( $_POST['__commit_y'] ) and is_a($rs, 'PGBRecordSet') ) {
            //print '<pre>' . $this->getName() . ' commit</pre>';
            $mk = $rs->getMasterKeys();
            if ( count( $mk ) == 0 and is_null($this->getSlaveRsName()) and $w_erros == 0 ) {
            // apenas recordsets que NAO SAO escravos precisam ser postados -> a postagem de um mestre
            // posta todos os seus escravos, tornando desnecessario postar os escravos de per si.
                //print '<pre>' . $this->getName() . ' commit Ok</pre>';
                $rs->post();
                $rs->applyFilter( array() );
                //print '<pre>' . $n . ' COMMIT: posicionando em ' . $rpoi . '</pre>';
                $rs->setRecordPointer( $rpoi );
            }
            $this->m_status = 1;
        }
        //print '<h1>' . $this->getName() . ' terminando process</h1>';
    }
    
    /**
     * This is an alias for {@link post}, kept here for historical reasons.
     * @deprecated 1.00 - 01/07/2006
     */
    function process() {
        $this->post();
    }

}
?>
Return current item: Piragibe