<?php

use Illuminate\Support\Facades\Log;
use ProcessMaker\BusinessModel\DynaForm;
use ProcessMaker\Core\System;
use ProcessMaker\Model\AdditionalTables as AdditionalTablesModel;
use ProcessMaker\Model\Dynaform as DynaformModel;
use ProcessMaker\Model\ProcessVariables;
use ProcessMaker\Project\Bpmn;
use ProcessMaker\Validation\ExceptionRestApi;
use ProcessMaker\Validation\ValidationUploadedFiles;

class pmTablesProxy extends HttpProxyController
{

    protected $className;
    protected $classPeerName;
    protected $dynUid;

    /**
     * get pmtables list
     *
     * @param string $httpData->start
     * @param string $httpData->limit
     * @param string $httpData->textFilter
     */
    public function getList($httpData)
    {
        $configurations = new Configurations();
        $processMap = new ProcessMap();

        // setting parameters
        $config = $configurations->getConfiguration('additionalTablesList', 'pageSize', '', $_SESSION['USER_LOGGED']);
        $env = $configurations->getConfiguration('ENVIRONMENT_SETTINGS', '');
        $limit_size = isset($config->pageSize) ? $config['pageSize'] : 20;
        $start = isset($httpData->start) ? $httpData->start : 0;
        $limit = isset($httpData->limit) ? $httpData->limit : $limit_size;
        $filter = isset($httpData->textFilter) ? $httpData->textFilter : '';
        $pro_uid = isset($httpData->pro_uid) ? $httpData->pro_uid : null;

        if ($pro_uid !== null) {
            $process = $pro_uid == '' ? ['not_equal' => $pro_uid] : ['equal' => $pro_uid];
            $addTables = AdditionalTables::getAll(false, false, $filter, $process);

            $c = $processMap->getReportTablesCriteria($pro_uid);
            $oDataset = RoutePeer::doSelectRS($c);
            $oDataset->setFetchmode(ResultSet::FETCHMODE_ASSOC);
            $reportTablesOldList = array();
            while ($oDataset->next()) {
                $reportTablesOldList[] = $oDataset->getRow();
            }
            foreach ($reportTablesOldList as $i => $oldRepTab) {
                if ($filter != '') {
                    if ((stripos($oldRepTab['REP_TAB_NAME'], $filter) !== false) ||
                            (stripos($oldRepTab['REP_TAB_TITLE'], $filter) !== false)) {
                        $addTables['rows'][] = [
                            'ADD_TAB_UID' => $oldRepTab['REP_TAB_UID'],
                            'PRO_UID' => $oldRepTab['PRO_UID'],
                            'DBS_UID' => ($oldRepTab['REP_TAB_CONNECTION'] == 'wf' ? 'workflow' : 'rp'),
                            'ADD_TAB_DESCRIPTION' => $oldRepTab['REP_TAB_TITLE'],
                            'ADD_TAB_NAME' => $oldRepTab['REP_TAB_NAME'],
                            'ADD_TAB_TYPE' => $oldRepTab['REP_TAB_TYPE'],
                            'TYPE' => 'CLASSIC'
                        ];
                    }
                } else {
                    $addTables['rows'][] = [
                        'ADD_TAB_UID' => $oldRepTab['REP_TAB_UID'],
                        'PRO_UID' => $oldRepTab['PRO_UID'],
                        'DBS_UID' => ($oldRepTab['REP_TAB_CONNECTION'] == 'wf' ? 'workflow' : 'rp'),
                        'ADD_TAB_DESCRIPTION' => $oldRepTab['REP_TAB_TITLE'],
                        'ADD_TAB_NAME' => $oldRepTab['REP_TAB_NAME'],
                        'ADD_TAB_TYPE' => $oldRepTab['REP_TAB_TYPE'],
                        'TYPE' => 'CLASSIC'
                    ];
                }
            }
            $addTables['count'] = count($addTables['rows']);
            if ($start != 0) {
                $addTables['rows'] = array_splice($addTables['rows'], $start);
            }
            $addTables['rows'] = array_splice($addTables['rows'], 0, $limit);
        } else {
            $addTables = AdditionalTables::getAll($start, $limit, $filter);
        }

        foreach ($addTables['rows'] as $i => $table) {
            //removing the prefix "PMT" to allow alphabetical order (just in view)
            if (substr($addTables['rows'][$i]['ADD_TAB_NAME'], 0, 4) == 'PMT_') {
                $addTables['rows'][$i]['ADD_TAB_NAME'] = substr($addTables['rows'][$i]['ADD_TAB_NAME'], 4);
            }
        }

        return $addTables;
    }

    /**
     * get processesList
     */
    public function getProcessList ()
    {
        require_once 'classes/model/Process.php';

        $process = new Process();
        return $process->getAll();
    }

    /**
     * get database connection list
     */
    public function getDbConnectionsList ()
    {
        if (! isset( $_SESSION['PROCESS'] )) {
            $_SESSION['PROCESS'] = $_POST['PRO_UID'];
        }
        $proUid = $_POST['PRO_UID'];
        $dbConn = new DbConnections();
        $dbConnections = $dbConn->getConnectionsProUid( $proUid, array('mysql') );

        $workSpace = new WorkspaceTools(config("system.workspace"));
        $workspaceDB = $workSpace->getDBInfo();

        if ($workspaceDB['DB_NAME'] == $workspaceDB['DB_RBAC_NAME']) {
            $defaultConnections = array (array ('DBS_UID' => 'workflow','DBS_NAME' => 'Workflow'));
        } else {
            $defaultConnections = array (array ('DBS_UID' => 'workflow','DBS_NAME' => 'Workflow'),
                                         array ('DBS_UID' => 'rp','DBS_NAME' => 'REPORT'));
        }

        $dbConnections = array_merge( $defaultConnections, $dbConnections );

        return $dbConnections;
    }

    /**
     * get dynaform fields
     *
     * @param string $httpData->PRO_UID
     * @param string $httpData->TYPE
     * @param string $httpData->GRID_UID
     */
    public function getDynafields ($httpData)
    {

        $aFields['FIELDS'] = array ();
        $aFields['PRO_UID'] = $httpData->PRO_UID;
        $dynFields = array ();

        if (isset($httpData->loadField) && $httpData->loadField) {
            unset($_SESSION['_cache_pmtables']);
        }

        $httpData->textFilter = (isset($httpData->textFilter))? $httpData->textFilter : null;

        if (isset( $httpData->TYPE ) && $httpData->TYPE == 'GRID') {
            if (isset( $httpData->GRID_UID )) {
                list($gridId, $dynaFormUid) = explode('-', $httpData->GRID_UID);

                $this->dynUid = $dynaFormUid;
                $this->gridId = $gridId;

                $dynFields = $this->_getDynafields($aFields['PRO_UID'], 'grid', $httpData->start, $httpData->limit, $httpData->textFilter);
            } else {
                $gridFields = $this->_getGridFields($aFields['PRO_UID']);

                foreach ($gridFields as $value) {
                    $dynFields[] = [
                        'FIELD_UID'  => $value['gridId'] . '-' . $value['uid'],
                        'FIELD_NAME' => $value['gridName']
                    ];
                }
            }
        } else {
            // normal dynaform
            $dynFields = $this->_getDynafields( $aFields['PRO_UID'], 'xmlform', $httpData->start, $httpData->limit, $httpData->textFilter );
        }

        return $dynFields;
    }

    public function updateAvDynafields ($httpData)
    {
        $indexes = explode( ',', $httpData->indexes );
        $fields = array ();
        $httpData->isset = $httpData->isset == 'true' ? true : false;

        if (isset( $_SESSION['_cache_pmtables'] ) && $_SESSION['_cache_pmtables']['pro_uid'] == $httpData->PRO_UID) {
            foreach ($indexes as $i) {
                if (is_numeric( $i )) {
                    if (isset( $_SESSION['_cache_pmtables']['rows'][$i] )) {
                        $_SESSION['_cache_pmtables']['rows'][$i]['_isset'] = $httpData->isset;
                        if ($httpData->isset) {
                            $_SESSION['_cache_pmtables']['count'] ++;
                        } else {
                            $_SESSION['_cache_pmtables']['count'] --;
                        }

                        $fields[] = $_SESSION['_cache_pmtables']['rows'][$i]['FIELD_NAME'];
                    }
                } else {

                    $index = $_SESSION['_cache_pmtables']['indexes'][$i];
                    $_SESSION['_cache_pmtables']['rows'][$index]['_isset'] = $httpData->isset;
                }
            }
        }
        return $fields;
    }

