<?php

/**
 * Goweb_Controller_Action_Admin
 *
 * @version    5.1.5
 * @author     I&D <dev@goweb.pt>, Pedro Pinto <ppinto@goweb.pt>
 * @copyright  2010 (c) Goweb (http://www.goweb.pt)
 */

/**
 * Goweb_Controller_Action
 */
Goweb::loadClass('Goweb_Controller_Action');

/**
 * Goweb_Paginator
 */
Goweb::loadClass('Goweb_Paginator');

/**
 * Goweb_Controller_Action_Admin
 * Classe com os metodos genericos do backoffice
 *
 * @version    5.0.10
 * @category   Goweb
 * @package    Goweb_Controller
 * @subpackage Action
 * @author     I&D <dev@goweb.pt>, Pedro Pinto <ppinto@goweb.pt>
 * @copyright  2010 (c) Goweb (http://www.goweb.pt)
 */
class Goweb_Controller_Action_Admin extends Goweb_Controller_Action {

    /**
     * @var string
     */
    protected $_controller = null;
    
    /**
     * @var string
     */
    protected $_title = null;
    
    /**
     * @var string
     */
    protected $_action = null;
    
    /**
     * @var string
     */
    protected $_section = null;
    
    /**
     * @var array
     */
    protected $_sections = array();    
    
    /**
     * @var string
     */
    protected $_links = null;
    
    /**
     * @var string
     */
    protected $_message = null;
    
    /**
     * @var string
     */
    protected $_error = null;
    
    /**
     * @var string
     */
    protected $_parentUrl = null;

    /**
     * @var Goweb_Session
     */
    protected $_session = null;

    /**
     * Modulos instalados
     *
     * @var array
     */
    protected $_installedModels = array();

    /**
     * Configuraca do paginador
     * 
     * @var array
     */
    protected $_paginatorParams = array('rows_per_page'  => 20,
                                        'links_per_page' => 10);
    
    /**
     * Funcoes publicas da administracao que nao necessita de login.
     *
     * *** Utilizar com cuidado! De preferencia nunca utilizar! ***
     *
     * @var array
     */
    protected $_publicFunctions = array(
                                        'newsletter' => 'outbox' // utilizado pelo cron job de envio da newsletter
                                        );
    
    /**
     * Object Goweb_Controller_Dispatcher_Token
     *
     * @var Goweb_Controller_Dispatcher_Token
    */
    protected $_token = null;
    
    /**
     * Constructor
     */
    public function init() {
        $this->_action = $this->_request->getParam($this->_request->getActionKey());
        $this->_controller = $this->_request->getParam($this->_request->getControllerKey());
        
        if (is_null($this->view->baseUrl)) $this->view->baseUrl = str_replace('/admin', '',  str_replace('/index.php', '', $this->_request->getBaseUrl()));
        
        $currentAction  = $this->_request->getActionName();
        $currentControl = $this->_request->getControllerName();
        $config = goweb::registry('config');
        goweb::loadClass('Zend_Auth_Storage_Session');
        $this->_auth->setStorage(new Zend_Auth_Storage_Session($config->session->name . '_Admin'));
        $this->_isAdmin = true;
        
        // vamos vericar se o controller action actual tem livre acesso
        if (isset($this->_publicFunctions) && is_array($this->_publicFunctions)){
            foreach ($this->_publicFunctions as $controller => $action){
                $this->_helper->acl->allow(null, $controller, $action);
            }
        }
        parent::init();
    }
    
    /**
     * Inicializa os links para as accoes que podem ser executadas.
    */
    protected function _initDefaultLinks() {

        $this->_links = array('list'       => '/' . $this->_controller . '/list/section/' . $this->_section . '/' . $this->_parentUrl,
                              'grid'       => '/' . $this->_controller . '/grid/section/' . $this->_section . '/' . $this->_parentUrl,
                              'dhtmlxData' => '/' . $this->_controller . '/dhtmlxData/section/' . $this->_section,
                              'dhtmlxGrid' => '/' . $this->_controller . '/dhtmlxGrid/section/' . $this->_section,
                              'treeDhtmlx' => '/' . $this->_controller . '/treeDhtmlx/section/' . $this->_section . '/' . $this->_parentUrl,
                              'add'        => '/' . $this->_controller . '/add/section/' . $this->_section . '/' . $this->_parentUrl,
                              'edit'       => '/' . $this->_controller . '/edit/section/' . $this->_section . '/' . $this->_parentUrl . 'id/',
                              'save'       => '/' . $this->_controller . '/save/section/' . $this->_section . '/' . $this->_parentUrl,
                              'delete'     => '/' . $this->_controller . '/delete/section/' . $this->_section . '/' . $this->_parentUrl . 'id/',
                              'deletelist' => '/' . $this->_controller . '/deletelist/section/' . $this->_section . '/' . $this->_parentUrl,
                              'excel'      => '/' . $this->_controller . '/list/section/' . $this->_section . '/export/1/' . $this->_parentUrl,
                              'xml'        => '/' . $this->_controller . '/xml/section/' . $this->_section . '/' . $this->_parentUrl);
        if ($this->_section == 'inscrito' && $this->_controller == 'newsletter'){
            $this->_links['import'] = '/newsletter/importInscritos';
        }
    }

