File "do-install.php"

Full Path: /home/humancap/cl.humancap.com.my/admin/secure/class/secure/do-install.php
File size: 43.7 KB
MIME-type: text/x-php
Charset: utf-8

<?php
// phpcs:disable PSR1.Files.SideEffects
use secure\Secure;
use phpformbuilder\Form;
use phpformbuilder\Validator\Validator;
use phpformbuilder\database\DB;
use generator\Generator;
use crud\ElementsUtilities;

session_start();

function getUsersTableQuery($pdo_driver, $users_table)
{
    switch ($pdo_driver) {
        case 'firebird':
            $users_table = strtoupper($users_table);
            $qry = 'CREATE TABLE ' . $users_table . ' (';
            $qry .= '    ID SMALLINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,';
            $qry .= '    PROFILES_ID SMALLINT NOT NULL,';
            $qry .= '    NAME VARCHAR(50) NOT NULL,';
            $qry .= '    FIRSTNAME VARCHAR(50) NOT NULL,';
            $qry .= '    ADDRESS VARCHAR(50) DEFAULT NULL,';
            $qry .= '    CITY VARCHAR(50) DEFAULT NULL,';
            $qry .= '    ZIP_CODE VARCHAR(20),';
            $qry .= '    EMAIL VARCHAR(50) NOT NULL,';
            $qry .= '    PHONE VARCHAR(20),';
            $qry .= '    MOBILE_PHONE VARCHAR(20),';
            $qry .= '    PASSWORD VARCHAR(255) NOT NULL,';
            $qry .= '    ACTIVE BOOLEAN DEFAULT false NOT NULL,';
            $qry .= '    CONSTRAINT ' . $users_table . '_PRIMARY_KEY PRIMARY KEY (ID),';
            $qry .= '    CONSTRAINT ' . $users_table . '_EMAIL_UNIQUE UNIQUE (EMAIL)';
            $qry .= ');';
            break;

        case 'oci':
            $users_table = strtoupper($users_table);
            $qry = 'CREATE TABLE ' . $users_table . ' (';
            $qry .= '    ID SMALLINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,';
            $qry .= '    PROFILES_ID SMALLINT NOT NULL,';
            $qry .= '    NAME VARCHAR(50) NOT NULL,';
            $qry .= '    FIRSTNAME VARCHAR(50) NOT NULL,';
            $qry .= '    ADDRESS VARCHAR(50) DEFAULT NULL,';
            $qry .= '    CITY VARCHAR(50) DEFAULT NULL,';
            $qry .= '    ZIP_CODE VARCHAR(20),';
            $qry .= '    EMAIL VARCHAR(50) NOT NULL,';
            $qry .= '    PHONE VARCHAR(20),';
            $qry .= '    MOBILE_PHONE VARCHAR(20),';
            $qry .= '    PASSWORD VARCHAR(255) NOT NULL,';
            $qry .= '    ACTIVE SMALLINT DEFAULT 0 NOT NULL,';
            $qry .= '    CONSTRAINT ' . $users_table . '_PRIMARY_KEY PRIMARY KEY (ID),';
            $qry .= '    CONSTRAINT ' . $users_table . '_EMAIL_UNIQUE UNIQUE (EMAIL)';
            $qry .= ')';
            break;

        case 'pgsql':
            $qry = 'CREATE TABLE ' . $users_table . ' (';
            $qry .= '    id serial4 NOT NULL,';
            $qry .= '    profiles_id int2 NOT NULL,';
            $qry .= '    name VARCHAR(50) NOT NULL,';
            $qry .= '    firstname VARCHAR(50) NOT NULL,';
            $qry .= '    address VARCHAR(50) DEFAULT NULL,';
            $qry .= '    city VARCHAR(50) DEFAULT NULL,';
            $qry .= '    zip_code VARCHAR(20),';
            $qry .= '    email VARCHAR(50) NOT NULL,';
            $qry .= '    phone VARCHAR(20),';
            $qry .= '    mobile_phone VARCHAR(20),';
            $qry .= '    password VARCHAR(255) NOT NULL,';
            $qry .= '    active int4 DEFAULT 0 NOT NULL,';
            $qry .= '    CONSTRAINT ' . $users_table . '_primary_key PRIMARY KEY (id),';
            $qry .= '    CONSTRAINT ' . $users_table . '_email_unique UNIQUE (email)';
            $qry .= ');';
            break;

            // mysql
        default:
            $qry = 'CREATE TABLE IF NOT EXISTS `' . $users_table . '` (';
            $qry .= '    `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,';
            $qry .= '    `profiles_id` int(11) NOT NULL,';
            $qry .= '    `name` varchar(50) NOT NULL,';
            $qry .= '    `firstname` varchar(50) NOT NULL,';
            $qry .= '    `address` varchar(50) DEFAULT NULL,';
            $qry .= '    `city` varchar(50) DEFAULT NULL,';
            $qry .= '    `zip_code` varchar(20),';
            $qry .= '    `email` varchar(50) NOT NULL,';
            $qry .= '    `phone` varchar(20) NULL,';
            $qry .= '    `mobile_phone` varchar(20) DEFAULT NULL,';
            $qry .= '    `password` varchar(255) NOT NULL,';
            $qry .= '    `active` boolean NOT NULL COMMENT \'Boolean\',';
            $qry .= '    PRIMARY KEY (`id`),';
            $qry .= '    UNIQUE KEY `email_UNIQUE` (`email`)';
            $qry .= ') ENGINE=InnoDB DEFAULT CHARSET=utf8;';
            break;
    }

    return $qry;
}