    /**
     * save pm table
     */
    public function save ($httpData, $alterTable = true)
    {
        try {
            // Remove temporary Propel folder used by Report Tables and PM Tables (Bug PMC-388)
            PmTable::removePmtPropelFolder();

            $reportTable = new \ProcessMaker\BusinessModel\ReportTable();

            return $reportTable->saveStructureOfTable((array)($httpData), $alterTable);
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Update the offline property.
     * @param stdClass $httpData
     * @return stdClass
     */
    public function updateOffline(stdClass $httpData): stdClass
    {
        $result = new stdClass();
        try {
            $array = G::json_decode(stripslashes($httpData->rows));
            $data = [];
            $enable = false;
            foreach ($array as $value) {
                if ($value->type !== "NORMAL") {
                    $data[] = $value->id;
                    $enable = $value->offline ? 1 : 0;
                }
            }
            AdditionalTablesModel::updatePropertyOffline($data, $enable);
            $result->success = true;
            $result->message = $enable ? G::LoadTranslation("ID_ENABLE") : G::LoadTranslation("ID_DISABLE");
        } catch (Exception $e) {
            $result->success = false;
            $result->message = $e->getMessage();
        }
        return $result;
    }

    /**
     * delete pm table
     *
     * @param string $httpData->rows
     */
    public function delete ($httpData)
    {
        $result = new stdClass();
        $rows = G::json_decode( stripslashes( $httpData->rows ) );
        $errors = '';
        $count = 0;
        $result = new StdClass();

        $tableCasesList = array();
        $conf = new Configurations();
        $confCasesListDraft = $conf->getConfiguration( 'casesList', 'draft');
        $confCasesListPaused = $conf->getConfiguration( 'casesList', 'paused');
        $confCasesListSent = $conf->getConfiguration( 'casesList', 'sent');
        $confCasesListTodo = $conf->getConfiguration( 'casesList', 'todo');
        $confCasesListUnassigned = $conf->getConfiguration( 'casesList', 'unassigned');
        $tableCasesList['draft'] = ($confCasesListDraft != null) ? (isset($confCasesListDraft['PMTable']) ? $confCasesListDraft['PMTable'] : '') : '';
        $tableCasesList['paused'] = ($confCasesListPaused != null) ? (isset($confCasesListPaused['PMTable']) ? $confCasesListPaused['PMTable'] : '') : '';
        $tableCasesList['sent'] = ($confCasesListSent != null) ? (isset($confCasesListSent['PMTable']) ? $confCasesListSent['PMTable'] : '') : '';
        $tableCasesList['todo'] = ($confCasesListTodo != null) ? (isset($confCasesListTodo['PMTable']) ? $confCasesListTodo['PMTable'] : '') : '';
        $tableCasesList['unassigned'] = ($confCasesListUnassigned != null) ? (isset($confCasesListUnassigned['PMTable']) ? $confCasesListUnassigned['PMTable'] : '') : '';

        foreach ($rows as $row) {
            try {
                $at = new AdditionalTables();
                $table = $at->load( $row->id );

                if (! isset( $table )) {
                    require_once 'classes/model/ReportTable.php';
                    $rtOld = new ReportTable();
                    $existReportTableOld = $rtOld->load( $row->id );
                    if (count($existReportTableOld) == 0) {
                        throw new Exception( G::LoadTranslation('ID_TABLE_NOT_EXIST_SKIPPED') );
                    }
                }

                foreach ($tableCasesList as $action => $idTable) {
                    if ($idTable == $row->id) {
                        $conf = new Configurations();
                        $resultJson = $conf->casesListDefaultFieldsAndConfig($action);
                        $conf->saveObject($resultJson, "casesList", $action, "", "", "");
                    }
                }

                if ($row->type == 'CLASSIC') {
                    $rp = new ReportTables();
                    $rp->deleteReportTable( $row->id );
                    $count ++;
                } else {
                    $at->deleteAll( $row->id );
                    $count ++;
                }

                $oCriteria = new Criteria('workflow');
                $oCriteria->add(CaseConsolidatedCorePeer::REP_TAB_UID, $row->id);
                $oResult = CaseConsolidatedCorePeer::doSelectOne($oCriteria);
                if(!empty($oResult)) {
                    $sTasUid = $oResult->getTasUid();
                    $oCaseConsolidated = new CaseConsolidatedCore();
                    $oCaseConsolidated = CaseConsolidatedCorePeer::retrieveByPK($sTasUid);
                    $oCaseConsolidated->delete();
                }
            } catch (Exception $e) {
                $tableName = isset( $table['ADD_TAB_NAME'] ) ? $table['ADD_TAB_NAME'] : $row->id;
                $errors .= $e->getMessage() . "\n";
                continue;
            }
        }

        if ($errors == '') {
            $result->success = true;
            $result->message = $count.G::LoadTranslation( 'ID_TABLES_REMOVED_SUCCESSFULLY' );
            G::auditLog("DeletePmtable", "Table Name: ". $table['ADD_TAB_NAME']." Table ID: (".$table['ADD_TAB_UID'].") ");
        } else {
            $result->success = false;
            $result->message = $count. G::LoadTranslation( 'ID_TABLES_REMOVED_WITH_ERRORS' ) .$errors;
        }

        $result->errors = $errors;

        return $result;
    }

    /**
     * get pm tables data
     *
     * @param string $httpData->id
     * @param string $httpData->start
     * @param string $httpData->limit
     * @param string $httpData->appUid
     */
    public function dataView ($httpData)
    {
        $co = new Configurations();
        $config = $co->getConfiguration( 'additionalTablesData', 'pageSize', '', $_SESSION['USER_LOGGED'] );
        $limit_size = isset( $config['pageSize'] ) ? $config['pageSize'] : 20;
        $start = isset( $httpData->start ) ? $httpData->start : 0;
        $limit = isset( $httpData->limit ) ? $httpData->limit : $limit_size;
        $appUid = isset( $httpData->appUid ) ? $httpData->appUid : false;
        $appUid = ($appUid == "true") ? true : false;
        $filter = isset( $httpData->textFilter ) ? $httpData->textFilter : '';
        $additionalTables = new AdditionalTables();
        $table = $additionalTables->load( $httpData->id, true );

        if ($filter != '') {
            $result = $additionalTables->getAllData( $httpData->id, $start, $limit, true, $filter, $appUid);
        } else {
            $result = $additionalTables->getAllData( $httpData->id, $start, $limit );
        }

        $primaryKeys = $additionalTables->getPrimaryKeys();

        if (is_array($result['rows'])) {
            foreach ($result['rows'] as $i => $row) {
                $primaryKeysValues = array ();
                foreach ($primaryKeys as $key) {
                    $primaryKeysValues[] = isset( $row[$key['FLD_NAME']] ) ? $row[$key['FLD_NAME']] : '';
                }

                $result['rows'][$i]['__index__'] = G::encrypt( implode( ',', $primaryKeysValues ), 'pmtable' );
            }
        } else {
            $result['rows'] = array();
        }

        return $result;
    }

    /**
     * create pm tables record
     *
     * @param string $httpData->rows
     */
    public function dataCreate ($httpData, $codification = 'json')
    {
        $result = new stdClass();

        try {
            $reportTable = new \ProcessMaker\BusinessModel\ReportTable();

            $arrayResult = $reportTable->createRecord((array)($httpData), $codification);

            if ($arrayResult['success']) {
                $result->success = true;
                $result->message = $arrayResult['message'];
                $result->rows    = $arrayResult['rows'];
                $result->rows['__index__'] = $arrayResult['index'];
            } else {
                $result->success = false;
                $result->message = '$$';
                $result->rows = [];
            }
        } catch (Exception $e) {
            $result->success = false;
            $result->rows = array ();
            $result->message = $e->getMessage();
        }

        return $result;
    }

    /**
     * update pm tables record
     *
     * @param string $httpData->id
     */
    public function dataUpdate ($httpData)
    {
        require_once 'classes/model/AdditionalTables.php';
        $oAdditionalTables = new AdditionalTables();
        $table = $oAdditionalTables->load( $httpData->id, true );
        $primaryKeys = $oAdditionalTables->getPrimaryKeys( 'keys' );
        $this->className = $table['ADD_TAB_CLASS_NAME'];
        $this->classPeerName = $this->className . 'Peer';
        $sPath = PATH_DB . config("system.workspace") . PATH_SEP . 'classes' . PATH_SEP;
        if (! file_exists( $sPath . $this->className . '.php' )) {
            throw new Exception( 'Update:: ' . G::loadTranslation( 'ID_PMTABLE_CLASS_DOESNT_EXIST', $this->className ) );
        }

        require_once $sPath . $this->className . '.php';

        $rows = G::json_decode( $httpData->rows );

        if (is_array( $rows )) {
            foreach ($rows as $row) {
                $row = (array) $row;
                $result = $this->_dataUpdate( $row, $primaryKeys );
            }
        } else {
            //then is object
            $row = (array) $rows;
            $result = $this->_dataUpdate( $row, $primaryKeys );
        }

        if ($result) {
            G::auditLog("UpdateDataPmtable", "Table Name: ".$table['ADD_TAB_NAME']." Table ID: (".$table['ADD_TAB_UID'].") ");
        }
        $this->success = $result;
        $this->message = $result ? G::loadTranslation( 'ID_UPDATED_SUCCESSFULLY' ) : G::loadTranslation( 'ID_UPDATE_FAILED' );
    }

    /**
     * remove a pm tables record
     *
     * @param string $httpData->id
     */
    public function dataDestroy ($httpData)
    {
        require_once 'classes/model/AdditionalTables.php';
        $oAdditionalTables = new AdditionalTables();
        $table = $oAdditionalTables->load( $httpData->id, true );
        $this->className = $table['ADD_TAB_CLASS_NAME'];
        $this->classPeerName = $this->className . 'Peer';
        $sPath = PATH_DB . config("system.workspace") . PATH_SEP . 'classes' . PATH_SEP;

        if (! file_exists( $sPath . $this->className . '.php' )) {
            throw new Exception( 'Destroy:: ' . G::loadTranslation( 'ID_PMTABLE_CLASS_DOESNT_EXIST', $this->className ) );
        }

        require_once $sPath . $this->className . '.php';

        G::auditLog("DeleteDataPmtable", "Table Name: ".$table['ADD_TAB_NAME']." Table ID: (".$table['ADD_TAB_UID'].") ");

        $this->success = $this->_dataDestroy( $httpData->rows );
        $this->message = $this->success ? G::loadTranslation( 'ID_DELETED_SUCCESSFULLY' ) : G::loadTranslation( 'ID_DELETE_FAILED' );
    }

    /**
     * Import pmTable from CSV file
     * @param $httpData
     */
    public function importCSV($httpData)
    {
        $filter = new InputFilter();
        $countRow = 250;
        $tmpfilename = $_FILES['form']['tmp_name']['CSV_FILE'];
        if (preg_match('/[\x00-\x08\x0b-\x0c\x0e\x1f]/', file_get_contents($tmpfilename)) === 0) {
            $filename = $_FILES['form']['name']['CSV_FILE'];
            if ($oFile = fopen($filter->xssFilterHard($tmpfilename, 'path'), 'r')) {
                require_once 'classes/model/AdditionalTables.php';
                $oAdditionalTables = new AdditionalTables();
                $aAdditionalTables = $oAdditionalTables->load($_POST['form']['ADD_TAB_UID'], true);
                $sErrorMessages = '';
                $i = 1;
                $conData = 0;
                $insert = 'REPLACE INTO ' . $aAdditionalTables['ADD_TAB_NAME'] . ' (';
                $query = '';
                $swHead = false;
                while (($aAux = fgetcsv($oFile, 4096, $_POST['form']['CSV_DELIMITER'], '"', '"')) !== false) {
                    if (!is_null($aAux[0])) {
                        if (count($aAdditionalTables['FIELDS']) > count($aAux)) {
                            $this->success = false;
                            $this->message = G::LoadTranslation('INVALID_FILE');
                            return 0;
                        }
                        if ($i == 1) {
                            $j = 0;
                            foreach ($aAdditionalTables['FIELDS'] as $aField) {
                                $insert .= $aField['FLD_NAME'] . ', ';
                                if ($aField['FLD_NAME'] === $aAux[$j]) {
                                    $swHead = true;
                                }
                                $j++;
                            }
                            $insert = substr($insert, 0, -2);
                            $insert .= ') VALUES ';
                        }

                        if ($swHead == false) {
                            $queryRow = '(';
                            $j = 0;
                            foreach ($aAdditionalTables['FIELDS'] as $aField) {
                                $conData++;
                                if (array_key_exists($j, $aAux)) {
                                    $temp = '"' . addslashes(G::is_utf8($aAux[$j]) ? $aAux[$j] : utf8_encode($aAux[$j])) . '"';
                                } else {
                                    $temp = '""';
                                }
                                if ($temp == '') {
                                    switch ($aField['FLD_TYPE']) {
                                        case 'DATE':
                                        case 'TIMESTAMP':
                                            $temp = 'NULL';
                                            break;
                                    }
                                }
                                $j++;
                                $queryRow .= $temp . ',';
                            }
                            $query .= substr($queryRow, 0, -1) . '),';
                            try {
                                if ($conData == $countRow) {
                                    $query = $insert . substr($query, 0, -1) . ';';
                                    $con = Propel::getConnection($aAdditionalTables['DBS_UID']);
                                    $con->begin();
                                    $con->executeUpdate($query);
                                    $con->commit();
                                    $query = '';
                                    $conData = 0;
                                }
                            } catch (Exception $oError) {
                                $sErrorMessages .= G::LoadTranslation('ID_ERROR_INSERT_LINE') . ': ' . G::LoadTranslation('ID_LINE') . ' ' . $i . '. ';
                            }
                        } else {
                            $swHead = false;
                        }
                        $i++;
                    }
                }
                fclose($oFile);
                if ($conData > 0) {
                    $query = $insert . substr($query, 0, -1) . ';';
                    $con = Propel::getConnection($aAdditionalTables['DBS_UID']);
                    $con->begin();
                    $con->executeUpdate($query);
                    $con->commit();
                }
            }
            if ($sErrorMessages != '') {
                $this->success = false;
                $this->message = $sErrorMessages;
            } else {
                $this->success = true;
                $this->message = G::loadTranslation('ID_FILE_IMPORTED_SUCCESSFULLY', array($filename
                ));
                G::auditLog("ImportTable", $filename);
            }
        } else {
            $sMessage = G::LoadTranslation('ID_UPLOAD_VALID_CSV_FILE');
            $this->success = false;
            $this->message = $sMessage;
        }
    }

    /**
     * import a CSV to pm tables record
     *
     * @param string $httpData->id
     */
    public function importCSVDeprecated ($httpData)
    {

        $filter = new InputFilter();
        $tmpfilename = $_FILES['form']['tmp_name']['CSV_FILE'];
        //$tmpfilename = $filter->xssFilterHard($tmpfilename, 'path');
        if (preg_match( '/[\x00-\x08\x0b-\x0c\x0e\x1f]/', file_get_contents( $tmpfilename ) ) === 0) {
            $filename = $_FILES['form']['name']['CSV_FILE'];
            $filename = $filter->xssFilterHard($filename, 'path');
            if ($oFile = fopen( $filter->xssFilterHard($tmpfilename, 'path'), 'r' )) {
                require_once 'classes/model/AdditionalTables.php';
                $oAdditionalTables = new AdditionalTables();
                $aAdditionalTables = $oAdditionalTables->load( $_POST['form']['ADD_TAB_UID'], true );
                $sErrorMessages = '';
                $i = 1;
                $swHead = false;
                while (($aAux = fgetcsv( $oFile, 4096, $_POST['form']['CSV_DELIMITER'] )) !== false) {
                    if (! is_null( $aAux[0] )) {
                        if (count( $aAdditionalTables['FIELDS'] ) > count( $aAux )) {
                            $this->success = false;
                            $this->message = G::LoadTranslation( 'INVALID_FILE' );
                            return 0;
                        }
                        if ($i == 1) {
                            $j = 0;
                            foreach ($aAdditionalTables['FIELDS'] as $aField) {
                                if ($aField['FLD_NAME'] === $aAux[$j]) {
                                    $swHead = true;
                                }
                                $j ++;
                            }
                        }

                        if ($swHead == false) {
                            $aData = array ();
                            $j = 0;
                            foreach ($aAdditionalTables['FIELDS'] as $aField) {
                                $aData[$aField['FLD_NAME']] = (isset( $aAux[$j] ) ? $aAux[$j] : '');
                                if ($aData[$aField['FLD_NAME']] == '') {
                                    switch ($aField['FLD_TYPE']) {
                                        case 'DATE':
                                        case 'TIMESTAMP':
                                            $aData[$aField['FLD_NAME']] = null;
                                            break;
                                    }
                                }
                                $j ++;
                            }
                            try {
                                if (! $oAdditionalTables->saveDataInTable( $_POST['form']['ADD_TAB_UID'], $aData )) {
                                    $sErrorMessages .= G::LoadTranslation( 'ID_DUPLICATE_ENTRY_PRIMARY_KEY' ) . ', ' . G::LoadTranslation( 'ID_LINE' ) . ' ' . $i . '. ';
                                }
                            } catch (Exception $oError) {
                                $sErrorMessages .= G::LoadTranslation( 'ID_ERROR_INSERT_LINE' ) . ': ' . G::LoadTranslation( 'ID_LINE' ) . ' ' . $i . '. ';
                            }
                        } else {
                            $swHead = false;
                        }
                        $i ++;
                    }
                }
                fclose( $oFile );
            }
            if ($sErrorMessages != '') {
                $this->success = false;
                $this->message = $sErrorMessages;
            } else {
                $this->success = true;
                $this->message = G::loadTranslation( 'ID_FILE_IMPORTED_SUCCESSFULLY', array ($filename
                ) );
                G::auditLog("ImportTable", $filename);
            }
        } else {
            $sMessage = G::LoadTranslation( 'ID_UPLOAD_VALID_CSV_FILE' );
            $this->success = false;
            $this->message = $sMessage;
        }
    }

    /**
     * Export pmTable to CSV format
     * @param $httpData
     * @return StdClass
     */
    public function exportCSV($httpData)
    {
        $result = new StdClass();
        try {

            $link = '';
            $size = '';
            $META = 'Content';
            $bytesSaved = 0;

            require_once 'classes/model/AdditionalTables.php';
            $oAdditionalTables = new AdditionalTables();
            $aAdditionalTables = $oAdditionalTables->load($_POST['ADD_TAB_UID'], true);
            $sErrorMessages = '';
            $sDelimiter = $_POST['CSV_DELIMITER'];

            $resultData = $oAdditionalTables->getAllData($_POST['ADD_TAB_UID'], null, null, false);
            $rows = $resultData['rows'];
            $count = $resultData['count'];

            $PUBLIC_ROOT_PATH = PATH_DATA . 'sites' . PATH_SEP . config("system.workspace") . PATH_SEP . 'public' . PATH_SEP;
            $filenameOnly = strtolower($aAdditionalTables['ADD_TAB_NAME'] . "_" . date("Y-m-d") . '_' . date("Hi") . ".csv");
            $filename = $PUBLIC_ROOT_PATH . $filenameOnly;
            $fp = fopen($filename, "wb");
            $swColumns = true;
            foreach ($rows as $keyCol => $cols) {
                if ($swColumns) {
                    fputcsv($fp, array_keys($cols), $sDelimiter, '"', "\\");
                    $swColumns = false;
                }
                fputcsv($fp, $cols, $sDelimiter, '"');
            }

            fclose($fp);
            $filenameLink = "streamExported?f=$filenameOnly";
            $size = filesize($filename);
            $link = $filenameLink;

            $result->success = true;
            $result->filename = $filenameOnly;
            $result->link = $link;
            $result->message = "Generated file: $filenameOnly, size: $size";
        } catch (Exception $e) {
            $result->success = false;
            $result->message = $e->getMessage();
        }

        return $result;
    }

    /**
     * import a pm table
     *
     * @param string $httpData->id
     */
    public function import($httpData)
    {
        //ob_start is for catching additional errors that have been printed on included files, 
        //this opening should be closed by ob_end_clean.
        ob_start();

        define('ERROR_PM_TABLES_OVERWRITE', 1);
        define('ERROR_PROCESS_NOT_EXIST', 2);
        define('ERROR_RP_TABLES_OVERWRITE', 3);
        define('ERROR_NO_REPORT_TABLE', 4);
        define('ERROR_OVERWRITE_RELATED_PROCESS', 5);

        $fromAdmin = false;
        if (isset($_POST["form"]["TYPE_TABLE"]) && !empty($_POST["form"]["TYPE_TABLE"])) {
            if ($_POST["form"]["TYPE_TABLE"] == 'admin') {
                $fromAdmin = true;
            }
        }

        $result = new stdClass();
        $arrayOverwrite = [];
        $arrayRelated = [];
        $arrayMessage = [];
        $validationType = 0;
        try {
            ValidationUploadedFiles::getValidationUploadedFiles()->dispatch(function($validator) {
                throw new ExceptionRestApi($validator->getMessage());
            });
            $errors = '';
            $fromConfirm = false;

            $overWrite = isset($_POST['form']['OVERWRITE']) ? true : false;

            if (isset($_POST["form"]["FROM_CONFIRM"]) && !empty($_POST["form"]["FROM_CONFIRM"])) {
                $fromConfirm = $_POST["form"]["FROM_CONFIRM"];
                $_FILES['form'] = $_SESSION['FILES_FORM'];
            }

            //save the file
            if ($_FILES['form']['error']['FILENAME'] !== 0) {
                throw new Exception(G::loadTranslation('ID_PMTABLE_UPLOADING_FILE_PROBLEM'));
            }
            $_SESSION['FILES_FORM'] = $_FILES['form'];

            $PUBLIC_ROOT_PATH = PATH_DATA . 'sites' . PATH_SEP . config("system.workspace") . PATH_SEP . 'public' . PATH_SEP;
            $filename = $_FILES['form']['name']['FILENAME'];
            $tempName = $_FILES['form']['tmp_name']['FILENAME'];

            if (!$fromConfirm) {
                G::uploadFile($tempName, $PUBLIC_ROOT_PATH, $filename);
            }

            if ($fromConfirm == 'clear') {
                $fromConfirm = true;
            }

            $fileContent = file_get_contents($PUBLIC_ROOT_PATH . $filename);

            if (strpos($fileContent, '-----== ProcessMaker Open Source Private Tables ==-----') === false) {
                $result->success = false;
                $result->errorType = 'notice';
                $result->message = G::loadTranslation('ID_PMTABLE_INVALID_FILE', array($filename));
                return $result;
            }

            $currentProUid = '';
            if (isset($_POST["form"]["PRO_UID_HELP"]) && !empty($_POST["form"]["PRO_UID_HELP"])) {
                $currentProUid = $_POST["form"]["PRO_UID_HELP"];
            } else {
                if (isset($_POST["form"]["PRO_UID"]) && !empty($_POST["form"]["PRO_UID"])) {
                    $currentProUid = $_POST["form"]["PRO_UID"];
                    $_SESSION['PROCESS'] = $currentProUid;
                } else {
                    $currentProUid = $_SESSION['PROCESS'];
                }
            }

            //Get Additional Tables
            $arrayTableSchema = [];
            $arrayTableData = [];

            $f = fopen($PUBLIC_ROOT_PATH . $filename, 'rb');

            $fdata = intval(fread($f, 9));
            $type = fread($f, $fdata);

            while (!feof($f)) {
                switch ($type) {
                    case '@META':
                        $fdata = intval(fread($f, 9));
                        $metadata = fread($f, $fdata);
                        break;
                    case '@SCHEMA':
                        $fdataUid = intval(fread($f, 9));
                        $uid = fread($f, $fdataUid);

                        $fdata = intval(fread($f, 9));
                        $schema = fread($f, $fdata);

                        $arrayTableSchema[] = unserialize($schema);
                        break;
                    case '@DATA':
                        $fdata = intval(fread($f, 9));
                        $tableName = fread($f, $fdata);

                        $fdata = intval(fread($f, 9));

                        if ($fdata > 0) {
                            $data = fread($f, $fdata);

                            $arrayTableData[$tableName] = unserialize($data);
                        }
                        break;
                }

                $fdata = intval(fread($f, 9));

                if ($fdata > 0) {
                    $type = fread($f, $fdata);
                } else {
                    break;
                }
            }

            fclose($f);

            // Remove temporary Propel folder used by Report Tables and PM Tables (Bug PMC-388)
            PmTable::removePmtPropelFolder();

            //First Validate the file
            $reportTable = new \ProcessMaker\BusinessModel\ReportTable();

            if (!$fromConfirm) {
                $aErrors = $reportTable->checkPmtFileThrowErrors(
                    $arrayTableSchema, $currentProUid, $fromAdmin, $overWrite, $_POST['form']['PRO_UID']
                );
                $countC = 0;
                $countM = 0;
                $countI = 0;
                foreach ($aErrors as $row) {
                    if ($row['ERROR_TYPE'] == ERROR_PM_TABLES_OVERWRITE || $row['ERROR_TYPE'] == ERROR_RP_TABLES_OVERWRITE) {
                        $arrayOverwrite[$countC] = $row;
                        $countC++;
                    } else {
                        if ($row['ERROR_TYPE'] == ERROR_OVERWRITE_RELATED_PROCESS) {
                            $arrayRelated[$countI] = $row;
                            $countI++;
                        } else {
                            $arrayMessage[$countM] = $row;
                            $countM++;
                        }
                    }
                }
                if (sizeof($aErrors)) {
                    $validationType = 1; //Yes no
                    throw new Exception(G::loadTranslation('ID_PMTABLE_IMPORT_WITH_ERRORS', array($filename)));
                }
            }
            //Then create the tables
            if (isset($_POST["form"]["TABLES_OF_NO"])) {
                $arrayOfNo = $_POST["form"]["TABLES_OF_NO"];
                $arrayOfNew = $_POST["form"]["TABLES_OF_NEW"];
                $aTablesCreateNew = explode('|', $arrayOfNew);
                $aTablesNoCreate = explode('|', $arrayOfNo);
                $errors = $reportTable->createStructureOfTables(
                    $arrayTableSchema,
                    $arrayTableData,
                    $currentProUid,
                    $fromAdmin,
                    true,
                    $aTablesNoCreate,
                    $aTablesCreateNew
                );
            } else {
                $errors = $reportTable->createStructureOfTables(
                    $arrayTableSchema,
                    $arrayTableData,
                    $currentProUid,
                    $fromAdmin,
                    true
                );
            }

            if ($errors == '') {
                $result->success = true;
                $msg = G::loadTranslation('ID_DONE');
            } else {
                $result->success = false;
                $result->errorType = 'warning';
                $msg = G::loadTranslation('ID_PMTABLE_IMPORT_WITH_ERRORS', array($filename)) . "\n\n" . $errors;
            }

            $result->message = $msg;
        } catch (ExceptionRestApi $e) {
            $result = new stdClass();
            $result->success = false;
            $result->errorType = 'notice';
            $result->message = $e->getMessage();
        } catch (Exception $e) {
            $result = new stdClass();
            $result->fromAdmin = $fromAdmin;
            $result->arrayMessage = $arrayMessage;
            $result->arrayRelated = $arrayRelated;
            $result->arrayOverwrite = $arrayOverwrite;
            $result->validationType = $validationType;
            $result->errorType = 'error';
            $result->buildResult = ob_get_contents();
            $result->success = false;

            // if it is a propel exception message
            if (preg_match('/(.*)\s\[(.*):\s(.*)\]\s\[(.*):\s(.*)\]/', $e->getMessage(), $match)) {
                $result->message = $match[3];
                $result->type = G::loadTranslation('ID_ERROR');
            } else {
                $result->message = $e->getMessage();
                $result->type = G::loadTranslation('ID_EXCEPTION');
            }
        }

        //ob_end_clean is used to close the ob_start opening at the beginning of this method.
        if (ob_get_contents()) {
            ob_end_clean();
        }
        return $result;
    }

    /**
     * Export PM tables
     * 
     * @param object $httpData
     * @return object
     */
    public function export($httpData)
    {
        $additionalTables = new AdditionalTables();
        $tablesToExport = G::json_decode(stripslashes($httpData->rows));

        try {
            $result = new stdClass();
            $net = new Net(G::getIpAddress());
            $metaInfo = " \n-----== ProcessMaker Open Source Private Tables ==-----\n" . " @Ver: 1.0 Oct-2009\n" . " @Processmaker version: " . System::getVersion() . "\n" . " -------------------------------------------------------\n" . " @Export Date: " . date("l jS \of F Y h:i:s A") . "\n" . " @Server address: " . getenv('SERVER_NAME') . " (" . getenv('SERVER_ADDR') . ")\n" . " @Client address: " . $net->hostname . "\n" . " @Workspace: " . config("system.workspace") . "\n" . " @Export trace back:\n\n";
            $exportTraceback = [];
            
            foreach ($tablesToExport as $table) {
                $numberRecords = 0;
                if ($table->_DATA) {
                    $tableData = $additionalTables->getAllData($table->ADD_TAB_UID, null, null, false);
                    $numberRecords = $tableData['count'];
                }
                $tableRecord = $additionalTables->load($table->ADD_TAB_UID);
                $table->ADD_TAB_NAME = $tableRecord['ADD_TAB_NAME'];
                array_push($exportTraceback, [
                    'uid' => $table->ADD_TAB_UID,
                    'name' => $table->ADD_TAB_NAME,
                    'num_regs' => $numberRecords,
                    'schema' => $table->_SCHEMA ? 'yes' : 'no',
                    'data' => $table->_DATA ? 'yes' : 'no'
                ]);
            }

            $trace = "TABLE UID                        TABLE NAME\tREGS\tSCHEMA\tDATA\n";
            foreach ($exportTraceback as $row) {
                $trace .= "{$row['uid']}\t{$row['name']}\t\t{$row['num_regs']}\t{$row['schema']}\t{$row['data']}\n";
            }
            $metaInfo .= $trace;

            //Export table
            $publicPath = PATH_DATA . 'sites' . PATH_SEP . config("system.workspace") . PATH_SEP . 'public' . PATH_SEP;
            $filenameOnly = strtolower('SYS-' . config("system.workspace") . "_" . date("Y-m-d") . '_' . date("Hi") . ".pmt");
            $filename = $publicPath . $filenameOnly;
            $fp = fopen($filename, "wb");
            $bytesSaved = 0;
            $bufferType = '@META';
            $fsData = sprintf("%09d", strlen($metaInfo));
            $fsbufferType = sprintf("%09d", strlen($bufferType));
            $bytesSaved += fwrite($fp, $fsbufferType); //writing the size of $oData
            $bytesSaved += fwrite($fp, $bufferType); //writing the $oData
            $bytesSaved += fwrite($fp, $fsData); //writing the size of $oData
            $bytesSaved += fwrite($fp, $metaInfo); //writing the $oData


            foreach ($tablesToExport as $table) {

                if ($table->_SCHEMA) {
                    //Export Schema
                    $pmTables = new AdditionalTables();
                    $aData = $pmTables->load($table->ADD_TAB_UID, true);

                    $bufferType = '@SCHEMA';
                    $dataTable = serialize($aData);
                    $fsUid = sprintf("%09d", strlen($table->ADD_TAB_UID));
                    $fsData = sprintf("%09d", strlen($dataTable));
                    $fsbufferType = sprintf("%09d", strlen($bufferType));
                    $bytesSaved += fwrite($fp, $fsbufferType); //writing the size of $oData
                    $bytesSaved += fwrite($fp, $bufferType); //writing the $oData
                    $bytesSaved += fwrite($fp, $fsUid); //writing the size of xml file
                    $bytesSaved += fwrite($fp, $table->ADD_TAB_UID); //writing the xmlfile
                    $bytesSaved += fwrite($fp, $fsData); //writing the size of xml file
                    $bytesSaved += fwrite($fp, $dataTable); //writing the xmlfile
                }

                if ($table->_DATA) {
                    //Export data
                    $pmTables = new additionalTables();
                    $tableData = $pmTables->getAllData($table->ADD_TAB_UID, null, null, false);

                    $dataTable = serialize($tableData['rows']);
                    $bufferType = '@DATA';
                    $fsbufferType = sprintf("%09d", strlen($bufferType));
                    $fsTableName = sprintf("%09d", strlen($table->ADD_TAB_NAME));
                    $fsData = sprintf("%09d", strlen($dataTable));
                    $bytesSaved += fwrite($fp, $fsbufferType); //writing type size
                    $bytesSaved += fwrite($fp, $bufferType); //writing type
                    $bytesSaved += fwrite($fp, $fsTableName); //writing the size of xml file
                    $bytesSaved += fwrite($fp, $table->ADD_TAB_NAME); //writing the xmlfile
                    $bytesSaved += fwrite($fp, $fsData); //writing the size of xml file
                    $bytesSaved += fwrite($fp, $dataTable); //writing the xmlfile
                }

                G::auditLog("ExportTable", $table->ADD_TAB_NAME . " (" . $table->ADD_TAB_UID . ") ");
            }

            fclose($fp);

            $filenameLink = "pmTables/streamExported?f=$filenameOnly";
            $size = round(($bytesSaved / 1024), 2) . " Kb";
            $link = $filenameLink;

            $result->success = true;
            $result->filename = $filenameOnly;
            $result->link = $link;
            $result->message = "Generated file: $filenameOnly, size: $size";
        } catch (Exception $e) {
            $result = new stdClass();
            $result->success = false;
            $result->message = $e->getMessage();
        }

        return $result;
    }

    public function exportList ()
    {
        require_once 'classes/model/AdditionalTables.php';

        $oCriteria = new Criteria( 'workflow' );
        $oCriteria->addSelectColumn( AdditionalTablesPeer::ADD_TAB_UID );
        $oCriteria->addSelectColumn( AdditionalTablesPeer::ADD_TAB_NAME );
        $oCriteria->addSelectColumn( AdditionalTablesPeer::ADD_TAB_DESCRIPTION );
        $oCriteria->addSelectColumn( "'" . G::LoadTranslation( 'ID_ACTION_EXPORT' ) . "' as 'CH_SCHEMA'" );
        $oCriteria->addSelectColumn( "'" . G::LoadTranslation( 'ID_ACTION_EXPORT' ) . "' as 'CH_DATA'" );

        $uids = explode( ',', $_GET['id'] );

        foreach ($uids as $UID) {
            if (! isset( $CC )) {
                $CC = $oCriteria->getNewCriterion( AdditionalTablesPeer::ADD_TAB_UID, $UID, Criteria::EQUAL );
            } else {
                $CC->addOr( $oCriteria->getNewCriterion( AdditionalTablesPeer::ADD_TAB_UID, $UID, Criteria::EQUAL ) );
            }
        }
        $oCriteria->add( $CC );
        $oCriteria->addAnd( $oCriteria->getNewCriterion( AdditionalTablesPeer::ADD_TAB_UID, '', Criteria::NOT_EQUAL ) );

        $oDataset = AdditionalTablesPeer::doSelectRS( $oCriteria );
        $oDataset->setFetchmode( ResultSet::FETCHMODE_ASSOC );

        $addTables = Array ();
        while ($oDataset->next()) {
            $addTables[] = $oDataset->getRow();
        }

        return $addTables;
    }

    public function updateTag ($httpData)
    {
        require_once 'classes/model/AdditionalTables.php';
        $oAdditionalTables = new AdditionalTables();
        $uid = $_REQUEST['ADD_TAB_UID'];
        $value = $_REQUEST['value'];

        $repTabData = array ('ADD_TAB_UID' => $uid,'ADD_TAB_TAG' => $value
        );
        $oAdditionalTables->update( $repTabData );
    }

    /**
     * - protected functions (non callable from controller outside) -
     */

    /**
     * Update data from a addTable record
     *
     * @param array $row
     * @param array $primaryKeys
     * @return boolean
     * @throws Exception
     *
     * @see workflow/engine/controllers/pmTablesProxy::dataUpdate()
     * @link https://wiki.processmaker.com/3.2/PM_Tables
     */
    public function _dataUpdate($row, $primaryKeys)
    {
        $keys = G::decrypt($row['__index__'], 'pmtable');
        $keys = explode(',', $keys);
        unset($row['__index__']);

        $params = [];

        foreach ($keys as $key) {
            $params[] = is_int($key) ? (int)$key : (string)$key;
        }

        $className = $this->classPeerName;
        $obj = call_user_func_array($className . "::retrieveByPk", $params);
        if (is_object($obj)) {
            foreach ($row as $key => $value) {
                // validation, don't modify primary keys
                if (in_array($key, $primaryKeys)) {
                    throw new Exception(G::loadTranslation('ID_DONT_MODIFY_PK_VALUE', [
                        $key
                    ]));
                }
                $action = 'set' . AdditionalTables::getPHPName($key);
                $obj->$action($value);
            }
            if ($r = $obj->validate()) {
                $obj->save();
                $result = true;
            } else {
                $msg = '';
                foreach ($obj->getValidationFailures() as $objValidationFailure) {
                    $msg .= $objValidationFailure->getMessage() . "\n";
                }
                throw new Exception($msg);
            }
        } else {
            $result = false;
        }

        return $result;
    }

    /**
     * Update data from a addTable record
     *
     * @param array $row
     * @return boolean
     * @see workflow/engine/controllers/pmTablesProxy::dataDestroy()
     * @link https://wiki.processmaker.com/3.2/PM_Tables
     */
    public function _dataDestroy($row)
    {
        $row = G::decrypt($row, 'pmtable');
        $row = str_replace('"', '', $row);
        $keys = explode(',', $row);
        $params = [];
        foreach ($keys as $key) {
            $params[] = is_int($key) ? (int)$key : (string)$key;
        }

        $className = $this->classPeerName;
        $obj = call_user_func_array($className . "::retrieveByPk", $params);

        if (is_object($obj)) {
            $obj->delete();
            return true;
        } else {
            return false;
        }
    }

    /**
     * It eliminates and generates the data report from the cases of a process.
     * 
     * @param object $httpData
     * @return object
     */
    public function genDataReport($httpData)
    {
        $result = new stdClass();

        $result->message = '';
        $result->success = true;

        $additionalTables = new AdditionalTables();
        $table = $additionalTables->load($httpData->id);

        if (!empty($table) && $table['PRO_UID'] != '') {
            try {
                $additionalTables->populateReportTable($table['ADD_TAB_NAME'], PmTable::resolveDbSource($table['DBS_UID']), $table['ADD_TAB_TYPE'], $table['PRO_UID'], $table['ADD_TAB_GRID'], $table['ADD_TAB_UID']);
                $result->message = G::LoadTranslation("ID_THE_REPORT_TABLE_IS_REGENERATING_PLEASE_COME_BACK_IN_A_FEW_MINUTES");
            } catch (Exception $e) {
                $message = 'Generation of data report could not be completed';
                $context = [
                    'proUid' => $table['PRO_UID'],
                    'tableName' => $table['ADD_TAB_NAME'],
                    'message' => $e->getMessage()
                ];
                Log::channel(':dataReport')->critical($message, Bootstrap::context($context));

                $result->message = 'Generation of data report could not be completed. Please check the processmaker.log for more details.';
                $result->success = false;
            }
        } else {
            $result->message = 'Unable to retrieve the table for this id: ' . $httpData->id . '.';
            $result->success = false;
        }
        return $result;
    }

    /**
     * Get all dynaform fields from a process (without grid fields)
     *
     * @param $proUid
     * @param $type [values:xmlform/grid]
     */
    public function _getDynafields2 ($proUid, $type = 'xmlform')
    {
        require_once 'classes/model/Dynaform.php';
        $fields = array ();
        $fieldsNames = array ();

        $oCriteria = new Criteria( 'workflow' );
        $oCriteria->addSelectColumn( DynaformPeer::DYN_FILENAME );
        $oCriteria->add( DynaformPeer::PRO_UID, $proUid );
        $oCriteria->add( DynaformPeer::DYN_TYPE, $type );
        $oDataset = DynaformPeer::doSelectRS( $oCriteria );
        $oDataset->setFetchmode( ResultSet::FETCHMODE_ASSOC );
        $oDataset->next();

        $excludeFieldsList = array ('title','subtitle','link','file','button','reset','submit','listbox','checkgroup','grid','javascript'
        );

        $labelFieldsTypeList = array ('dropdown','checkbox','radiogroup','yesno'
        );

        while ($aRow = $oDataset->getRow()) {
            if (file_exists( PATH_DYNAFORM . PATH_SEP . $aRow['DYN_FILENAME'] . '.xml' )) {
                $G_FORM = new Form( $aRow['DYN_FILENAME'], PATH_DYNAFORM, SYS_LANG );

                if ($G_FORM->type == 'xmlform' || $G_FORM->type == '') {
                    foreach ($G_FORM->fields as $fieldName => $fieldNode) {
                        if (! in_array( $fieldNode->type, $excludeFieldsList ) && ! in_array( $fieldName, $fieldsNames )) {
                            $fields[] = array ('name' => $fieldName,'type' => $fieldNode->type,'label' => $fieldNode->label
                            );
                            $fieldsNames[] = $fieldName;

                            if (in_array( $fieldNode->type, $labelFieldsTypeList ) && ! in_array( $fieldName . '_label', $fieldsNames )) {
                                $fields[] = array ('name' => $fieldName . '_label','type' => $fieldNode->type,'label' => $fieldNode->label . '_label'
                                );
                                $fieldsNames[] = $fieldName;
                            }
                        }
                    }
                }
            }
            $oDataset->next();
        }

        return $fields;
    }

    /**
     * Get fields from all forms in a process
     *
     * @param string $proUid
     * @param string $type
     * @param int $start
     * @param int $limit
     * @param string $filter
     * @return array
     */
    public function _getDynafields($proUid, $type = 'xmlform', $start = null, $limit = null, $filter = null)
    {
        // Cache session flag
        $cache = 1;

        // If the fields aren't in session cache, we need to get them
        if (!isset($_SESSION['_cache_pmtables']) || (isset($_SESSION['_cache_pmtables']) && $_SESSION['_cache_pmtables']['pro_uid'] != $proUid) ||
            (isset($_SESSION['_cache_pmtables']) && $_SESSION['_cache_pmtables']['dyn_uid'] != $this->dynUid)
        ) {
            // Initialize variables
            $cache = 0;
            $fields = [];
            $fieldsNames = [];

            // If exist the process in BPMN Projects table, is a process created in version 3.x
            $bpmn = new Bpmn();
            if ($bpmn->exists($proUid)) {
                switch ($type) {
                    case 'xmlform':
                        // Initialize variables
                        $arrayDataTypeToExclude = ['array', 'grid'];
                        $arrayTypeToExclude = [
                            'multipleFile',
                            'title',
                            'subtitle',
                            'link',
                            'file',
                            'button',
                            'reset',
                            'submit',
                            'listbox',
                            'grid',
                            'array',
                            'javascript',
                            'location',
                            'scannerCode'
                        ];
                        $arrayControlSupported = [];
                        $dynaFormNotAllowedVariables = $this->getDynaformVariables($proUid, $arrayTypeToExclude, false);
                        $index = 0;

                        // Get all supported controls
                        $dynaFormAllControl = $this->getDynaformVariables($proUid, $arrayTypeToExclude, true, 'DATA');
                        foreach ($dynaFormAllControl as $value) {
                            $arrayControl = array_change_key_case($value, CASE_UPPER);
                            if (isset($arrayControl['DATATYPE']) && isset($arrayControl['TYPE'])) {
                                if (!in_array($arrayControl['DATATYPE'], $arrayDataTypeToExclude) &&
                                    !in_array($arrayControl['TYPE'], $arrayTypeToExclude)
                                ) {
                                    $arrayControlSupported[$arrayControl['VAR_UID']] = $arrayControl['TYPE'];
                                }
                            }
                        }

                        // Get all process variables
                        $records = ProcessVariables::query()->select(['VAR_UID', 'VAR_NAME', 'VAR_FIELD_TYPE'])
                            ->where('PRJ_UID', '=', $proUid)->get()->toArray();
                        foreach ($records as $record) {
                            if (!in_array($record['VAR_NAME'], $dynaFormNotAllowedVariables) &&
                                !in_array($record['VAR_FIELD_TYPE'], $arrayTypeToExclude) &&
                                !in_array($record['VAR_NAME'], $fieldsNames)
                            ) {
                                $fields[] = [
                                    'FIELD_UID' => $record['VAR_NAME'] . '-' . $record['VAR_FIELD_TYPE'],
                                    'FIELD_NAME' => $record['VAR_NAME'],
                                    'FIELD_VALIDATE' => 'any',
                                    '_index' => $index++,
                                    '_isset' => true
                                ];

                                $fieldsNames[] = $record['VAR_NAME'];
                            }

                            if (isset($arrayControlSupported[$record['VAR_UID']]) &&
                                !in_array($record['VAR_NAME'] . '_label', $fieldsNames)
                            ) {
                                $fields[] = [
                                    'FIELD_UID' => $record['VAR_NAME'] . '_label' . '-' . $arrayControlSupported[$record['VAR_UID']],
                                    'FIELD_NAME' => $record['VAR_NAME'] . '_label',
                                    'FIELD_VALIDATE' => 'any',
                                    '_index' => $index++,
                                    '_isset' => true
                                ];

                                $fieldsNames[] = $record['VAR_NAME'] . '_label';
                            }
                        }
                        break;
                    case 'grid':
                        // Initialize variables
                        $dynaFormUid = $this->dynUid;
                        $gridId = $this->gridId;

                        // Get form information
                        $dynaForm = new DynaForm();
                        $arrayDynaFormData = $dynaForm->getDynaFormRecordByPk($dynaFormUid, [], false);

                        // If form exists, get grids and fields
                        if ($arrayDynaFormData !== false) {
                            $arrayGrid = PmDynaform::getGridsAndFields($arrayDynaFormData['DYN_CONTENT']);

                            if ($arrayGrid !== false && isset($arrayGrid[$gridId])) {
                                $grid = $arrayGrid[$gridId];

                                $arrayValidTypes = [
                                    'text' => ['type' => 'text', 'label' => false],
                                    'textarea' => ['type' => 'textarea', 'label' => false],
                                    'dropdown' => ['type' => 'dropdown', 'label' => true],
                                    'checkbox' => ['type' => 'checkbox', 'label' => false],
                                    'datetime' => ['type' => 'date', 'label' => false],
                                    'suggest' => ['type' => 'suggest', 'label' => false],
                                    'hidden' => ['type' => 'hidden', 'label' => false]
                                ];

                                $index = 0;

                                foreach ($grid->columns as $value) {
                                    $field = $value;

                                    if (isset($field->type) && isset($arrayValidTypes[$field->type]) &&
                                        isset($field->id) && $field->id != '' && isset($field->name) && $field->name != ''
                                    ) {
                                        if (!in_array($field->id, $fieldsNames)) {
                                            $fields[] = [
                                                'FIELD_UID' => $field->id . '-' . $arrayValidTypes[$field->type]['type'],
                                                'FIELD_NAME' => $field->id,
                                                'FIELD_VALIDATE' => 'any',
                                                '_index' => $index++,
                                                '_isset' => true
                                            ];

                                            $fieldsNames[] = $field->id;
                                        }

                                        if ($arrayValidTypes[$field->type]['label'] &&
                                            !in_array($field->id . '_label', $fieldsNames)
                                        ) {
                                            $fields[] = [
                                                'FIELD_UID' => $field->id . '_label' . '-' . $arrayValidTypes[$field->type]['type'],
                                                'FIELD_NAME' => $field->id . '_label',
                                                'FIELD_VALIDATE' => 'any',
                                                '_index' => $index++,
                                                '_isset' => true
                                            ];

                                            $fieldsNames[] = $field->id . '_label';
                                        }
                                    }
                                }
                            }
                        }
                        break;
                }
            } else {
                // Initialize variables
                $excludeFieldsList = [
                    'multipleFile',
                    'title',
                    'subtitle',
                    'link',
                    'file',
                    'button',
                    'reset',
                    'submit',
                    'listbox',
                    'checkgroup',
                    'grid',
                    'javascript',
                    'location',
                    'scannerCode',
                    'array'
                ];
                $labelFieldsTypeList = ['dropdown', 'radiogroup'];
                $index = 0;

                // Get all forms from the process
                $query = DynaformModel::query()->select(['DYN_FILENAME'])->where('PRO_UID', '=', $proUid)
                    ->where('DYN_TYPE', '=', $type);
                if (isset($this->dynUid)) {
                    $query->where('DYN_UID', '=', $this->dynUid);
                }
                $rows = $query->get()->toArray();

                foreach ($rows as $row) {
                    // If exists the XML file related exist, we need to parse to extract all fields
                    if (file_exists(PATH_DYNAFORM . PATH_SEP . $row['DYN_FILENAME'] . '.xml')) {
                        $dynaFormHandler = new DynaformHandler(PATH_DYNAFORM . $row['DYN_FILENAME'] . '.xml');
                        $nodeFieldsList = $dynaFormHandler->getFields();

                        foreach ($nodeFieldsList as $node) {
                            $arrayNode = $dynaFormHandler->getArray($node);
                            $fieldName = $arrayNode['__nodeName__'];
                            $fieldType = isset($arrayNode['type']) ? $arrayNode['type'] : '';
                            $fieldValidate = (isset($arrayNode['validate'])) ? $arrayNode['validate'] : '';

                            if (!in_array($fieldType, $excludeFieldsList) && !in_array($fieldName, $fieldsNames)) {
                                $fields[] = [
                                    'FIELD_UID' => $fieldName . '-' . $fieldType,
                                    'FIELD_NAME' => $fieldName,
                                    'FIELD_VALIDATE' => $fieldValidate,
                                    '_index' => $index++,
                                    '_isset' => true
                                ];
                                $fieldsNames[] = $fieldName;

                                if (in_array($fieldType, $labelFieldsTypeList) && !in_array($fieldName . '_label',
                                        $fieldsNames)
                                ) {
                                    $fields[] = [
                                        'FIELD_UID' => $fieldName . '_label' . '-' . $fieldType,
                                        'FIELD_NAME' => $fieldName . '_label',
                                        'FIELD_VALIDATE' => $fieldValidate,
                                        '_index' => $index++,
                                        '_isset' => true
                                    ];
                                    $fieldsNames[] = $fieldName;
                                }
                            }
                        }
                    }
                }
            }

            // Sort fields
            sort($fields);

            // If the report table already exists, get all information of the report table
            $fieldsEdit = [];
            if (isset($_SESSION['ADD_TAB_UID'])) {
                $additionalTables = new AdditionalTables();
                $table = $additionalTables->load($_SESSION['ADD_TAB_UID'], true);

                foreach ($table['FIELDS'] as $i => $field) {
                    array_push($fieldsEdit, $field['FLD_DYN_NAME']);
                }
            }

            // Map response array
            $indexes = [];
            foreach ($fields as $i => $field) {
                $fields[$i]['_index'] = $i;
                $indexes[$field['FIELD_NAME']] = $i;

                if (in_array($field['FIELD_NAME'], $fieldsEdit)) {
                    $fields[$i]['_isset'] = false;
                }
            }

            // Build the cache session (only for processes created in version 2.x)
            $_SESSION['_cache_pmtables'] = [];
            $_SESSION['_cache_pmtables']['pro_uid'] = $proUid;
            $_SESSION['_cache_pmtables']['dyn_uid'] = $this->dynUid;
            $_SESSION['_cache_pmtables']['rows'] = $fields;
            $_SESSION['_cache_pmtables']['count'] = count($fields);
            $_SESSION['_cache_pmtables']['indexes'] = $indexes;
        }

        // Filter the fields
        $tmp = [];
        foreach ($_SESSION['_cache_pmtables']['rows'] as $i => $row) {
            if (isset($filter) && $filter != '') {
                if ($row['_isset'] && stripos($row['FIELD_NAME'], $filter) !== false) {
                    $tmp[] = $row;
                }
            } else {
                if ($row['_isset']) {
                    $tmp[] = $row;
                }
            }
        }

        // Get the slice requested
        $fields = array_slice($tmp, $start, $limit);

        // Return data
        return [
            'cache' => $cache,
            'count' => count($tmp),
            'rows' => $fields
        ];
    }

    /**
     * Get all dynaform grid fields from a process
     *
     * @param $proUid
     * @param $gridId
     */
    public function _getGridDynafields ($proUid, $gridId)
    {
        $fields = array ();
        $fieldsNames = array ();
        $excludeFieldsList = array ('title','subtitle','link','file','button','reset','submit','listbox','checkgroup','grid','javascript'
        );

        $labelFieldsTypeList = array ('dropdown','checkbox','radiogroup','yesno'
        );

        $G_FORM = new Form( $proUid . '/' . $gridId, PATH_DYNAFORM, SYS_LANG, false );

        if ($G_FORM->type == 'grid') {
            foreach ($G_FORM->fields as $fieldName => $fieldNode) {
                if (! in_array( $fieldNode->type, $excludeFieldsList ) && ! in_array( $fieldName, $fieldsNames )) {
                    $fields[] = array ('name' => $fieldName,'type' => $fieldNode->type,'label' => $fieldNode->label
                    );
                    $fieldsNames[] = $fieldName;

                    if (in_array( $fieldNode->type, $labelFieldsTypeList ) && ! in_array( $fieldName . '_label', $fieldsNames )) {
                        $fields[] = array ('name' => $fieldName . '_label','type' => $fieldNode->type,'label' => $fieldNode->label . '_label'
                        );
                        $fieldsNames[] = $fieldName;
                    }
                }
            }
        }

        return $fields;
    }

    /**
     * Get all dynaform fields inside all grids from a process
     *
     * @param $proUid
     */
    public function _getGridFields ($proUid)
    {
        try {

            $bpmn = new \ProcessMaker\Project\Bpmn();
            $flagIsBpmn = $bpmn->exists($proUid);

            $arrayField = [];
            $arrayFieldName = [];

            $delimiter = DBAdapter::getStringDelimiter();

            $criteria = new Criteria('workflow');

            $criteria->addSelectColumn(DynaformPeer::DYN_UID);
            $criteria->addSelectColumn(DynaformPeer::DYN_FILENAME);
            $criteria->addSelectColumn(DynaformPeer::DYN_CONTENT);
            $criteria->addSelectColumn(DynaformPeer::DYN_TITLE);
            $criteria->add(DynaformPeer::PRO_UID, $proUid, Criteria::EQUAL);
            $criteria->add(DynaformPeer::DYN_TYPE, 'xmlform', Criteria::EQUAL);

            $rsCriteria = DynaformPeer::doSelectRS($criteria);
            $rsCriteria->setFetchmode(ResultSet::FETCHMODE_ASSOC);

            while ($rsCriteria->next()) {
                $record = $rsCriteria->getRow();

                if ($flagIsBpmn) {
                    $arrayGrid = PmDynaform::getGridsAndFields($record['DYN_CONTENT']);

                    if ($arrayGrid !== false) {
                        foreach ($arrayGrid as $value) {
                            $grid = $value;

                            $arrayField[] = [
                                'uid'      => $record['DYN_UID'], //dynaFormUid
                                'gridId'   => $grid->id,
                                'gridName' => $grid->id . ' (' . $record['DYN_TITLE'] . ')'
                            ];
                        }
                    }
                } else {
                    $dynaformHandler = new DynaformHandler(PATH_DYNAFORM . $record['DYN_FILENAME'] . '.xml');
                    $nodeFieldsList = $dynaformHandler->getFields();

                    foreach ($nodeFieldsList as $node) {
                        $arrayNode = $dynaformHandler->getArray($node);
                        $fieldName = $arrayNode['__nodeName__'];
                        $fieldType = $arrayNode['type'];

                        if ($fieldType == 'grid') {
                            if (!in_array($fieldName, $arrayFieldName)) {
                                $arrayField[] = [
                                    'uid'      => str_replace($proUid . '/', '', $arrayNode['xmlgrid']), //dynaFormUid (Grid)
                                    'gridId'   => $fieldName,
                                    'gridName' => $fieldName
                                ];

                                $arrayFieldName[] = $fieldName;
                            }
                        }
                    }
                }
            }

            //Return
            return $arrayField;
        } catch (Exception $e) {
            throw $e;
        }
    }

    /**
     * Get all dynaform variables
     *
     * @param $sProcessUID
     */
    public function getDynaformVariables($sProcessUID, $excludeFieldsList, $allowed = true, $option = "VARIABLE")
    {
        $dynaformVariables = array();
        $oC = new Criteria( 'workflow' );
        $oC->addSelectColumn( DynaformPeer::DYN_CONTENT );
        $oC->add( DynaformPeer::PRO_UID, $sProcessUID );
        $oData = DynaformPeer::doSelectRS( $oC );
        $oData->setFetchmode( ResultSet::FETCHMODE_ASSOC );
        $oData->next();
        while ($aRowd = $oData->getRow()) {
            $dynaform = G::json_decode($aRowd['DYN_CONTENT'],true);
            if(is_array($dynaform) && sizeof($dynaform)) {
                $items = $dynaform['items'][0]['items'];
                foreach($items as $key => $val){
                    foreach($val as $column) {
                        if($allowed) {
                            if(isset($column['type']) && !in_array( $column['type'], $excludeFieldsList )){
                                switch ($option) {
                                    case "VARIABLE":
                                        if (array_key_exists("variable", $column)) {
                                            if($column["variable"] != "") {
                                                $dynaformVariables[] = $column["variable"];
                                            }
                                        }
                                        break;
                                    case "DATA":
                                        $dynaformVariables[] = $column;
                                        break;
                                }
                            }
                        } else {
                            if(isset($column['type']) && in_array( $column['type'], $excludeFieldsList )){
                                switch ($option) {
                                    case "VARIABLE":
                                        if (array_key_exists("variable", $column)) {
                                            if($column["variable"] != "") {
                                                $dynaformVariables[] = $column["variable"];
                                            }
                                        }
                                        break;
                                    case "DATA":
                                        $dynaformVariables[] = $column;
                                        break;
                                }
                            }
                        }
                    }
                }
            }
            $oData->next();
        }

        if ($option == "VARIABLE") {
            return array_unique($dynaformVariables);
        } else {
            return $dynaformVariables;
        }
    }
}