    /**
     * Listagem dos registos.
     *
     * @param Goweb_Admin_Section
     * @param string
     * @param boolean
     *
     * @return string
     */
    protected function _listGeneric($section,
                                    $view = null,
                                    $cleanModelParts = true,
                                    $template = 'list.tpl',
                                    $parent = 'pid',
                                    $parent_id = 0) {
        
        $model = $section->getModel();
        /**
         * Acontece por vezes o model estar com o loadForeigns a false erradamente... 
         */
        $model->loadForeigns(true);
        if (is_null($parent_id)) $parent_id = 0;
        if (!$view) {
            Goweb::loadClass('Goweb_View');
            $view = new Goweb_View(GENERIC_VIEWS_PATH);
        }
        
        if (!$this->_links) {
            $this->_initDefaultLinks();
        }
        $order = $ordering = $sorting = null;
        $this->_getPermissions($section);
        if ($this->_request->ordering && $this->_request->sorting) {
            $ordering = $this->_request->ordering;
            $sorting = $this->_request->sorting;
            
            if (in_array($ordering, $model->getColsNames())) {
                if ($sorting == 'desc') {
                    $order = '-' . $ordering;
                    $model->order($order);
                } else {
                    $order = $ordering;
                    $model->order($order);
                }
            } else {
                $foreignCols = $model->getForeignColsWithAliases();
                if (array_key_exists($ordering, $foreignCols)) {
                    $foreignKey = $foreignCols[$ordering];
                    if ($sorting == 'desc') {
                        $order = '-' . $foreignKey;
                        $model->order($order);
                    } else {
                        $order = $foreignKey;
                        $model->order($order);
                    }
                }
            }
        } else {            
            $ordering = $section->getOrdering();
            if (count($ordering) > 0) {
                $order = $section->getOrdering();
                $model->order($order);                
                if (is_int(strpos($ordering[0], '-', 0))) {
                    $ordering = substr($ordering[0], 1);
                    $sorting  = 'desc';
                } else {
                    $ordering = $ordering[0];
                }                
            }
        }

        $where = $section->getWhere();
        if (count($where) > 0) {
            $model->where($where);                              
        }
        
        $view->ordering = $ordering;
        $view->sorting  = $sorting;
        
        $where = array();
        $orWhere = array();
        $pidCol = null;
        $pid = null;
        $tree = $section->getTree($cleanModelParts);

        // se pertencer a uma tree
        if ($tree && $section->getTreeParams()) {
            $treeParams = $section->getTreeParams();
            if ($treeParams) {
                $pid = $this->_getParam($treeParams['nodesParams']['urlId']);
                
                if (!$pid) {
                    $pid = '0';
                }
                if ($tree instanceof Goweb_Js_Dhtmlx_Tree && isset($this->_links['treeDhtmlx'])){
                    $tree->setLink($this->_links['treeDhtmlx']);
                    if ($pid > 0){
                        $treeModel = $treeParams['model'];
                        $treeModel->loadForeigns(false);
                        $cats = $treeModel->getPais($pid);
                        $cats = array_reverse($cats, true);
                        $treeModel->loadForeigns(true);
                        $tree->setOpenTo($cats);
                    } else {
                        $tree->setOpenTo($pid);
                    }
                } else {
                    $tree->setOpenTo($pid);
                }
                $pidCol = $treeParams['nodesParams']['urlId'] ? $treeParams['nodesParams']['urlId'] : $treeParams['nodesParams']['id'];
                
                
                if (in_array($pidCol, $model->getColsNames()) && $model->getCol($pidCol)->getType() == 'ForeignKeyMultiple'){
                    if (is_numeric($pid) && $pid > 0){
                        $alias = $model->getCol($pidCol)->getParam('alias');
                        $modelMultipleName = $model->getCol($pidCol)->getParam('modelMultiple');
                        $modelName = $model->getCol($pidCol)->getParam('model');
                        
                        $modelMultiple = goweb::getModelInstance($modelMultipleName, $model->getAdapter(), false);
                        $modelMultipleOrigin = goweb::getModelInstance($modelName, $model->getAdapter(), false);
                        
                        $foreignCols = Goweb_Db_Model::getColsWithPrefix($modelMultiple->getTableName(), $modelMultiple->getColsNames());
                        $whereMultiple = $model->getTableName() . '.' . $model->getPrimary() . '='. $modelMultiple->getTableName().'.'.$model->getCol($pidCol)->getParam('col');
                        $whereMultiple .= ' AND ' . $modelMultiple->getTableName().'.'.$pidCol.'='.$pid;
                        $model->joinInner($modelMultiple->getTableName(), $whereMultiple, $foreignCols);
                    }
                } else {
                    $where[] = array('where' => $model->getTableName() . '.' . $pidCol . '=?', 'bind' => $pid);
                }
            }
                
                
        } elseif (($parent_id >= 0 && in_array($parent, $model->getColsNames()))) {
            if (!($this->_request->search && strlen($this->_request->search) > 1)){
                $pidCol = $parent;
                $pid = $parent_id;
                $where[] = array('where' => $model->getTableName() . '.' . $pidCol . '=?', 'bind' => $pid);
            } else {
                if ($parent_id > 0){
                    $pidCol = $parent;
                    $pid = $parent_id;
                    $modelNameSearch = $model->getCol($pidCol)->getParam('model');
                    $modelSearch = goweb::getModelInstance($modelNameSearch, $model->getAdapter(), false);
                    $idsSearch = $modelSearch->getFilhosIds($pid);
                    $idsSearch[] = $pid;
                    if (count($idsSearch) > 0){
                        $where[] = array('where' => $model->getTableName() . '.' . $pidCol . ' IN (' . implode(', ', $idsSearch) . ')', 'bind' => '');
                    }
                }

            }
        }

        // search
        if ($this->_request->search && strlen($this->_request->search) > 1) {
            $searchCols = $section->getSearchCols();
            if (count($searchCols) > 0) {
                $where[] = array('where' => $model->getTableName() . '.' . $searchCols[0] . ' LIKE ?' , 'bind' => '%' . $this->_request->search . '%');
            }
            for ($i=1; $i<count($searchCols); $i++) {
                $orWhere[] = array('where' => $model->getTableName() . '.' . $searchCols[$i] . ' LIKE ?' , 'bind' => '%' . $this->_request->search . '%');
            }
        }
        
        // where
        if (count($where) > 0) {
            foreach ($where as $w) {
                $model->where($w['where'], $w['bind']);
            }
        }
        
        // orWhere
        if (count($orWhere) > 0) {
            foreach ($orWhere as $w) {
                $model->orWhere($w['where'], $w['bind']);
            }
        }

        foreach ($this->_request->getUserParams() as $param => $value){
            if ($param == 'lang') continue;
            if ($param != $pidCol && $param != $model->getPrimary() && (in_array($param, $model->getColsNames()) || 
                    (array_search($param, $model->getForeignColsWithAliases()) && 
                    in_array(array_search($param, $model->getForeignColsWithAliases()), $model->getColsNames()) ) ) 
                    && $model->colExists($param) && 
                        $this->_request->{$param}){
                if (strpos($param, $this->_links['list']) === false) $this->_links['list'] .= $param . '/'.$this->_request->{$param}. '/';
                if (strpos($param, $this->_links['add']) === false) $this->_links['add'] .= $param . '/'.$this->_request->{$param}. '/';
                if (strpos($param, $this->_links['delete']) === false) {
                    $this->_links['delete'] = str_replace('/'.$model->getPrimary(), '', $this->_links['delete']) . $param . '/'.$this->_request->{$param} . '/'.$model->getPrimary() . '/' ;
                }
                if ($model->getCol($param)->getType() == 'ForeignKeyMultiple'){
                    $alias = $model->getCol($param)->getParam('alias');
                    $modelMultipleName = $model->getCol($param)->getParam('modelMultiple');
                    $modelName = $model->getCol($param)->getParam('model');

                    $modelMultiple = goweb::getModelInstance($modelMultipleName, $model->getAdapter(), false);
                    $modelMultipleOrigin = goweb::getModelInstance($modelName, $model->getAdapter(), false);

                    $foreignCols = Goweb_Db_Model::getColsWithPrefix($modelMultiple->getTableName(), $modelMultiple->getColsNames());
                    $whereMultiple = $model->getTableName() . '.' . $model->getPrimary() . '='. $modelMultiple->getTableName().'.'.$model->getCol($param)->getParam('col');
                    $whereMultiple .= ' AND ' . $modelMultiple->getTableName().'.'.$param.'='.$this->_request->{$param};
                    $model->joinInner($modelMultiple->getTableName(), $whereMultiple, $foreignCols);
                } else {
                    $model->where($model->getTableName() . '.' . $param . '=?', $this->_request->{$param});
                }
            }
        }
        
        $paginatorQuery = '';
        if ($ordering) {
            $paginatorQuery .= 'ordering=' . $ordering . '&';
            if ($sorting) {
                $paginatorQuery .= 'sorting=' . $sorting . '&';
            }
        }
        
        $paginatorQuerySearch = $paginatorQuery . 'page=';        
        if ($this->_request->search) {
            $paginatorQuery .= 'search=' . $this->_request->search . '&';
        }       
       
        $paginatorQuery .= 'page=';
        $select = $model->getSelect();
                                            
        if ($this->_request->getParam('export')){
            $view->labels           = $section->getLabels();
            $view->cols             = $model->getCols();
            $view->model            = $model;
            $view->result           = $model->fetchAll();
            $viewRenderer = Zend_Controller_Action_HelperBroker::getExistingHelper('viewRenderer');
            $viewRenderer->setNoRender();
            return Goweb_Controller_Action::export($view);
        }
        $paginator = new Goweb_Paginator(Goweb::link($this->_links['list'] . ($pid >= 0 && strpos($this->_links['list'], $pidCol . '/' . $pid . '/') ? $pidCol . '/' . $pid . '/' : ''), $paginatorQuery),
                                         $model->count(),
                                         $this->_request->page,
                                         $this->_paginatorParams['rows_per_page'],
                                         $this->_paginatorParams['links_per_page']);

        $model->setSelect($select);
        $model->limitPage($paginator->getPage(), $paginator->getRowsPerPage());
        $view->paginator = $paginator;
        $view->paginator_output = $view->render('paginator.tpl');
        if (isset($this->_links['imagens'])){
            if (!$this->view->list_actions) $this->view->list_actions = array();
            
            $this->view->list_actions[] = array('link' => $this->_links['imagens'],
                                  'img' => '/admin/media/images/edit_image.gif',
                                  'alt' => translateAdmin('Gerir Imagens'));
            
            
        }
        if (isset($this->_links['ficheiros'])){
            if (!$this->view->list_actions) $this->view->list_actions = array();
            
            $this->view->list_actions[] = array('link' => $this->_links['ficheiros'],
                                  'img' => '/admin/media/images/edit_file.gif',
                                  'alt' => translateAdmin('Gerir Ficheiros'));
            
        }
        // search
        Goweb::loadClass('Form_Search', ADMIN_PATH . DIRECTORY_SEPARATOR . 'Index');
        $formSearch = new Form_Search('form_search', Goweb::link($this->_links['list'] . ($pid ? 'pid/' . $pid : ''), $paginatorQuerySearch . $paginator->getPage()));
        if ($this->_request->search) {
            $formSearch->search->value = $this->_request->search;         
        }
        $formSearch->render();        
        // ---
        
        $toString = $model->getToString();
        if (!$toString) {
            $toString = $model->getPrimary();

        }

        $section->checkLabels();
        if (is_null($view->action)) {
            $view->action = translateAdmin('Listagem');
        }

        if ($this->_title) {
            $view->title        = $this->_title;
        }
        $view->sections         = $this->_sections;
        $view->section_title    = $section->getTitle();
        $view->section_singular = $section->getTitleSingular();
        $view->labels           = $section->getLabels();
        $view->cols             = $section->getListCols();
        $view->modelCols        = $model->getCols();
        $view->to_string        = $toString;
        $view->primary          = $model->getPrimary();
        $view->rowset           = $model->fetchAll();
        $view->links            = $this->_links;
        $view->page             = $paginator->getPage();
        $view->form_search      = $formSearch;
        $view->pid              = $pid;
        $view->tree             = $tree;
        $view->error            = $this->_error;
        $view->message          = $this->_message;
        return $view->render($template);
    }
    