function getUsersProfilesTableQuery($pdo_driver, $tables, $users_table)
{
    switch ($pdo_driver) {
        case 'firebird':
            $users_table = strtoupper($users_table);
            $qry = 'CREATE TABLE ' . $users_table . '_PROFILES (';
            $qry .= '    ID SMALLINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,';
            $qry .= '    PROFILE_NAME VARCHAR(100) NOT NULL,';
            foreach ($tables as $t) {
                $t = strtoupper($t);
                $qry .= '    R_' . $t . ' SMALLINT DEFAULT 0 NOT NULL,';
                $qry .= '    U_' . $t . ' SMALLINT DEFAULT 0 NOT NULL,';
                $qry .= '    CD_' . $t . ' SMALLINT DEFAULT 0 NOT NULL,';
                $qry .= '    CQ_' . $t . ' VARCHAR(255) DEFAULT null,';
            }
            $qry .= '    CONSTRAINT ' . $users_table . '_PRPK PRIMARY KEY (ID),';
            $qry .= '    CONSTRAINT ' . $users_table . '_PRPN_UQ UNIQUE (PROFILE_NAME)';
            $qry .= ');';
            break;

        case 'oci':
            $users_table = strtoupper($users_table);
            $qry = 'CREATE TABLE ' . $users_table . '_PROFILES (';
            $qry .= '    ID SMALLINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,';
            $qry .= '    PROFILE_NAME VARCHAR2(100) NOT NULL,';
            foreach ($tables as $t) {
                $t = strtoupper($t);
                $qry .= '    R_' . $t . ' SMALLINT DEFAULT 0 NOT NULL,';
                $qry .= '    U_' . $t . ' SMALLINT DEFAULT 0 NOT NULL,';
                $qry .= '    CD_' . $t . ' SMALLINT DEFAULT 0 NOT NULL,';
                $qry .= '    CQ_' . $t . ' VARCHAR(255) DEFAULT null,';
            }
            $qry .= '    CONSTRAINT ' . $users_table . '_PRPK PRIMARY KEY (ID),';
            $qry .= '    CONSTRAINT ' . $users_table . '_PRPN_UQ UNIQUE (PROFILE_NAME)';
            $qry .= ')';
            break;

        case 'pgsql':
            $users_table = strtoupper($users_table);
            $qry = 'CREATE TABLE ' . $users_table . '_profiles (';
            $qry .= '    id serial4 NOT NULL,';
            $qry .= '    profile_name VARCHAR(100) NOT NULL,';
            foreach ($tables as $t) {
                $qry .= '    r_' . $t . ' int4 DEFAULT 0 NOT NULL,';
                $qry .= '    u_' . $t . ' int4 DEFAULT 0 NOT NULL,';
                $qry .= '    cd_' . $t . ' int4 DEFAULT 0 NOT NULL,';
                $qry .= '    cq_' . $t . ' VARCHAR(255) DEFAULT null,';
            }
            $qry .= '    CONSTRAINT ' . $users_table . '_PRPK PRIMARY KEY (id),';
            $qry .= '    CONSTRAINT ' . $users_table . '_PRPN_UQ UNIQUE (profile_name)';
            $qry .= ')';
            break;

            // mysql
        default:
            $qry = 'CREATE TABLE IF NOT EXISTS `' . $users_table . '_profiles` (';
            $qry .= '  `id` int(11) NOT NULL AUTO_INCREMENT,';
            $qry .= '  `profile_name` varchar(100) NOT NULL,';
            foreach ($tables as $t) {
                $qry .= '  `r_' . $t . '` tinyint(1) NOT NULL DEFAULT 0,';
                $qry .= '  `u_' . $t . '` tinyint(1) NOT NULL DEFAULT 0,';
                $qry .= '  `cd_' . $t . '` tinyint(1) NOT NULL DEFAULT 0,';
                $qry .= '  `cq_' . $t . '` VARCHAR(255) NULL DEFAULT NULL,';
            }
            $qry .= '  PRIMARY KEY (`id`),';
            $qry .= '  UNIQUE KEY `profile_name_UNIQUE` (`profile_name`)';
            $qry .= ') ENGINE=InnoDB DEFAULT CHARSET=utf8;';
            break;
    }

    return $qry;
}