    /**
     * Metodo para a criacao da tree
     *
     * @param Goweb_Admin_Section
     * @param string
     *
     * @return string
    */
    protected function _treeGeneric($section, $view = null, $cleanModelParts = true, $template = 'tree.tpl') {
        $model = $section->getModel();
        
        if (!$view) {
            Goweb::loadClass('Goweb_View');
            $view = new Goweb_View(GENERIC_VIEWS_PATH);
        }
        
        $where = null;        
        $tree = $section->getTree($cleanModelParts);
        
        // se pertencer a uma tree
        if (!$tree) {
            throw new Goweb_Exception('Nenhum objecto "Tree" foi definido para a sec&ccedil;&atilde;a.');
        }               
        
        $treeParams = $section->getTreeParams();
        if ($treeParams) {
            $pidCol = $treeParams['nodesParams']['urlId'] ? $treeParams['nodesParams']['urlId'] : $treeParams['nodesParams']['id'];
            $pid = $this->_getParam($pidCol);
            if (!$pid) {
                $pid = '0';
            }
            if ($tree instanceof Goweb_Js_Dhtmlx_Tree && isset($this->_links['treeDhtmlx'])){
                $tree->setLink($this->_links['treeDhtmlx']);
                if ($pid > 0){
                    $treeModel = $treeParams['model'];
                    $treeModel->loadForeigns(false);
                    $cats = $treeModel->getPais($pid);
                    $cats = array_reverse($cats, true);
                    $treeModel->loadForeigns(true);
                    $tree->setOpenTo($cats);
                } else {
                    $tree->setOpenTo($pid);
                }
            } else {
                $tree->setOpenTo($pid);
            }
            
            $where = $model->getTableName() . '.' . $pidCol . '=?';
            $model->where($where, $pid);
        }

        if (is_null($view->action)) {
            $view->action = translateAdmin('Listagem');
        }
        if ($this->_title) {
            $view->title        = $this->_title;
        }
        $view->sections         = $this->_sections;
        $view->section_title    = $section->getTitle();
        $view->section_singular = $section->getTitleSingular();
        $view->tree             = $tree;
        $view->error            = $this->_error;
        $view->message          = $this->_message;
        
        return $view->render($template);
    }
    
    /**
     * Metodo para adicionar um novo registo
     *
     * @param Goweb_Admin_Section
     * @param string
     * @param string
     * @param integer
     * @param boolean
     * @param boolean
     *
     * @return string
    */
    protected function _addGeneric($section, $view = null, $parent = null, $parent_id = 0, $cleanValues = false, $useTree = true, $cleanModelParts = true, $template = null, $defaultElementsValues = array()) {

    	if (!$view) {
            Goweb::loadClass('Goweb_View');
            $view = new Goweb_View(GENERIC_VIEWS_PATH);
        }
        
        if (method_exists($section, 'getCanAdd'))
            $can_add = $section->getCanAdd();
        
        if (isset($can_add) && !$can_add){
            $this->_error = translateAdmin('N&atilde;o pode adicionar novos elementos em {{section}}!',  $section->getTitleSingular());
            return $this->_listGeneric($section, $view, true, 'list.tpl', $parent, $parent_id);
        }
            
        if (!$this->_links) {
            $this->_initDefaultLinks();
        }

        // usar tree?
        if ($useTree) {
            // se tiver tree
            $tree = $section->getTree($cleanModelParts);
            if ($tree) {
                if (!$parent_id) {
                    $parent_id = '0';
                }
                
                if ($tree instanceof Goweb_Js_Dhtmlx_Tree && isset($this->_links['treeDhtmlx'])){
                    $tree->setLink($this->_links['treeDhtmlx']);
                    if ($parent_id > 0){
                        $treeModel = $treeParams['model'];
                        $treeModel->loadForeigns(false);
                        $cats = $treeModel->getPais($parent_id);
                        $cats = array_reverse($cats, true);
                        $treeModel->loadForeigns(true);
                        $tree->setOpenTo($cats);
                    } else {
                        $tree->setOpenTo($parent_id);
                    }
                } else {
                    $tree->setOpenTo($parent_id);
                }
                $view->tree = $tree;
            }
        }
        
        Goweb::loadClass('Form_Generic', ADMIN_PATH . DIRECTORY_SEPARATOR . 'Index');
        
        $model = $section->getModel();
        
        /**
         * podem ser aplicados filtros na listagem, mas que não é suposto serem aplicados na edição ... 
         */
        $model->reset();
        foreach ($this->_request->getUserParams() as $param => $value){
            if ($param == 'lang') continue;
            if ($param != $parent && $param != $model->getPrimary() && (in_array($param, $model->getColsNames()) || 
                    (array_search($param, $model->getForeignColsWithAliases()) && 
                    in_array(array_search($param, $model->getForeignColsWithAliases()), $model->getColsNames()) ) ) 
                    && $model->colExists($param) && 
                        $this->_request->{$param}){
                            if (strpos($param, $this->_links['save']) === false) $this->_links['save'] .= $param . '/'.$this->_request->{$param}. '/';
                            if (!isset($defaultElementsValues[$param])) {
                                $defaultElementsValues[$param] = $this->_request->{$param};
                            }
                        }
        }
        
        if (!$view->form){
            $form = new Form_Generic($this->_section, Goweb::link($this->_links['save']));
            $form->setup($this->_section, $section->getLabels(), $model, $parent, $parent_id);
        } else {
            $form = $view->form;
        }
        
        if ($cleanValues) {
            $form->cleanValues();
            if ($parent) {
                if ($form->elementExists($parent)) $form->{$parent}->value = $this->_request->{$parent};
            }
        }

        foreach ($defaultElementsValues as $element => $value) {
            if ($form->elementExists($element)) $form->{$element}->value = $value;
        }
        
        $form->render();
        if ($view->ficha){
            $view->ficha->setName($form->getName());
        }
        
        $model = $section->getModel();
        $this->view->fieldsUpdate = array();
        foreach ($model->getCols() as $colName => $col){
            if ($col->getType() == 'ForeignKey' && $col->getParam('update')){
                if (is_array($col->getParam('update'))){
                    foreach ($col->getParam('update') as $column){
                        $this->view->fieldsUpdate[] = $column;
                    }
                } else {
                    $this->view->fieldsUpdate[] = $col->getParam('update');
                }
            }
        }

        if(is_null($template) && count($model->getTabCols()) > 0){
            $template = 'form_tab.tpl';
        } else if(is_null($template)) {
            $template = 'form.tpl';
        }

        $section->checkLabels();
        if (is_null($view->action)) {
            $view->action = translateAdmin('Inser&ccedil;&atilde;o');
        }
        if ($this->_title) {
            $view->title        = $this->_title;
        }
        $view->sections         = $this->_sections;
        $view->section_title    = $section->getTitle();
        $view->section_singular = $section->getTitleSingular();
        $view->model            = $section->getModel();
        $view->links            = $this->_links;
        $view->error            = $this->_error;
        $view->message          = $this->_message;
        $view->form             = $form;
        return $view->render($template);
    }
    