function getConstraintQuery($pdo_driver, $users_table)
{
    switch ($pdo_driver) {
        case 'firebird':
            $users_table = strtoupper($users_table);
            $qry = 'ALTER TABLE ' . $users_table . ' ADD CONSTRAINT ' . $users_table . '_FK FOREIGN KEY (PROFILES_ID) REFERENCES ' . $users_table . '_PROFILES(ID);';
            break;

        case 'oci':
            $users_table = strtoupper($users_table);
            $qry = 'ALTER TABLE ' . $users_table . ' ADD CONSTRAINT ' . $users_table . '_FK FOREIGN KEY (PROFILES_ID) REFERENCES ' . $users_table . '_PROFILES(ID)';
            break;

        case 'pgsql':
            $qry = 'ALTER TABLE ' . $users_table . ' ADD CONSTRAINT fk_' . $users_table . '_' . $users_table . '_profiles FOREIGN KEY (profiles_id) REFERENCES ' . $users_table . '_profiles (id) ON DELETE NO ACTION ON UPDATE NO ACTION;';
            break;

            // mysql
        default:
            $qry = 'ALTER TABLE `' . $users_table . '` ADD CONSTRAINT `fk_' . $users_table . '_' . $users_table . '_profiles` FOREIGN KEY (`profiles_id`) REFERENCES `' . $users_table . '_profiles` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION;';
            break;
    }

    return $qry;
}