    /**
     * Metodo generico de edicao.
     *
     * @param Goweb_Admin_Section
     * @param string
     * @param string
     * @param integer
     * @param boolean
     * @param boolean
     *
     * @return string
    */
    protected function _editGeneric($section, $view = null, $parent = null, $parent_id = 0, $useTree = true, $cleanModelParts = true, $template = null) {
        

        if (!$view) {
            Goweb::loadClass('Goweb_View');
            $view = new Goweb_View(GENERIC_VIEWS_PATH);
        }
        
        $model = $section->getModel();
        /**
         * podem ser aplicados filtros na listagem, mas que não é suposto serem aplicados na edição ... 
         */
        $model->reset();
        
        if (method_exists($section, 'getCantEdit'))
            $cant_edit = $section->getCantEdit();

        if (isset($cant_edit) && in_array($this->_getParam($model->getPrimary()), $cant_edit)){
            $this->_error = translateAdmin('O elemento {{id}} da {{section}} n&atilde;o pode ser editado!', $this->_getParam($model->getPrimary()), $section->getTitleSingular());
            return $this->_listGeneric($section, $view, true, 'list.tpl', $parent, $parent_id);
        }
        
        if (!$this->_links) {
            $this->_initDefaultLinks();
            $this->_links['edit'] = $this->view->baseUrl . '/' . $this->_controller . '/edit/section/' . $this->_section . '/' . $this->_parentUrl . 'id/' . $this->_getParam($model->getPrimary()) .'/';
            $this->_links['save'] = '/' . $this->_controller . '/save/section/' . $this->_section . '/' . $this->_parentUrl . 'id/' . $this->_getParam($model->getPrimary()) .'/';
            if($this->_request->page){
                $this->_links['edit'] .= '?page=' . $this->_request->page;
                $this->_links['save'] .= '?page=' . $this->_request->page; 
            }
            
        }
        
        Goweb::loadClass('Form_Generic', ADMIN_PATH . DIRECTORY_SEPARATOR . 'Index');
        
        // guarda uma cópia das partes do select do Model
        $select = $model->getSelect();
        if (!$this->_getParam($model->getPrimary()) || !$model->find($this->_getParam($model->getPrimary()))) {
            return $this->_listGeneric($section, $view);            
        }        
        $model->setSelect($select);
        
        
        // usar tree?
        if ($useTree) {
            // se tiver tree
            $tree = $section->getTree($cleanModelParts);
            if ($tree) {
                if (!$parent_id) {
                    $parent_id = '0';
                }            

                if ($tree instanceof Goweb_Js_Dhtmlx_Tree && isset($this->_links['treeDhtmlx'])){
                    $tree->setLink($this->_links['treeDhtmlx']);
                    if ($parent_id > 0){
                        $treeModel = $treeParams['model'];
                        $treeModel->loadForeigns(false);
                        $cats = $treeModel->getPais($parent_id);
                        $cats = array_reverse($cats, true);
                        $treeModel->loadForeigns(true);
                        $tree->setOpenTo($cats);
                    } else {
                        $tree->setOpenTo($parent_id);
                    }
                } else {
                    $tree->setOpenTo($parent_id);
                }
                
                $view->tree = $tree;
            }
        }
        if (!$view->form){
            $form = new Form_Generic($this->_section, $this->_links['save']);
            $form->setup($this->_section, $section->getLabels(), $model, $parent, $parent_id);
        } else {
            $form = $view->form;
        }
        
        $form->render();
        if ($view->ficha){
            $view->ficha->setName($form->getName());
        }
        $this->view->fieldsUpdate = array();
        foreach ($model->getCols() as $colName => $col){
            if ($col->getType() == 'ForeignKey' && $col->getParam('update')){
                if (is_array($col->getParam('update'))){
                    foreach ($col->getParam('update') as $column) $this->view->fieldsUpdate[] = $column;
                } else {
                    $this->view->fieldsUpdate[] = $col->getParam('update');
                }
            }
        }
        if(is_null($template) && count($model->getTabCols()) > 0){
            $template = 'form_tab.tpl';
        } else if(is_null($template)) {
            $template = 'form.tpl';
        }
        
        $section->checkLabels();
        if (is_null($view->action)) {
            $view->action = translateAdmin('Edi&ccedil;&atilde;o');
        }
        if ($this->_title) {
            $view->title        = $this->_title;
        }
        $view->sections         = $this->_sections;
        $view->section_title    = $section->getTitle();
        $view->section_singular = $section->getTitleSingular();
        $view->model            = $model;
        $view->links            = $this->_links;
        $view->error            = $this->_error;
        $view->message          = $this->_message;
        $view->form             = $form;
        return $view->render($template);
    }
    
    /**
     * Metodo generico de gravacao.
     *
     * @param Goweb_Admin_Section
     * @param string
     * @param string
     * @param integer
     * @param boolean
     * @param boolean
     * @param boolean
     *
     * @return string
    */
    protected function _saveGeneric($section, $view = null, $parent = null, $parent_id = 0, $cleanValues = false, $useTree = true, $cleanModelParts = true, $template = 'form.tpl') {

        if (!$view) {
            Goweb::loadClass('Goweb_View');
            $view = new Goweb_View(GENERIC_VIEWS_PATH);
        }
        
        if (!$this->_links) {
            $this->_initDefaultLinks();
        }
        
        Goweb::loadClass('Form_Generic', ADMIN_PATH . DIRECTORY_SEPARATOR . 'Index');
        
        $model = $section->getModel();
        // guarda uma cópia das partes do select do Model
        $select = $model->getSelect();
        
        if ($this->_getParam($model->getPrimary()) > 0) {
            $this->_links['save'] .= $model->getPrimary() . '/' . $this->_getParam($model->getPrimary()) . '/';
        }
    
        foreach ($this->_request->getUserParams() as $param => $value){
            if ($param == 'lang') continue;
            if ($param != $parent && $param != $model->getPrimary() && (in_array($param, $model->getColsNames()) || 
                    (array_search($param, $model->getForeignColsWithAliases()) && 
                    in_array(array_search($param, $model->getForeignColsWithAliases()), $model->getColsNames()) ) ) 
                    && $model->colExists($param) && 
                        $this->_request->{$param}){
                            if (strpos($param, $this->_links['save']) === false) $this->_links['save'] .= $param . '/'.$this->_request->{$param}. '/';
                        }
        }
        
        if (!$view->form){
            $form = new Form_Generic($this->_section, Goweb::link($this->_links['save']));
        } else {
            $form = $view->form;
        }


        if (!$this->_getParam($model->getPrimary()) > 0 || !$model->find($this->_getParam($model->getPrimary()))) {
            $this->_message = translateAdmin('{{section}} foi inserido(a) com sucesso.', $section->getTitleSingular());
            if (is_null($view->action)) {
                $view->action = translateAdmin('Inser&ccedil;&atilde;o');
            }
        } else {
            $this->_message = translateAdmin('{{section}} foi actualizado(a) com sucesso.', $section->getTitleSingular());
            if (is_null($view->action)) {
                $view->action = translateAdmin('Edi&ccedil;&atilde;o');
            }
        }

        $form->setup($this->_section, $section->getLabels(), $model, $parent, $parent_id);
        $this->view->fieldsUpdate = array();
        foreach ($model->getCols() as $colName => $col){
            if ($col->getType() == 'ForeignKey' && $col->getParam('update')){
                if (is_array($col->getParam('update'))){
                    foreach ($col->getParam('update') as $column) $this->view->fieldsUpdate[] = $column;
                } else {
                    $this->view->fieldsUpdate[] = $col->getParam('update');
                }
            }
        }
        if (!$form->validate()) {
            if (count($form->getErrorMessages()) > 1) {
                $this->_error = translateAdmin('Corrija por favor os erros no formul&aacute;rio');
                $this->_message = null;
            } else {
                $this->_error = translateAdmin('Corrija por favor o erro no formul&aacute;rio');
                $this->_message = null;
            }
        } else {
            if (!$model->isLoaded()) {
                $model->find($this->_getParam($model->getPrimary()));
            }
            foreach ($this->_request->getUserParams() as $param => $value){
                if ($param == 'lang') continue;
                if ($param != $parent && $param != $model->getPrimary() && (in_array($param, $model->getColsNames()) ||
                    (array_search($param, $model->getForeignColsWithAliases()) &&
                    in_array(array_search($param, $model->getForeignColsWithAliases()), $model->getColsNames()) ) )
                    && $model->colExists($param) &&
                    $this->_request->{$param}){
                        if (!$form->elementExists($param)) {
                            $form->addHidden($param, $this->_request->{$param});
                        }
                }
            }
            $model = $this->_formModelValues($form, $model);

            if (!is_null($parent)) {
                if (is_null($model->{$parent})) {
                    $model->{$parent} = $parent_id;
                }
            }

            if (!$model->save()) {
                return false;                
            }

            if ($this->_request->submit) {                
                return true;
            }
            
            if ($this->_request->submit_and_new) {
                // $this->_links['save'] = '/' . $this->_controller . '/save/section/' . $this->_section . '/' . $this->_parentUrl;
                return true;
            }

            if ($this->_request->submit_and_continue) {                
                return true;
            }
            
            if ($this->_request->submit_extra) {
                return true;
            }
            
            $primary = $model->getPrimary();            
            // $form->setAction(Goweb::link($this->view->baseUrl . '/' . $this->_controller .'/save/section/' . $this->_section . '/id/' . $model->{$primary} . '/'));
        }
        
        $model->setSelect($select);
        // usar tree?
        if ($useTree) {
            $tree = $section->getTree($cleanModelParts);
            // se tiver tree
            if ($tree) {
                if (!$parent_id) {
                    $parent_id = '0';
                }            

                if ($tree instanceof Goweb_Js_Dhtmlx_Tree && isset($this->_links['treeDhtmlx'])){
                    $tree->setLink($this->_links['treeDhtmlx']);
                    if ($parent_id > 0){
                        $treeModel = $treeParams['model'];
                        $treeModel->loadForeigns(false);
                        $cats = $treeModel->getPais($parent_id);
                        $cats = array_reverse($cats, true);
                        $treeModel->loadForeigns(true);
                        $tree->setOpenTo($cats);
                    } else {
                        $tree->setOpenTo($parent_id);
                    }
                } else {
                    $tree->setOpenTo($parent_id);
                }
                
                $view->tree = $tree;
            }
        }        
        
        $form->render();
        if ($view->ficha){
            $view->ficha->setName($form->getName());
        }
        $id = $model->getPrimary();
        $section->checkLabels();
        if ($this->_title) {
            $view->title        = $this->_title;
        }
        $view->{$id}            = $model->{$id}; 
        $view->sections         = $this->_sections;
        $view->section_title    = $section->getTitle();
        $view->section_singular = $section->getTitleSingular();
        $view->model            = $section->getModel();
        $view->links            = $this->_links;
        $view->error            = $this->_error;
        $view->message          = $this->_message;
        $view->form             = $form;
        return $view->render($template);
    }

    /**
     * Metodo que verifica relacao entre os modelos de dados.
     *
     * @param string
     * @param string
     * @param integer
     * @param integer
     *
     * @return boolean
     */
    protected function _findRelation($relations, $parent, $child, $total) {
        if (array_key_exists($child, $relations) &&
            array_key_exists($relations[$child], $relations) &&
            $parent != $child && $total > 0) {
            $total--;
            return $this->_findRelation($relations, $parent, $relations[$child], $total);
        } elseif ($parent == $child) {
            return true;
        }
        return false;
    }