if (!file_exists("install.lock")) {
    if (!file_exists('../../../conf/conf.php')) {
        exit('Configuration file not found (1)');
    }
    include_once '../../../conf/conf.php';

    if (preg_match('`([a-zA-Z0-9_]+)\.(?:[a-zA-Z]+)$`', DB_NAME, $out)) {
        // for firebird ; e.g.: C:/Users/folder/DATABASES/firebird/PHPCG_TEST.FDB
        $database_name = \strtolower($out[1]);
    } else {
        $database_name = DB_NAME;
    }

    // We don't use the DEBUG_DB_QUERIES constant here
    // because the INSERTs would be only simulated
    // which may lead to some confusion (the users & users_profiles tables would be created with no records).
    // To debug, set the DEBUG_DB_QUERIES_FOR_INSTALL to "true".
    define('DEBUG_DB_QUERIES_FOR_INSTALL', false);

    /* =============================================
    Get default tables used in admin
    ============================================= */

    if (!isset($_POST['form-admin-setup'])) {
        if (file_exists(ADMIN_DIR . 'crud-data/db-data.json')) {
            $_SESSION['form-admin-setup'] = array();
            $_SESSION['form-admin-setup']['tables'] = array();
            $json    = file_get_contents(ADMIN_DIR . 'crud-data/db-data.json');
            $db_data = json_decode($json, true);
            if (empty($db_data)) {
                $_SESSION['msg'] = '<div class="alert alert-danger has-icon mb-5"><p class="lead text-semibold">You must first generate your admin dashboard and then install the authentication module.</p><p>The authentication module requires the admin dashboard to be built to assign rights to users on the tables used.</p></div>';
            }
            $tables = array_keys($db_data);
            foreach ($tables as $t) {
                $_SESSION['form-admin-setup']['tables'][] = $t;
            }
            if (PDO_DRIVER === 'firebird') {
                // Firebird table names are limited to 31 characters (!)
                $table_name_too_long = array();
                foreach ($tables as $t) {
                    if (strlen($t) > 28) {
                        $table_name_too_long[] = $t;
                    }
                }
                if (!empty($table_name_too_long)) {
                    $_SESSION['msg'] .= '<div class="alert alert-danger has-icon mb-5"><p class="lead text-semibold">Firebird limits table names to 31 characters.<br>The authentication module uses 3 characters, which limits the table names to a maximum of 28 characters.<br>The following table names are too long, so you must rename them before installing the authentication module:</p><p>' . implode('<br>', $table_name_too_long) . '</p></div>';
                }
            }
        }
    }

    /* =============================================
    Register Posted Values
    ============================================= */

    if (isset($_POST['form-admin-setup'])) {
        include_once CLASS_DIR . 'phpformbuilder/Validator/Validator.php';
        include_once CLASS_DIR . 'phpformbuilder/Validator/Exception.php';
        $validator = new Validator($_POST);
        $required = array('tables.0', 'users_table', 'name', 'firstname', 'email', 'pass');
        foreach ($required as $required) {
            $validator->required()->validate($required);
        }

        // registration table name is reserved & unallowed
        if (\strtolower($_POST['users_table']) == \strtolower(PHPCG_USERDATA_TABLE)) {
            $validator->maxLength(-1, USER_TABLE_ALREADY_EXISTS)->validate('users_table');
        }

        $validator->hasPattern('`^[a-z_]+$`', WRONG_PATTERN)->validate('users_table');
        $validator->email()->validate('email');
        $validator->minLength(6)->validate('pass');
        $validator->hasLowercase()->validate('pass');
        $validator->hasUppercase()->validate('pass');

        // check for errors
        if ($validator->hasErrors()) {
            $_SESSION['errors']['form-admin-setup'] = $validator->getAllErrors();
        } else {
            // if NOT https://www.phpcrudgenerator.com (AUTH module is disabled in demo)
            if (!DEMO) {
                /* =============================================
                Create users & profiles tables
                ============================================= */

                $db = new DB(DEBUG);

                include_once ADMIN_DIR . 'secure/class/secure/Secure.php';

                $users_table = $_POST['users_table'];
                $users_table_profiles = $users_table . '_profiles';
                if (PDO_DRIVER === 'firebird' || PDO_DRIVER === 'oci') {
                    $users_table = strtoupper($users_table);
                    $users_table_profiles = strtoupper($users_table_profiles);
                }

                try {
                    $tables    = $_POST['tables'];
                    $db_tables = $db->getTables();
                    foreach ($tables as $t) {
                        if (!in_array($t, $db_tables)) {
                            exit('Table ' . $t . ' not found');
                        }
                    }

                    /* add users table to administrable tables */

                    if (!in_array($users_table, $tables)) {
                        $tables[] = $users_table;
                    }

                    /* add profiles table to administrable tables */

                    if (!in_array($users_table_profiles, $tables)) {
                        $tables[] = $users_table_profiles;
                    }

                    /* test if user table & user_profiles table already exist */

                    $user_table_already_exists          = false;
                    $user_profiles_table_already_exists = false;

                    if (in_array($users_table, $db_tables)) {
                        $user_table_already_exists = true;
                    }

                    if (in_array($users_table_profiles, $db_tables)) {
                        $user_profiles_table_already_exists = true;
                    }

                    /* create profiles table if not exists */

                    if (!$user_profiles_table_already_exists) {
                        $qry = getUsersProfilesTableQuery(PDO_DRIVER, $tables, $users_table);

                        if (!$db->execute($qry, false, DEBUG_DB_QUERIES_FOR_INSTALL)) {
                            throw new \Exception($users_table . '_profiles ' . QUERY_FAILED);
                        }
                    }

                    /* create users table if not exists */

                    if (!$user_table_already_exists) {
                        $qry = getUsersTableQuery(PDO_DRIVER, $users_table);

                        if (!$db->execute($qry, false, DEBUG_DB_QUERIES_FOR_INSTALL)) {
                            throw new \Exception($users_table . ' ' . QUERY_FAILED);
                        }
                    }

                    if (!$user_profiles_table_already_exists && !$user_table_already_exists) {
                        // add constraints
                        $qry = getConstraintQuery($db->getPdoDriver(), $users_table);

                        if (!$db->execute($qry, false, DEBUG_DB_QUERIES_FOR_INSTALL)) {
                            throw new \Exception($users_table . ' ADD CONSTRAINT ' . QUERY_FAILED);
                        }

                        // Begin the transaction after the CREATE & ALTER statements (CREATE & ALTER are auto-committed)
                        $db->transactionBegin();

                        /* insert main administrator profile */

                        $insert = array();
                        // $insert['id']           = null;
                        $insert['profile_name'] = 'superadmin';

                        foreach ($tables as $t) {
                            $insert['r_' . $t]  = 2;
                            $insert['u_' . $t]  = 2;
                            $insert['cd_' . $t] = 2;
                            $insert['cq_' . $t] = '';
                        }

                        if (PDO_DRIVER === 'firebird' || PDO_DRIVER === 'oci') {
                            $insert = array_change_key_case($insert, CASE_UPPER);
                        }

                        if (!$db->insert($users_table_profiles, $insert, DEBUG_DB_QUERIES_FOR_INSTALL)) {
                            throw new \Exception($users_table_profiles . ' INSERT ' . QUERY_FAILED);
                        }

                        /* insert main administrator */

                        $profile_id = $db->getMaximumValue($users_table_profiles, 'id', DEBUG_DB_QUERIES_FOR_INSTALL);

                        $encrypted_pass = Secure::encrypt($_POST['pass']);

                        $insert = array();
                        // $insert['id']           = null;
                        $insert['profiles_id']  = $profile_id;
                        $insert['name']         = $_POST['name'];
                        $insert['firstname']    = $_POST['firstname'];
                        $insert['address']      = $_POST['address'];
                        $insert['city']         = $_POST['city'];
                        $insert['zip_code']     = $_POST['zip_code'];
                        $insert['email']        = $_POST['email'];
                        $insert['phone']        = $_POST['phone'];
                        $insert['mobile_phone'] = $_POST['mobile_phone'];
                        $insert['password']     = $encrypted_pass;
                        $insert['active']       = 1;

                        if (PDO_DRIVER === 'firebird' || PDO_DRIVER === 'oci') {
                            $insert = array_change_key_case($insert, CASE_UPPER);
                        }

                        if (!$db->insert($users_table, $insert, DEBUG_DB_QUERIES_FOR_INSTALL)) {
                            throw new \Exception($users_table . ' INSERT ' . QUERY_FAILED);
                        }
                    }

                    /* register users table name in conf file */

                    $file = ADMIN_DIR . 'secure/conf/conf.php';
                    $contents = '<?php define(\'USERS_TABLE\', \'' . $users_table . '\');' . "\n";
                    file_put_contents($file, $contents);

                    // commit and save the entire transaction
                    // only if no error has been thrown before

                    /* =============================================
                    All OK
                    ============================================= */

                    $db->transactionCommit();

                    // Generate Class, List & forms for users & profiles
                    include_once(GENERATOR_DIR . 'class/generator/Generator.php');
                    $generator = new Generator();
                    $generator->database = $database_name;

                    // reset relations
                    $generator->init();
                    $generator->tables = $db->getTables();

                    /* =============================================
                    profiles
                    ============================================= */

                    $generator->table        = $users_table_profiles;
                    $generator->table_icon   = 'fa-solid fa-user-shield';
                    $generator->table_label  = $generator->getLabel($generator->table);
                    $upperCamelCaseTable     = ElementsUtilities::upperCamelCase($generator->table);
                    $generator->item         = mb_strtolower($upperCamelCaseTable);
                    $generator->reset('columns');
                    $generator->getDbColumns();
                    $generator->registerColumnsProperties();
                    $generator->resetRelations();
                    $generator->registerRelations();

                    // users_profiles select values
                    $custom_read_update_values = array(
                        YES        => 2,
                        RESTRICTED => 1,
                        NO         => 0
                    );
                    $custom_create_delete_values = array(
                        YES        => 2,
                        RESTRICTED => 1,
                        NO         => 0
                    );
                    $custom_usertable_create_delete_values = array(
                        YES        => 2,
                        NO         => 0
                    );

                    $r = 'r_';
                    $u = 'u_';
                    $cd = 'cd_';
                    $cq = 'cq_';
                    if (PDO_DRIVER === 'firebird' || PDO_DRIVER === 'oci') {
                        $r  = strtoupper('r_');
                        $u  = strtoupper('u_');
                        $cd = strtoupper('cd_');
                        $cq = strtoupper('cq_');
                    }
                    foreach ($tables as $t) {
                        $generator->registerSelectValues($r . $t, 'custom_values', '', '', '', '', $custom_read_update_values, false);
                        $generator->registerSelectValues($u . $t, 'custom_values', '', '', '', '', $custom_read_update_values, false);
                        if ($t !== $users_table) {
                            $generator->registerSelectValues($cd . $t, 'custom_values', '', '', '', '', $custom_create_delete_values, false);
                        } else {
                            $generator->registerSelectValues($cd . $t, 'custom_values', '', '', '', '', $custom_usertable_create_delete_values, false);
                        }
                    }

                    $generator->columns['fields'] = array_map(
                        function ($value) {
                            $find = array('R ', 'U ', 'Cd ', 'Cq ');
                            $replace = array('Read ', 'Update ', 'Create/Delete ', 'Constraint query ');
                            return str_replace($find, $replace, $value);
                        },
                        $generator->columns['fields']
                    );

                    foreach ($tables as $t) {
                        $index = array_search($r . $t, $generator->columns['name']);
                        $generator->columns['field_type'][$index] = 'select';
                        $generator->columns['field_width'][$index] = '50% grouped';
                        $generator->columns['nested'][$index] = true;

                        $index = array_search($u . $t, $generator->columns['name']);
                        $generator->columns['field_type'][$index] = 'select';
                        $generator->columns['field_width'][$index] = '50% grouped';
                        $generator->columns['nested'][$index] = true;

                        $index = array_search($cd . $t, $generator->columns['name']);
                        $generator->columns['field_type'][$index] = 'select';
                        $generator->columns['field_width'][$index] = '50%';
                        $generator->columns['nested'][$index] = true;

                        $index = array_search($cq . $t, $generator->columns['name']);
                        $generator->columns['field_width'][$index] = '100%';
                        $generator->columns['nested'][$index] = true;
                        if ($t !== $users_table) {
                            $generator->columns['tooltip'][$index] = CONSTRAINT_QUERY_TIP;
                        } else {
                            $generator->columns['help_text'][$index] = CONSTRAINT_USERS_CREATE_HELPER;
                        }
                    }
                    $generator->field_delete_confirm_1 = 'profile_name';
                    $generator->field_delete_confirm_2 = '';

                    $json_data = array(
                        'list_options'           => $generator->list_options,
                        'columns'                => $generator->columns,
                        'external_columns'       => $generator->external_columns,
                        'field_delete_confirm_1' => $generator->field_delete_confirm_1,
                        'field_delete_confirm_2' => $generator->field_delete_confirm_2
                    );

                    // register table & columns properties in json file
                    $json = json_encode($json_data);
                    $generator->registerJson($generator->table . '.json', $json);

                    // db-data (admin/data/db-data.json)
                    $json                                      = file_get_contents(ADMIN_DIR . 'crud-data/db-data.json');
                    $db_data                                   = json_decode($json, true);
                    $table                                     = $generator->table;
                    $db_data[$table]['icon']                   = $generator->table_icon;
                    $db_data[$table]['field_delete_confirm_1'] = $generator->field_delete_confirm_1;
                    $db_data[$table]['field_delete_confirm_2'] = $generator->field_delete_confirm_2;
                    $db_data[$table]['fields']                 = $generator->columns['fields'];

                    $json = json_encode($db_data);
                    $dir = ADMIN_DIR . 'crud-data/';
                    $file = 'db-data.json';
                    $generator->registerAdminFile($dir, $file, $json);

                    $generator->action = 'build_paginated_list';
                    $generator->runBuild();
                    $generator->action = 'build_create_edit';
                    $generator->runBuild();

                    // edit admin/inc/forms/[userstable]profiles[-create|-edit].php
                    // for specific customization
                    include_once 'users-profiles-form-edit-customization.php';

                    /* =============================================
                    users
                    ============================================= */

                    $generator->table        = $users_table;
                    $generator->table_icon   = 'fa-solid fa-user';
                    $generator->table_label  = $generator->toReadable($generator->table);
                    $upperCamelCaseTable     = ElementsUtilities::upperCamelCase($generator->table);
                    $generator->item         = mb_strtolower($upperCamelCaseTable);
                    $generator->reset('columns');
                    $generator->getDbColumns();
                    $generator->registerColumnsProperties();

                    $id_col           = 'id';
                    $profiles_id_col  = 'profiles_id';
                    $profile_name     = 'profile_name';
                    $name             = 'name';
                    $email            = 'email';
                    $active           = 'active';
                    $password         = 'password';
                    if (PDO_DRIVER === 'firebird' || PDO_DRIVER === 'oci') {
                        $id_col           = 'ID';
                        $profiles_id_col  = 'PROFILES_ID';
                        $profile_name     = 'PROFILE_NAME';
                        $name             = 'NAME';
                        $email            = 'EMAIL';
                        $active           = 'ACTIVE';
                        $password         = 'PASSWORD';
                    }

                    $generator->registerSelectValues($profiles_id_col, 'from_table', $users_table_profiles, $id_col, $profile_name, '', '', false);

                    // users select values (profile id => name)
                    $index = array_search($profiles_id_col, $generator->columns['name']);
                    $generator->columns['relation'][$index] = array(
                        'target_table'  => $users_table_profiles,
                        'target_fields' => $profile_name,
                        'target_fields_display_values' => $profile_name
                    );
                    $generator->columns['fields'][$profiles_id_col] = PROFILE;
                    $generator->columns['field_type'][$index]    = 'select';
                    $generator->field_delete_confirm_1 = $email;
                    $generator->field_delete_confirm_2 = '';

                    // filters
                    $generator->list_options['filters'] = array(
                        array(
                            'filter_mode'     => 'simple',
                            'filter_A'        => $name,
                            'select_label'    => NAME,
                            'select_name'     => $name,
                            'option_text'     => $users_table . '.' . $name,
                            'fields'          => $users_table . '.' . $name,
                            'field_to_filter' => $users_table . '.' . $name,
                            'from'            => $users_table,
                            "from_table"      => $users_table,
                            "join_tables"     => [],
                            "join_queries"    => [],
                            'type'            => 'text',
                            'column'          => 4
                        ),
                        array(
                            'filter_mode'     => 'simple',
                            'filter_A'        => $email,
                            'select_label'    => EMAIL,
                            'select_name'     => $email,
                            'option_text'     => $users_table . '.' . $email,
                            'fields'          => $users_table . '.' . $email,
                            'field_to_filter' => $users_table . '.' . $email,
                            'from'            => $users_table,
                            "from_table"      => $users_table,
                            "join_tables"     => [],
                            "join_queries"    => [],
                            'type'            => 'text',
                            'column'          => 9
                        ),
                        array(
                            'filter_mode'     => 'simple',
                            'filter_A'        => $active,
                            'select_label'    => ENABLED,
                            'select_name'     => $active,
                            'option_text'     => $users_table . '.' . $active,
                            'fields'          => $users_table . '.' . $active,
                            'field_to_filter' => $users_table . '.' . $active,
                            'from'            => $users_table,
                            "from_table"      => $users_table,
                            "join_tables"     => [],
                            "join_queries"    => [],
                            'type'            => 'boolean',
                            'column'          => 13
                        )
                    );

                    // nested columns
                    $nested_columns = array('address', 'city', 'zip_code', 'phone', 'mobile_phone');
                    if (PDO_DRIVER === 'firebird' || PDO_DRIVER === 'oci') {
                        $nested_columns = array_map('strtoupper', $nested_columns);
                    }
                    foreach ($nested_columns as $nested) {
                        if ($nested_index = array_search($nested, $generator->columns['name'])) {
                            $generator->columns['nested'][$nested_index] = true;
                        }
                    }
                    // sorting
                    $sorting_columns = array('profiles_id', 'name', 'firstname', 'email', 'active');
                    if (PDO_DRIVER === 'firebird' || PDO_DRIVER === 'oci') {
                        $sorting_columns = array_map('strtoupper', $sorting_columns);
                    }
                    foreach ($sorting_columns as $sorting) {
                        if ($sorting_index = array_search($sorting, $generator->columns['name'])) {
                            $generator->columns['sorting'][$sorting_index] = true;
                        }
                    }
                    // active
                    $active_index = array_search($active, $generator->columns['name']);
                    $generator->columns['field_type'][$active_index] = 'boolean';
                    $generator->columns['value_type'][$active_index] = 'boolean';

                    // password
                    $password_index = array_search($password, $generator->columns['name']);
                    $generator->columns['field_type'][$password_index] = 'password';
                    $generator->columns['special'][$password_index]    = USERS_PASSWORD_CONSTRAINT;
                    $help_text_constant = strtoupper(str_replace('-', '_', USERS_PASSWORD_CONSTRAINT));
                    $generator->columns['help_text'][$password_index]  = constant($help_text_constant);

                    $json_data = array(
                        'list_options'         => $generator->list_options,
                        'columns'              => $generator->columns,
                        'external_columns'     => $generator->external_columns,
                        'field_delete_confirm_1' => $generator->field_delete_confirm_1,
                        'field_delete_confirm_2' => $generator->field_delete_confirm_2
                    );

                    // register table & columns properties in json file
                    $json = json_encode($json_data);
                    $generator->registerJson($generator->table . '.json', $json);

                    // db-data (admin/data/db-data.json)
                    $json                                      = file_get_contents(ADMIN_DIR . 'crud-data/db-data.json');
                    $db_data                                   = json_decode($json, true);
                    $table                                     = $generator->table;
                    $db_data[$table]['icon']                   = $generator->table_icon;
                    $db_data[$table]['field_delete_confirm_1'] = $generator->field_delete_confirm_1;
                    $db_data[$table]['field_delete_confirm_2'] = $generator->field_delete_confirm_2;
                    $db_data[$table]['fields']                 = $generator->columns['fields'];

                    $json = json_encode($db_data);
                    $dir = ADMIN_DIR . 'crud-data/';
                    $file = 'db-data.json';
                    $generator->registerAdminFile($dir, $file, $json);

                    $_SESSION['generator'] = $generator;

                    $generator->action = 'build_paginated_list';
                    $generator->runBuild();
                    $generator->action = 'build_create_edit';
                    $generator->runBuild();

                    $_SESSION['form_select_table']['table'] = $users_table;

                    // reset user msg then register the Secure msg
                    $_SESSION['msg'] = '';
                    Secure::userMessage(USER_MANAGEMENT_SUCCESSFULLY_CREATED, 'alert-success');


                    /* =============================================
                    TODO : send user email here
                    ============================================= */

                    // create lock file
                    if (file_put_contents('install.lock', '') === false) {
                        exit('Unable to write lock file');
                    }
                    $generator->lockAdminPanel();
                    $_SESSION['generator'] = $generator;
                    $reload_page_message = true;
                } catch (\Exception $e) {
                    // If there was a problem, rollback the transaction
                    // as if no database actions here had ever happened
                    if ($db->inTransaction()) {
                        $db->transactionRollback();
                    }

                    echo $e->getMessage();
                }
            }
        }
    }

    if (!isset($_POST['form-admin-setup']) || (isset($_POST['form-admin-setup']) && $validator->hasErrors())) {
        /* =============================================
        Select Tables
        ============================================= */

        $db = new DB();
        if (!$db->IsConnected()) {
            Secure::userMessage(FAILED_TO_CONNECT_DB, 'alert-danger');
        } else {
            $default_users_table = '';
            if (!isset($_SESSION['form-admin-setup']['users_table']) || empty($_SESSION['form-admin-setup']['users_table'])) {
                $default_users_table = 'phpcg_users';
            }
            $tables = $db->getTables();
            $form_admin_setup = new Form('form-admin-setup', 'horizontal', 'data-fv-no-auto-submit=true, novalidate');
            $form_admin_setup->setMode('development');
            $form_admin_setup->useLoadJs('core');
            $options = array(
                'openDomReady'       => '',
                'closeDomReady'      => ''
            );
            $form_admin_setup->setOptions($options);
            $form_admin_setup->addInput('hidden', 'reset-relations', true);
            $form_admin_setup->addOption('tables[]', '', '', '', 'data-placeholder=true');
            foreach ($tables as $table) {
                $form_admin_setup->addOption('tables[]', $table, $table);
            }
            $form_admin_setup->setCols(3, 9);
            $form_admin_setup->startFieldset(TABLES, '', 'class=text-bg-info');
            $form_admin_setup->addHelper(SELECT_TABLES_USED_IN_ADMIN_HELP, 'tables[]', 'after');
            $form_admin_setup->addSelect('tables[]', SELECT_TABLES_USED_IN_ADMIN, 'data-slimselect=true, data-close-on-select=false, data-placeholder=' . SELECT_CONST . ' ..., multiple, required');
            $form_admin_setup->addHelper(USERS_TABLE_NAME_HELP, 'users_table', 'after');
            $form_admin_setup->addInput('text', 'users_table', $default_users_table, USERS_TABLE_NAME, 'required');
            $form_admin_setup->endFieldset();
            $form_admin_setup->startFieldset(SITE_ADMINISTRATOR, '', 'class=text-bg-info');
            $form_admin_setup->addInput('text', 'name', '', NAME, 'required');
            $form_admin_setup->addInput('text', 'firstname', '', FIRST_NAME, 'required');
            $form_admin_setup->addTextarea('address', '', ADDRESS);
            $form_admin_setup->setCols(3, 3);
            $form_admin_setup->groupElements('city', 'zip_code');
            $form_admin_setup->addInput('text', 'city', '', CITY);
            $form_admin_setup->setCols(2, 4);
            $form_admin_setup->addInput('text', 'zip_code', '', ZIP_CODE);
            $form_admin_setup->setCols(3, 9);
            $form_admin_setup->addInput('text', 'email', '', EMAIL, 'required');
            $form_admin_setup->setCols(3, 3);
            $form_admin_setup->groupElements('phone', 'mobile_phone');
            $form_admin_setup->addInput('text', 'phone', '', PHONE);
            $form_admin_setup->setCols(2, 4);
            $form_admin_setup->addInput('text', 'mobile_phone', '', MOBILE_PHONE);
            $form_admin_setup->setCols(3, 9);
            $form_admin_setup->addHelper(ADMIN_PASSWORD_HELP, 'pass', 'after');
            $form_admin_setup->addInput('password', 'pass', '', PASSWORD, 'required');
            $form_admin_setup->endFieldset();
            $form_admin_setup->setCols(0, 12);
            $form_admin_setup->centerContent();
            $form_admin_setup->addBtn('submit', 'submit-btn', 1, SUBMIT . '<i class="' . ICON_CHECKMARK . ' append"></i>', 'class=btn btn-lg btn-primary, data-ladda-button=true, data-style=zoom-in');
            $form_admin_setup->addPlugin('passfield', '#pass', 'lower-upper-min-6');
            $form_admin_setup->addPlugin('formvalidation', '#form-admin-setup', 'default', array('language' => FORMVALIDATION_JAVASCRIPT_LANG));
        }
    }
} else {
    exit('Installer is locked. Delete admin/secure/install.lock to unlock the access.');
}
?>
<div class="container">
    <div class="row justify-content-md-center">
        <div class="col-md-11 col-lg-10 mb-4">
            <h2 class="h1 text-center my-4"><?php echo DATABASE_CONST . ' ' . $database_name; ?> - <?php echo USER_MANAGEMENT; ?></h2>
            <?php
            if (DEMO === true) {
                ?>
            <div class="alert alert-info has-icon">
                <p class="lead">The user authentication module is disabled in this demo.</p>

                <p>In real life, it protects access to the site administration interface using a login form.</p>

                <p>The lead administrator may then:</p>
                <ul>
                    <li>create different user profiles</li>
                    <li>assign read/write / delete permissions to each profile</li>
                    <li>create different users and assign each of them a profile</li>
                </ul>

                <p>Rights can be allocated individually to each table.</p>
                <p>They can also be restricted to the user himself.</p>

                <p>For example:<br>
                    If there is an "actors" table joined to the user table, an actor can access his own records only.</p>
            </div>
                <?php
            }
            if (isset($form_admin_setup)) {
                $form_admin_setup->render();
            } elseif (isset($reload_page_message)) {
                ?>
            <div class="alert alert-success has-icon">
                <p class="lead text-center"><?php echo ADMIN_AUTHENTICATION_MODULE_INSTALLED; ?></p>
            </div>
                <?php
            }
            ?>
        </div>
    </div>