    /**
     * Metodo para a remocao automatica do registo seleccionado
     *
     * @param Goweb_Admin_Section
     * @param string
     * @param integer
     * @param integer
     *
     * @return boolean
    */
    protected function _deleteGeneric($section, $view = null, $parent = null, $parent_id = 0) {
        $model = $section->getModel();
        
        if (!$this->_getParam($model->getPrimary()) || !$model->find($this->_getParam($model->getPrimary()))) {
            return false;
        }
            
        if (method_exists($section, 'getCantDelete'))
            $cant_delete = $section->getCantDelete();

        if (isset($cant_delete) && in_array($this->_getParam($model->getPrimary()), $cant_delete)){
            $this->_error = translateAdmin('O elemento {{id}} da {{section}} n&atilde;o pode ser removido!', $this->_getParam($model->getPrimary()), $section->getTitleSingular());
            return $this->_listGeneric($section, $view, true, 'list.tpl', $parent, $parent_id);
        }        
        if (!$this->_links) {
            $this->_initDefaultLinks();
        }
        
        $relations = $model->getRelations();
        if (count($relations) > 0) {
            foreach ($relations as $relation) {
                $modelName = $relation['model'];
                $foreignModel = Goweb::getModelInstance($modelName, $model->getAdapter(), false);
                $key = $foreignModel->getTableName() . '.' . $foreignModel->getAdapter()->quoteIdentifier($relation['col']);
                $where = $foreignModel->getAdapter()->quoteInto($key . ' = ?', $this->_getParam($model->getPrimary()));
                $foreignModel->where($where);
                if ($foreignModel->count() > 0) {
                    $this->_error = translateAdmin('O item n&atilde;o pode ser removido porque tem itens associados!');
                    return false;
                }
            }
        }
        
        foreach ($model->getCols() as $key => $col) {
            if ($col->getParam('type') == 'Image' || $col->getParam('type') == 'File') {                
                $filename = $model->{$key};                
                $uploadPath = WWW_PATH . DIRECTORY_SEPARATOR . trim($col->getParam('target'), '/') . DIRECTORY_SEPARATOR;
                if (strlen($filename)) {
                    try {
                        if (file_exists($uploadPath . $filename) && is_writable($uploadPath . $filename))
                        unlink($uploadPath . $filename);
                        if ($col->getParam('type') == 'Image'){
                            $sizes = $col->getParam('sizes');
                            foreach ($sizes as $size){
                                if (file_exists($uploadPath . $size . '_' . $filename) && is_writable($uploadPath . $size . '_' . $filename)) {
                                    unlink($uploadPath . $size . '_' . $filename);
                                }
                            }
                        }
                    } catch (Exception $e) {
                        $this->_error = translateAdmin('N&atilde;o foi possivel remover item.');
                        return false;
                    }
                }
            }
        }
        $model->delete($this->_getParam($model->getPrimary()));
        $this->_message = translateAdmin('{{seccao}} foi removido com sucesso.', $section->getTitleSingular());
        return true;
    }

    /**
     * Metodo para a remocao automatica dos registos seleccionados
     *
     * @param Goweb_Admin_Section
     * @param string
     * @param integer
     * @param integer
     * @return boolean
     */
    protected function _deletelistGeneric($section, $view = null, $parent = null, $parent_id = 0) {
        $model = $section->getModel();
        
        $ids = $this->_request->ids;
        
        if (!$ids || !is_array($ids)) {
            return false;
        }
        
        if (!$this->_links) {
            $this->_initDefaultLinks();
        }
        
        $relations = $model->getRelations();
        if (count($relations) > 0) {
            foreach ($ids as $id) {
                foreach ($relations as $relation) {
                    $modelName = $relation['model'];
                    $foreignModel = Goweb::getModelInstance($modelName, $model->getAdapter());
                    $key = $foreignModel->getTableName() . '.' . $foreignModel->getAdapter()->quoteIdentifier($relation['col']);
                    $where = $foreignModel->getAdapter()->quoteInto($key . ' = ?', $id);
                    $foreignModel->where($where);
                    if ($foreignModel->count() > 0) {
                        $this->_error = translateAdmin('O item n&atilde;o pode ser removido porque tem itens associados!');
                        return false;
                    }
                }
            }
        }
        
        foreach ($ids as $id) {
            if (!$model->find($id)) {
                return false;
            }
            foreach ($model->getCols() as $key => $col) {
                if ($col->getParam('type') == 'Image' || $col->getParam('type') == 'File') {                
                    $filename = $model->{$key};
                    $uploadPath = WWW_PATH . DIRECTORY_SEPARATOR . trim($col->getParam('target'), '/') . DIRECTORY_SEPARATOR;
                    if (strlen($filename)) {
                        try {
                            if (file_exists($uploadPath . $filename) && is_writable($uploadPath . $filename))
                                unlink($uploadPath . $filename);
                            if ($col->getParam('type') == 'Image'){
                                $sizes = $col->getParam('sizes');
                                foreach ($sizes as $size){
                                    if (file_exists($uploadPath . $size . '_' . $filename) && is_writable($uploadPath . $size . '_' . $filename)) {
                                        unlink($uploadPath . $size . '_' . $filename);
                                    }
                                }
                            }
                        } catch (Exception $e) {
                            $this->_error = translateAdmin('N&atilde;o foi possivel remover item.');
                            return false;
                        }
                    }
                    
                }
            }
            
            $model->delete($id);
        }
        
        $this->_message = translateAdmin('Os {{section}} foram removidos com sucesso.', $section->getTitle());
        return true;
    }

    /**
     * Metodo para a remocao automatica dos registos seleccionados
     *
     * @param string    $image      Caminho completo da imagem
     * @param string    $watermark  Caminho completo ou relativo da watermark
     * @return boolean  No caso de sucesso
     */
    function _applyWatermark($image, $watermark) {
        if (strpos($watermark, '/', 0) === 0) {
            $watermark = WWW_PATH . DIRECTORY_SEPARATOR . ltrim($watermark, '/');
        } else {
            $watermark = WWW_PATH . DIRECTORY_SEPARATOR . ltrim($watermark, '/');
        }
        
        if (!is_file($image) || !is_writable($image)) {
            throw new Goweb_Exception("A image '$image' n&atilde;o existe ou n&atilde;o possui permiss&otilde;es de escrita.");
        }
        if (!is_file($watermark) || !is_readable($watermark)) {
            throw new Goweb_Exception("A image '$watermark' n&atilde;o existe ou n&atilde;o possui permiss&otilde;es de leitura.");
        }

        if (exif_imagetype($watermark) != IMAGETYPE_PNG) {
            throw new Goweb_Exception('O tipo de imagem requerido para a marca de água é PNG.');
        }                

        $watermarkResource = imagecreatefrompng($watermark);

        switch (exif_imagetype($image)) {
         case IMAGETYPE_GIF:
             $imageResource = imagecreatefromgif($image);
             break;
         case IMAGETYPE_JPEG:
             $imageResource = imagecreatefromjpeg($image);
             break;
         case IMAGETYPE_PNG:
             $imageResource = imagecreatefrompng($image);
             break;
         default:
             throw new Goweb_Exception("O tipo de imagem '" . exif_imagetype($image) . "' n&atilde;o e suportado.");
             break;
        }
        
        imagecopy($imageResource, $watermarkResource,
                  ceil(imagesx($imageResource) / 2) - ceil(imagesx($watermarkResource) / 2),
                  ceil(imagesy($imageResource) / 2) - ceil(imagesy($watermarkResource) / 2),
                  0, 0, imagesx($watermarkResource), imagesy($watermarkResource));
        
        switch (exif_imagetype($image)) {
         case IMAGETYPE_GIF:
             imagegif($imageResource, $image);
             break;
         case IMAGETYPE_JPEG:
             imagejpeg($imageResource, $image, 100);
             break;
         case IMAGETYPE_PNG:             
         default:
             imagepng($imageResource, $image);
             break;
        }
        
        imagedestroy($watermarkResource);
        imagedestroy($imageResource);
    }
    
    /**
     * Metodo para processar um pedido via REQUEST_URI.
     */
    protected function _processRequestUri() {
        $path = $_SERVER['REQUEST_URI'];
        $this->_params = array();
        
        if (URL_REWRITE) {
            if (strstr($path, '?')) {
                $path = substr($path, 0, strpos($path, '?'));
            }
            
            $path = explode('/', trim($path, '/'));
            
            $this->_controller = $path[0];
            $this->_action     = isset($path[1]) ? $path[1] : null;
            
            if (!strlen($this->_controller)) {
                $this->_controller = 'index';
                $this->_action = 'index';
            }
            
            for ($i=2; $i<sizeof($path); $i=$i+2) {
                $this->_params[$path[$i]] = isset($path[$i+1]) ? $path[$i+1] : null;
            }
        } else {
            if (strstr($path, '?')) {
                $path = substr($path, strpos($path, '?') + 1, strlen($path));
            }
            
            $path = explode('&', trim($path));
            $pathArray = array();
            
            if (sizeof($path) > 0) {
                foreach ($path as $arg) {
                    $arr = explode('=', $arg);
                    if (sizeof($arr) == 2) {
                        $pathArray[$arr[0]] = $arr[1];
                    }
                }
            }        
            
            if (array_key_exists('controller', $pathArray)) {
                $this->_controller = $pathArray['controller'];
                if (array_key_exists('action', $pathArray)) {
                    $this->_action = $pathArray['action'];
                    unset($pathArray['controller']);
                    unset($pathArray['action']);
                    $this->_params = $pathArray;
                }
            }
        }   
    }

    /**
     * Adicionar valores á tree dhtmlx
     *
     * @param String $value
     * @param mixed $id
     * @throws Goweb_Exception
     * @return String html com o nó da àrvore
     */
    protected function _addTreeOptions($value, $id, $nodesParams = null){
        $value = str_replace('&', '&amp;', html_entity_decode($value, null, CHARSET));

        if (is_null($nodesParams) && isset($this->_categoriasNodesParams)) {
            $nodesParams = $this->_categoriasNodesParams;
        } elseif (is_null($nodesParams)){
            throw new Goweb_Exception('Par&acirc;metros n&atilde;o definidos');
        }
        $urlBase = Zend_Controller_Front::getInstance()->getRequest()->getBaseUrl();
        if (strpos($nodesParams['url'], $urlBase) === false) $nodesParams['url'] = $urlBase . $nodesParams['url'];
        $out = '';
        // configura os links e icons extra
        for ($i=1; $i<=5; $i++) {
            $param = 'url_' . $i . '_icon';
            if  (array_key_exists($param, $nodesParams) && $nodesParams[$param]) {
                $onClick = '';
                if ($nodesParams[$param] == '/media/images/icon_delete.gif'){
                    $onClick = ' onclick="javascript:return confirm(\'' . translateAdmin('Tem a certeza que deseja remover o item seleccionado?') . '\');"';
                }
                if (strpos($nodesParams[$param], str_replace('/index.php', '', $urlBase)) === false) {
                    $nodesParams[$param] = str_replace('/index.php', '', $urlBase) . $nodesParams[$param];
                }
                $img = $nodesParams[$param];
                $param = 'url_' . $i;
                $link = '';
                if (array_key_exists($param, $nodesParams) && $nodesParams[$param]) {
                    if (strpos($nodesParams[$param], $urlBase) === false) $nodesParams[$param] = $urlBase . $nodesParams[$param];
                    $link = $this->view->link($nodesParams[$param] . $id);
                }
                $out .= '<a href="' . ($link ? $link : '#') . '"' . $onClick . '>' . $this->view->img($img, array('border' => 0)) . '</a>';
            }
        }
        $link = $this->view->link($nodesParams['url'] . $id);
        return '<a href="' . ($link ? $link : '#') . '"'. '>' . $value . '</a>' . $out;
    }

    /**
     * obter as permissões da seção
     *
     * @param Goweb_Admin_Section $section
     */
    protected function _getPermissions($section){
        if ($section->getUpdateOnly()){
            $this->_links['delete'] = null;
            $this->_links['deletelist'] = null;
        }
        
        
        if (method_exists($section, 'getActions'))
            $this->view->list_actions = $section->getActions();
        if (method_exists($section, 'getCantEdit'))
            $cant_edit = $section->getCantEdit();
        if (method_exists($section, 'getCantDelete'))
            $cant_delete = $section->getCantDelete();
        if (method_exists($section, 'getCantDeleteList'))
            $cant_delete_list = $section->getCantDeleteList();
        if (method_exists($section, 'getCanAdd'))
            $can_add = $section->getCanAdd();
        
        if (isset($can_add) && !$can_add){
            $this->_links['add'] = null;
        }
                                            
        if (isset($cant_delete_list) && !$cant_delete_list){
            $this->_links['deletelist'] = null;
        }
        
        if (isset($cant_delete)){
             $this->view->cant_delete = $cant_delete; 
        }

        if (isset($cant_edit)){
             $this->view->cant_edit = $cant_edit; 
        }
        if (isset($cant_delete) && (is_bool($cant_delete) && !$cant_delete)){
            $this->_links['delete'] = null;
        }
    }
    
    
    /**
     * Metodo principal que faz o encaminhamento do pedido para o sitio correcto.
     */
    public function __call($action, $arguments) {
        //$this->_processRequestUri();

        if ($this->_action && !method_exists($this->_action . 'Action', $this)) {
            $this->_action = 'index';
        }
        
        $this->_section = $this->_getParam('section');

        $method = $this->_action . 'Action';
        $this->{$method}();
    }
}