</div>
<script>
    <?php
    if (isset($reload_page_message)) {
        ?>
        $('#auth-not-installed').remove();
        $('#install-authentication-module-btn').closest('div').remove();
        setTimeout(() => {
            location.reload();
        }, 4000);
        <?php
    } else {
        ?>
        var run = function() {
            // scroll to error (invalid feedback)
            if (document.querySelector('#form-admin-setup p.invalid-feedback:not(.fv-plugins-message-container)')) {
                setTimeout(() => {
                    let dims = document.querySelector('#form-admin-setup p.invalid-feedback:not(.fv-plugins-message-container)').getBoundingClientRect();
                    window.scrollTo(window.scrollX, dims.top - 200 + window.scrollY);
                }, 1000);
            } else {
                window.scrollTo(0, 0);
            }
        }
        <?php
    }
    ?>
    <?php
    if (isset($form_admin_setup)) {
        $script = $form_admin_setup->printJsCode(false, false);
        echo str_replace(['<script>', '</script>'], '', $script);
        ?>

/* Ajax POST */
if (typeof fvCallback == 'undefined') {
let fvCallback;
}
fvCallback = function() {
const form = forms['form-admin-setup'];

// form.fv is the validator
// you can then use the formvalidation plugin API
form.fv.on('core.form.valid', function() {
const target = $('#authentication-module-installer'),
data = $('#form-admin-setup').serialize();
$.ajax({
url: '<?php echo ADMIN_URL; ?>secure/install/index.php',
type: 'POST',
data: data,
}).done(function(data) {
target.html(data);
}).fail(function(data, statut, error) {
console.log(error);
});
});
};
        <?php
    }
    ?>
</script>