<?php
// +-------------------------------------------------+
// © 2002-2005 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: acces.class.php,v 1.57.2.1.2.5 2025/12/16 10:47:41 qvarin Exp $


if (stristr ($_SERVER['REQUEST_URI'], ".class.php")) {
    die ("no access");
}

global $base_path, $class_path;

if ( !defined ('USER_PRF_TYP')) {
    define ('USER_PRF_TYP', 0); // 0 = profil utilisateur (role)
}
if ( !defined ('RES_PRF_TYP')) {
    define ('RES_PRF_TYP', 1); // 1 = profil ressource
}

require_once ($class_path."/marc_table.class.php");


class acces {

    /* Table catalogue */
    protected static $t_cat = [];

    protected static $t_doms = [];

    public $dom;

    public function __construct()
    {
        if (!count(static::$t_cat)) {
            //Lecture catalog
            $this->parseCatalog();
        }
    }

    /**
     * Lecture fichier catalog
     */
    public function parseCatalog()
    {
        global $base_path;
        global $msg;
        $t_cat = array();
        $cp = new accesParser();
        $t_cat = $cp->run("$base_path/admin/acces/catalog.xml");

        foreach ($t_cat as $v) {
            $mt = $this->parseManifestFile($v['path']);
            $activation_param = $mt['activation_param'];
            global ${$activation_param};
            if (${$activation_param} == '1') {
                static::$t_cat[$v['id']]['id'] = $v['id'];
                static::$t_cat[$v['id']]['path'] = $v['path'];
                static::$t_cat[$v['id']]['comment'] = '';
                if (!empty($msg[$mt['comment']])) {
                    static::$t_cat[$v['id']]['comment'] = $msg[$mt['comment']];
                }
            }
        }
        unset($t_cat);
    }


    /**
     * Lecture des domaines actives
     *
     * @return array
     */
    public function getCatalog()
    {
        return static::$t_cat;
    }


    /**
     * Lecture fichier manifest
     * @param string $path
     * @return array
     */
    public function parseManifestFile($path)
    {
        global $base_path;

        $mp = new accesParser();
        return $mp->run("$base_path/admin/acces/$path/manifest.xml");
    }


    /**
     * Instanciation domaine
     *
     * @param int $dom_id
     * @return mixed
     */
    public function setDomain($dom_id)
    {
        if (empty(static::$t_doms[$dom_id])) {
            static::$t_doms[$dom_id] = new domain(static::$t_cat[$dom_id]['id'], static::$t_cat[$dom_id]['path']);
        }
        $this->dom = static::$t_doms[$dom_id];
        return $this->dom;
    }

    public static function get_instance()
    {
        global $ac;

        if (!$ac) {
            $ac = new acces();
        }
        return $ac;
    }
}


class domain
{

    public $dom = [];

    protected static $store;

    /**
     * instanciation domaine
     *
     * @param int $id
     * @param string $path
     */
    public function __construct($id, $path)
    {
        if (!$id || !$path) {
            return;
        }
        $this->dom['id'] = $id;
        $this->dom['path'] = $path;
        $this->parseDomainFile();
        $this->enableProperties();
        $this->parseMsgFile();
        $this->setDefaultRights();
        $this->checkTables();  // Commente en OPAC
    }


    /**
     * Lecture du fichier domain
     */
    public function parseDomainFile()
    {
        global $base_path;

        $ap = new accesParser();
        $dom_file = "$base_path/admin/acces/" . $this->dom['path'] . "/domain.xml";
        $this->dom = $this->dom + $ap->run($dom_file);
    }


    /**
     * Activation des proprietes
     */
    protected function enableProperties()
    {
        if (empty($this->dom['enable'])) {
            return;
        }
        $dom_user_properties = explode(',', $this->dom['user']['properties']);
        foreach ($this->dom['enable'] as $k => $v) {

            $callback = explode('|', $v);
            $callback_method = $callback[0];
            $callback_params = explode(',', $callback[1]);
            $enable = false;
            if (method_exists($this, $callback_method)) {
                $enable = call_user_func_array([$this, $callback_method], [$callback_params]);
            }
            if (!$enable) {
                $dom_user_property_key = array_search($k, $dom_user_properties);
                if ($dom_user_property_key !== false) {
                    unset($dom_user_properties[$dom_user_property_key]);
                }
            }
        }
        $this->dom['user']['properties'] = implode(',', $dom_user_properties);
    }


    protected static function check_param($args)
    {
        if (!is_array($args) || (count($args) != 3)) {
            return false;
        }
        $type_param = $args[0];
        $sstype_param = $args[1];
        $valeur_param = $args[2];
        $q = "select id_param from parametres where type_param='" . addslashes($type_param) . "' and sstype_param='" . addslashes($sstype_param) . "' ";
        $q .= "and valeur_param='" . addslashes($valeur_param) . "' limit 1";
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            return true;
        }
        return false;
    }


    /**
     * lecture messages
     */
    public function parseMsgFile()
    {
        global $base_path, $lang;

        $msg_file = $base_path . "/admin/acces/" . $this->dom['path'] . "/messages/" . $lang . ".xml";
        if (!is_readable($msg_file)) {
            $msg_file = $base_path . "/admin/acces/" . $this->dom['path'] . "/messages/fr_FR.xml";
        }
        if (is_readable($msg_file)) {
            $parser = new XMLlist($msg_file);
            $parser->analyser();
            $this->dom['msg'] = $parser->table;
        }
    }


    /**
     * definition des droits par defaut
     */
    public function setDefaultRights()
    {
        $this->dom['default_rights'] = 0;
    }


    /**
     * creation des tables necessaires
     */
    public function checkTables()
    {
        //creation de la table de stockage des droits par profils utilisateurs et par ressources si inexistante
        $q = "CREATE TABLE IF NOT EXISTS acces_res_".$this->dom['id']." (
            res_num int(8) unsigned NOT NULL default 0,
            res_prf_num int(2) unsigned NOT NULL default 0,
            usr_prf_num int(2) unsigned NOT NULL default 0,
            res_rights int(2) NOT NULL default 0,
            res_mask int(2) NOT NULL default 0,
            PRIMARY KEY (res_num, usr_prf_num)
        )";
        pmb_mysql_query($q);

        //creation de la table de surcharge des droits par utilisateur si inexistante
        $q = "CREATE TABLE IF NOT EXISTS acces_usr_".$this->dom['id']." (
            usr_num int(8) unsigned NOT NULL default 0,
            res_prf_num int(2) unsigned NOT NULL default 0,
            usr_rights int(2) NOT NULL default 0,
            updated timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (usr_num, res_prf_num)
         )";
        pmb_mysql_query($q);
    }


    /**
     * Lecture message general/specifique domaine
     *
     * @param string $msg_code
     */
    public function getComment($msg_code)
    {
        global $msg;
        if (substr($msg_code, 0, 4) == 'msg:') {
            return $msg[substr($msg_code, 4)];
        } else {
            return $this->dom['msg'][$msg_code];
        }
    }


    /**
     * Retourne la liste des proprietes utilisateur accessibles
     * et eventuellement celles utilisees
     *
     * @return array
     */
    public function getUserProperties()
    {
        $t_acc = explode(',', $this->dom['user']['properties']);
        $t_r = array();
        foreach ($t_acc as $v) {
            $t_r[$v]['id'] = $v;
            $p_lib = $this->dom['properties'][$v]['lib'];
            $t_r[$v]['lib'] = $this->getComment($p_lib);
        }
        unset($v);
        return $t_r;
    }


    public function getDisplayUserProperties()
    {
        global $charset;

        $display = '';
        $t = $this->getUserProperties();
        if (count($t)) {
            foreach ($t as $k => $v) {
                $display .= "<div class='row'>";
                $display .= "<input type='checkbox' id='chk_prop[$k]' name='chk_prop[]' value='" . $k . "' />";
                $display .= "&nbsp;<label class='etiquette' for='chk_prop[$k]'>" . htmlentities($v['lib'], ENT_QUOTES, $charset) . "</label>";
                $display .= "</div>";
            }
        }
        return $display;
    }


    /**
     * enregistrement d'un profil utilisateur
     *
     * @param int $prf_id
     * @param string $prf_lib
     * @param string $prf_rule
     * @param string $prf_hrule
     * @param int $prf_used
     * @return int
     */
    public function saveUserProfile($prf_id, $prf_lib, $prf_rule, $prf_hrule, $prf_used)
    {
        if ($prf_id) { // profils deja enregistres, on modifie juste nom et profil utilise
            $q = "update acces_profiles set prf_name='" . addslashes($prf_lib) . "', prf_used='" . $prf_used . "' where prf_id ='" . $prf_id . "' ";
            pmb_mysql_query($q);
            return $prf_id;
        } else { //on regarde si un profil similaire n'existe pas deja
            $q = "select prf_id from acces_profiles where dom_num='" . $this->dom['id'] . "' and prf_type='" . USER_PRF_TYP . "' and prf_rule='" . addslashes($prf_rule) . "' limit 1 ";
            $r = pmb_mysql_query($q);
            if (pmb_mysql_num_rows($r)) {
                $row = pmb_mysql_fetch_object($r);
                return $row->prf_id;
            } else {
                $q = "insert into acces_profiles set dom_num='".$this->dom['id']."', prf_type='".USER_PRF_TYP."',prf_name='".addslashes($prf_lib)."', prf_rule='".addslashes($prf_rule)."', prf_hrule='".addslashes($prf_hrule)."' ";
                pmb_mysql_query($q);
                $il = pmb_mysql_insert_id();
                $q = "update acces_profiles set prf_used=prf_id where prf_id='" . $il . "' ";
                pmb_mysql_query($q);
                return $il;
            }
        }
    }


    /**
     * enregistrement des profils utilisateurs
     * @param int $prf_id
     * @param string $prf_lib
     * @param string $prf_rule
     * @param string $prf_hrule
     * @param array $prf_used
     * @param array $unused_prf_id
     */
    public function saveUserProfiles($prf_id, $prf_lib, $prf_rule, $prf_hrule, $prf_used = array(), $unused_prf_id = array())
    {
        $t_id = array();
        if (count($prf_id)) {
            foreach ($prf_id as $k => $v) {
                $t_id[] = $this->saveUserProfile($v, $prf_lib[$k], $prf_rule[$k], $prf_hrule[$k], $prf_used[$k]);
            }
            $l_id = implode("','", $t_id);
            $q = "delete from acces_profiles where dom_num='" . $this->dom['id'] . "' and prf_type='" . USER_PRF_TYP . "' and prf_id not in ('" . $l_id . "') ";
            pmb_mysql_query($q);
        }

        //que faire des anciens profils inutilises
        if (count($unused_prf_id)) {
            foreach ($unused_prf_id as $v) {

                //modification dans la table des droits par ressource
                if (empty($prf_used[$v])) {
                    $prf_used[$v] = 0;
                }
                $q = "update acces_res_" . $this->dom['id'] . " set usr_prf_num='" . $prf_used[$v] . "' where usr_prf_num='" . $v . "' ";
                pmb_mysql_query($q);

                //suppression dans la table de droits
                $q = "delete from acces_rights where dom_num='" . $this->dom['id'] . "' and usr_prf_num='" . $v . "' ";
                pmb_mysql_query($q);
            }
        }
    }


    /**
     * lecture des profils utilisateurs
     * @return string
     */
    public function loadUserProfiles()
    {
        $q = "select acces_profiles.prf_id as id, acces_profiles.* from acces_profiles where ";
        $q .= "dom_num = '" . $this->dom['id'] . "' ";
        $q .= "and prf_type='" . USER_PRF_TYP . "' order by prf_name ";
        return $q;
    }


    /**
     * Lecture des profils utilisateurs utiles pour le calcul des droits
     *
     * @param array $except
     * @return string
     */
    public function loadUsedUserProfiles($except = array())
    {
        $q = "select * from acces_profiles where ";
        $q .= "dom_num = '" . $this->dom['id'] . "' ";
        $q .= "and prf_type='" . USER_PRF_TYP . "' ";
        if (count($except)) {
            $q .= "and prf_id not in ('" . implode("','", $except) . "') ";
        }
        $q .= "and prf_id = prf_used ";
        $q .= "order by prf_name ";
        return $q;
    }


    /**
     * suppression des profils utilisateurs
     */
    public function deleteUserProfiles()
    {
        $q = "delete from acces_profiles where prf_type='" . USER_PRF_TYP . "' and dom_num='" . $this->dom['id'] . "' ";
        pmb_mysql_query($q);
    }


    /**
     * Calcul des profils utilisateurs en mode automatique (produit scalaire)
     *
     * @param array $chk_prop
     * @return array|number|string|array|mixed
     */
    public function calcUserProfiles($chk_prop = array())
    {
        global $msg;
        if (!count($chk_prop)) {
            return array();
        }

        //Recuperation proprietes utilisateurs
        $t_p = array();
        $t_pid = explode(',', $this->dom['user']['properties']);

        foreach ($t_pid as $v) {
            if (in_array($v, $chk_prop)) {
                $t_p[$v]['type'] = $this->dom['properties'][$v]['ref']['type'];
                $t_p[$v]['name'] = $this->dom['properties'][$v]['ref']['name'];
                $t_p[$v]['key'] = $this->dom['properties'][$v]['ref']['key'];
                $t_p[$v]['value'] = $this->dom['properties'][$v]['ref']['value'];
                $t_p[$v]['clause'] = $this->dom['properties'][$v]['ref']['clause'];
                $t_p[$v]['class'] = $this->dom['properties'][$v]['ref']['class'];
                $t_p[$v]['method'] = $this->dom['properties'][$v]['ref']['method'];
            }
        }
        unset($v);

        //Recuperation des valeurs possibles pour chaque propriete
        $t_kv = array();
        foreach ($t_p as $k => $v) {
            switch ($v['type']) {
                case 'table':
                    $q = "select " . $v['key'] . ", " . $v['value'] . " from " . $v['name'] . " " . ($v['clause'] ? "where " . $v['clause'] . " " : "") . "order by 2 ";
                    $r = pmb_mysql_query($q);
                    while (($row = pmb_mysql_fetch_row($r))) {
                        $t_kv[$k][$row[0]] = $row[1];
                    }
                    break;
                case 'marc_table':
                    $t_m = marc_list_collection::get_instance('doctype');
                    $t_kv[$k] = $t_m->table;
                    break;
                case 'callable':
                    $t_kv[$k] = call_user_func_array(array($v["class"], $v["method"]), []);
                    break;
                default:
                    break;
            }
        }
        unset($v);
        $t_k = array_keys($t_kv);
        $t_kv = array_reverse($t_kv, true);

        $nb_tab = count($t_kv);

        $t_r = $this->tab_scal($t_kv);

        $p = current($t_k);
        foreach ($t_r as $k => $v) {
            reset($t_k);
            $p = current($t_k);
            $prev_p = 0;
            $j = 0;
            for ($i = 0; $i < $nb_tab; $i++) {
                $t_r[$k]['rule']['search'][] = 'f_' . $p;
                if ($prev_p == 0) {
                    $t_r[$k]['rule']['inter_' . $prev_p . '_f_' . $p] = '';
                } else {
                    $t_r[$k]['rule']['inter_' . $prev_p . '_f_' . $p] = 'and';
                }
                $t_r[$k]['rule']['op_' . $prev_p . '_f_' . $p] = 'EQ';
                $t_r[$k]['rule']['field_' . $prev_p . '_f_' . $p][] = $v[$j];
                $prev_p = $p;

                if (empty($t_r[$k]['hrule'])) {
                    $t_r[$k]['hrule'] = '';
                }
                if ($t_r[$k]['hrule']) {
                    $t_r[$k]['hrule'] .= "\n" . $msg['search_and'] . " ";
                }
                $t_r[$k]['hrule'] .= $this->getComment($this->dom['properties'][$p]['lib']) . " " . $v[$j + 1];

                if (empty($t_r[$k]['name'])) {
                    $t_r[$k]['name'] = $this->dom['msg']['user_prf_calc_lib'];
                }
                $t_r[$k]['name'] .= " / " . $v[$j + 1];

                $p = next($t_k);
                $j += 2;
            }
        }


        /**
         * comparaison et reprise des profils deja existants
         */
        foreach ($t_r as $k => $v) {
            $t_r[$k]['rule'] = serialize($v['rule']);
            $q = "select prf_id, prf_name, prf_used from acces_profiles where dom_num='" . $this->dom['id'] . "' and prf_type='" . USER_PRF_TYP . "' and prf_rule='" . $t_r[$k]['rule'] . "' limit 1 ";
            $r = pmb_mysql_query($q);
            if (pmb_mysql_num_rows($r)) {
                $row = pmb_mysql_fetch_object($r);
                $t_r[$k]['name'] = $row->prf_name;
                $t_r[$k]['old'] = $row->prf_id;
            } else {
                $t_r[$k]['old'] = 0;
            }
        }
        //print '<pre>';print_r($t_r);print '</pre>';

        //faut trier
        uasort($t_r, "cmpRules");

        return $t_r;
    }


    public function tab_scal($t_kv)
    {
        $nb_val = 1;
        foreach ($t_kv as $v) {
            $nb_val = $nb_val * count($v);
        }
        unset($v);

        $tr = array();
        $i = 1;
        $t1 = array_pop($t_kv);
        $n1 = $nb_val / count($t1);
        foreach ($t1 as $k1 => $v1) {
            for ($j = 0; $j < $n1; $j++) {
                $tr[$i][] = $k1;
                $tr[$i][] = $v1;
                $i++;
            }
        }
        while (count($t_kv)) {
            $t2 = array_pop($t_kv);
            $tr = $this->tab_scal_2($tr, $t2);
        }
        return $tr;
    }


    public function tab_scal_2($tr, $t2)
    {
        foreach ($tr as $kr => $vr) {
            $tr[$kr][] = key($t2);
            $tr[$kr][] = current($t2);
            if (!next($t2)) {
                reset($t2);
            }
        }
        return $tr;
    }


    /**
     * Retourne la liste des proprietes ressource accessibles
     * et eventuellement celles utilisees
     *
     * @return array
     */
    public function getResourceProperties()
    {
        $t_acc = explode(',', $this->dom['resource']['properties']);
        $t_r = array();
        foreach ($t_acc as $v) {
            $t_r[$v]['id'] = $v;
            $p_lib = $this->dom['properties'][$v]['lib'];
            $t_r[$v]['lib'] = $this->getComment($p_lib);
        }
        unset($v);
        return $t_r;
    }


    public function getDisplayResourceProperties()
    {
        global $charset;

        $display = '';
        $t = $this->getResourceProperties();
        if (count($t)) {
            foreach ($t as $k => $v) {
                $display .= "<div class='row'>";
                $display .= "<input type='checkbox' id='chk_prop[$k]' name='chk_prop[]' value='" . $k . "' />";
                $display .= "&nbsp;<label class='etiquette' for='chk_prop[$k]'>" . htmlentities($v['lib'], ENT_QUOTES, $charset) . "</label>";
                $display .= "</div>";
            }
        }
        return $display;
    }


    /**
     * enregistrement d'un profil ressource
     *
     * @param int $prf_id
     * @param string $prf_lib
     * @param string $prf_rule
     * @param string $prf_hrule
     * @param int $prf_used
     * @return int
     */
    public function saveResourceProfile($prf_id, $prf_lib, $prf_rule, $prf_hrule, $prf_used)
    {
        if ($prf_id) { // profils deja enregistres, on modifie juste nom et profil utilise
            $q = "update acces_profiles set prf_name='" . addslashes($prf_lib) . "', prf_used='" . $prf_used . "' where prf_id ='" . $prf_id . "' ";
            pmb_mysql_query($q);
            return $prf_id;
        } else {
            $q = "insert into acces_profiles set dom_num='".$this->dom['id']."', prf_type='".RES_PRF_TYP."', prf_name='".addslashes($prf_lib)."', prf_rule='".addslashes($prf_rule)."', prf_hrule='".addslashes($prf_hrule)."' ";
            pmb_mysql_query($q);
            $il = pmb_mysql_insert_id();
            $q = "update acces_profiles set prf_used=prf_id where prf_id='" . $il . "' ";
            pmb_mysql_query($q);
            return $il;
        }
    }


    /**
     * enregistrement des profils ressources
     *
     * @param int $prf_id
     * @param string $prf_lib
     * @param string $prf_rule
     * @param string $prf_hrule
     * @param array $prf_used
     * @param array $unused_prf_id
     */
    public function saveResourceProfiles($prf_id, $prf_lib, $prf_rule, $prf_hrule, $prf_used = array(), $unused_prf_id = array())
    {
        $t_id = array();
        if (!empty($prf_id) && is_countable($prf_id) && count($prf_id)) {
            foreach ($prf_id as $k => $v) {
                $t_id[] = $this->saveResourceProfile($v, $prf_lib[$k], $prf_rule[$k], $prf_hrule[$k], $prf_used[$k]);
            }
        }
        $l_id = implode("','", $t_id);
        $q = "delete from acces_profiles where dom_num='" . $this->dom['id'] . "' and prf_type='" . RES_PRF_TYP . "' and prf_id not in ('" . $l_id . "') ";
        pmb_mysql_query($q);

        //que faire des anciens profils inutilises
        if (!empty($unused_prf_id) && is_countable($unused_prf_id) && count($unused_prf_id)) {
            foreach ($unused_prf_id as $v) {
                //modification dans la table des droits par ressource
                if (!$prf_used[$v]) {
                    $prf_used[$v] = 0;
                }
                $q = "update acces_res_" . $this->dom['id'] . " set res_prf_num='" . $prf_used[$v] . "' where res_prf_num='" . $v . "' ";
                pmb_mysql_query($q);

                //suppression dans la table de droits
                $q = "delete from acces_rights where dom_num='" . $this->dom['id'] . "' and res_prf_num='" . $v . "' ";
                pmb_mysql_query($q);
            }
        }
    }


    /**
     * Lecture des profils ressources
     *
     * @return string
     */
    public function loadResourceProfiles()
    {
        $q = "select acces_profiles.prf_id as id, acces_profiles.* from acces_profiles where ";
        $q .= "dom_num = '" . $this->dom['id'] . "' ";
        $q .= "and prf_type='" . RES_PRF_TYP . "' order by prf_name ";
        return $q;
    }


    /**
     * Lecture des profils ressources utiles pour le calcul des droits
     *
     * @param array $except
     * @return string
     */
    public function loadUsedResourceProfiles($except = array())
    {
        $q = "select * from acces_profiles where ";
        $q .= "dom_num = '" . $this->dom['id'] . "' ";
        $q .= "and prf_type='" . RES_PRF_TYP . "' ";
        if (count($except)) {
            $q .= "and prf_id not in ('" . implode("','", $except) . "') ";
        }
        $q .= "and prf_id = prf_used ";
        $q .= "order by prf_name ";
        return $q;
    }


    /**
     * suppression des profils ressources
     */
    public function deleteResourceProfiles()
    {
        $q = "delete from acces_profiles where prf_type='" . RES_PRF_TYP . "' and dom_num='" . $this->dom['id'] . "' ";
        pmb_mysql_query($q);
    }


    /**
     * Calcul des profils ressources en mode automatique (produit scalaire)
     *
     * @param array $chk_prop
     * @return array|number|string|array|mixed
     */
    public function calcResourceProfiles($chk_prop = array())
    {
        global $msg;
        if (!count($chk_prop)) {
            return array();
        }

        //Recuperation proprietes utilisateurs
        $t_p = array();
        $t_pid = explode(',', $this->dom['resource']['properties']);

        foreach ($t_pid as $v) {
            if (in_array($v, $chk_prop)) {
                $t_p[$v]['type'] = $this->dom['properties'][$v]['ref']['type'];
                $t_p[$v]['name'] = $this->dom['properties'][$v]['ref']['name'];
                $t_p[$v]['key'] = $this->dom['properties'][$v]['ref']['key'];
                $t_p[$v]['value'] = $this->dom['properties'][$v]['ref']['value'];
                $t_p[$v]['clause'] = $this->dom['properties'][$v]['ref']['clause'];
                $t_p[$v]['class'] = $this->dom['properties'][$v]['ref']['class'];
                $t_p[$v]['method'] = $this->dom['properties'][$v]['ref']['method'];
            }
        }
        unset($v);

        //Recuperation des valeurs possibles pour chaque propriete
        $t_kv = array();
        foreach ($t_p as $k => $v) {
            switch ($v['type']) {
                case 'table':
                    $q = "select " . $v['key'] . ", " . $v['value'] . " from " . $v['name'] . " " . ($v['clause'] ? "where " . $v['clause'] . " " : "") . "order by 2 ";
                    $r = pmb_mysql_query($q);
                    while (($row = pmb_mysql_fetch_row($r))) {
                        $t_kv[$k][$row[0]] = $row[1];
                    }
                    break;
                case 'marc_table':
                    $xf = substr($v['name'], 0, strrpos($v['name'], '.'));
                    $t_m = marc_list_collection::get_instance($xf);
                    $t_kv[$k] = $t_m->table;
                    break;
                case 'callable':
                    $t_kv[$k] = call_user_func_array(array($v["class"], $v["method"]), []);
                    break;
                default:
                    break;
            }
        }
        unset($v);
        $t_k = array_keys($t_kv);
        $t_kv = array_reverse($t_kv, true);

        $nb_tab = count($t_kv);

        $t_r = $this->tab_scal($t_kv);

        $p = current($t_k);
        foreach ($t_r as $k => $v) {
            reset($t_k);
            $p = current($t_k);
            $prev_p = 0;
            $j = 0;
            for ($i = 0; $i < $nb_tab; $i++) {
                $t_r[$k]['rule']['search'][] = 'f_' . $p;
                if ($prev_p == 0) {
                    $t_r[$k]['rule']['inter_' . $prev_p . '_f_' . $p] = '';
                } else {
                    $t_r[$k]['rule']['inter_' . $prev_p . '_f_' . $p] = 'and';
                }
                $t_r[$k]['rule']['op_' . $prev_p . '_f_' . $p] = 'EQ';
                $t_r[$k]['rule']['field_' . $prev_p . '_f_' . $p][] = $v[$j];
                $prev_p = $p;

                if (empty($t_r[$k]['hrule'])) {
                    $t_r[$k]['hrule'] = '';
                }
                if ($t_r[$k]['hrule']) {
                    $t_r[$k]['hrule'] .= "\n" . $msg['search_and'] . " ";
                }
                $t_r[$k]['hrule'] .= $this->getComment($this->dom['properties'][$p]['lib']) . " " . $v[$j + 1];

                if (empty($t_r[$k]['name'])) {
                    $t_r[$k]['name'] = $this->dom['msg']['res_prf_calc_lib'];
                }
                $t_r[$k]['name'] .= " / " . $v[$j + 1];

                $p = next($t_k);
                $j += 2;
            }
        }

        //comparaison et reprise des profils deja existants
        foreach ($t_r as $k => $v) {
            $t_r[$k]['rule'] = serialize($v['rule']);
            $q = "select prf_id, prf_name from acces_profiles where dom_num='" . $this->dom['id'] . "' and prf_type='" . RES_PRF_TYP . "' and prf_rule='" . $t_r[$k]['rule'] . "' limit 1 ";
            $r = pmb_mysql_query($q);
            if (pmb_mysql_num_rows($r)) {
                $row = pmb_mysql_fetch_object($r);
                $t_r[$k]['name'] = $row->prf_name;
                $t_r[$k]['old'] = $row->prf_id;
            } else {
                $t_r[$k]['old'] = 0;
            }
        }
        //print '<pre>';print_r($t_r);print '</pre>';

        //faut trier
        uasort($t_r, "cmpRules");

        return $t_r;
    }


    /**
     * lecture des elements faisant l'objet d'un controle d'acces sur les ressources
     * si all=0, retourne les controles dependants de l'utilisateur
     * si all=1, retourne les controles independants de l'utilisateur
     * si all=2, retourne tous les controles
     *
     * @param number $all
     * @return NULL[]
     */
    public function getControls($all = 2)
    {
        $t_r = array();
        foreach ($this->dom['controls'] as $k => $v) {

            switch ($all) {
                case 0:
                    if ($v['global'] != 'yes') {
                        $t_r[$k] = $this->getComment($v['lib']);
                    }
                    break;
                case 1:
                    if ($v['global'] == 'yes') {
                        $t_r[$k] = $this->getComment($v['lib']);
                    }
                    break;
                case 2:
                default:
                    $t_r[$k] = $this->getComment($v['lib']);
                    break;
            }
        }
        unset($v);
        return $t_r;
    }


    /**
     * enregistrement des droits du domaine
     *
     * @param array $t_rights
     */
    public function saveDomainRights($t_rights = array())
    {
        //recuperation de la liste des controles
        $t_ctl = $this->getControls(2);
        //Suppression des anciens droits
        $q = "delete from acces_rights where dom_num='" . $this->dom['id'] . "' ";
        pmb_mysql_query($q);

        //recuperation des roles utilisateurs
        $q_usr = $this->loadUsedUserProfiles();
        $r_usr = pmb_mysql_query($q_usr);
        $t_usr = array();
        $t_usr[0] = 0;
        if (pmb_mysql_num_rows($r_usr)) {
            while (($row = pmb_mysql_fetch_object($r_usr))) {
                $t_usr[] = $row->prf_id;
            }
        }

        $q_res = $this->loadUsedResourceProfiles();
        $r_res = pmb_mysql_query($q_res);
        $t_res = array();
        $t_res[0] = 0;
        if (pmb_mysql_num_rows($r_res)) {
            while (($row = pmb_mysql_fetch_object($r_res))) {
                $t_res[] = $row->prf_id;
            }
        }

        //pour chaque profil utilisateur
        foreach ($t_usr as $k_usr => $v_usr) {

            //pour chaque profil ressource
            foreach ($t_res as $k_res => $v_res) {

                //representation decimale des droits utilisateur/ressource
                $b_r = 0;
                if (count($t_ctl)) {
                    foreach ($t_ctl as $k_ctl => $v_ctl) {
                        if (!empty($this->dom['controls'][$k_ctl]['global']) && ($this->dom['controls'][$k_ctl]['global'] == 'yes') && !empty($t_rights[0][0][$k_ctl]) && ($t_rights[0][0][$k_ctl] == 1)) {
                            $b_r = $b_r + pow(2, ($k_ctl - 1));
                        } elseif (!empty($t_rights[$v_usr][$v_res][$k_ctl]) && ($t_rights[$v_usr][$v_res][$k_ctl] == 1)) {
                            $b_r = $b_r + pow(2, ($k_ctl - 1));
                        }
                    }
                }
                $q = "replace into acces_rights set dom_num=" . $this->dom['id'] . ",  usr_prf_num=" . $v_usr . ", res_prf_num=" . $v_res . ", dom_rights=" . $b_r . " ";
                pmb_mysql_query($q);
            }
        }
    }


    /**
     * lecture nombre de ressources a mettre a jour
     *
     * @return number|string
     */
    public function getNbResourcesToUpdate()
    {
        $ret = 0;
        switch ($this->dom['resource']['ref']['type']) {
            case 'table':
                $q = "select count(*) from " . $this->dom['resource']['ref']['name'] . " ";
                $r = pmb_mysql_query($q);
                if (pmb_mysql_num_rows($r)) {
                    $ret = pmb_mysql_result($r, 0, 0);
                }
                break;
            case 'rdf':
                $this->get_store($this->dom['resource']['ref']['name']);
                $query = self::$store->query('
                SELECT ?uri WHERE {
                    ?uri <' . $this->dom['resource']['ref']['key'] . '> <' . $this->dom['resource']['ref']['value'] . '>
                }');
                if ($query) {
                    $ret = self::$store->num_rows();
                }
                break;
            default:
                break;
        }
        return $ret;
    }


    /**
     * generation des droits par profil utilisateur pour chacune des ressources
     *
     * @param number $nb_done
     * @param number $chk_sav_spe_rights
     * @return number|boolean
     */
    public function applyDomainRights($nb_done = 0, $chk_sav_spe_rights = 0)
    {
        $nb_per_pass = 500;

        switch ($this->dom['resource']['ref']['type']) {
            case 'table':
                //lecture de nb_per_pass ressources
                $q0 = "select " . $this->dom['resource']['ref']['key'] . " from " . $this->dom['resource']['ref']['name'] . " limit " . $nb_done . "," . $nb_per_pass;
                $r0 = pmb_mysql_query($q0);

                //pour chaque ressource, definition du profil
                $nb_done += pmb_mysql_num_rows($r0);
                if ($r0) {
                    while (($row0 = pmb_mysql_fetch_row($r0))) {
                        $this->applyRessourceRights($row0[0], $chk_sav_spe_rights);
                    }
                }
                break;
            case 'rdf':
                $this->get_store($this->dom['resource']['ref']['name']);
                $query = self::$store->query('
                SELECT ?uri WHERE {
                    ?uri <' . $this->dom['resource']['ref']['key'] . '> <' . $this->dom['resource']['ref']['value'] . '>
                }');
                if ($query) {
                    if (self::$store->num_rows()) {
                        $result = self::$store->get_result();
                        foreach ($result as $elem) {
                            $this->applyRessourceRights(onto_common_uri::get_id($elem->uri), $chk_sav_spe_rights);
                            $nb_done++;
                        }
                    }
                }
                break;
            default:
                break;
        }
        return $nb_done;
    }


    /**
     * Suppression des droits pour un domaine
     *
     */
    public function deleteDomainRights()
    {
        $this->dom['id'] = intval($this->dom['id']);
        if (!$this->dom['id']) {
            return;
        }
        $table_r = "acces_res_". $this->dom['id'];
        $q = "show tables like '$table_r'";
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            $qr = "delete from acces_res_" . $this->dom['id'] . " ";
            pmb_mysql_query($qr);
        }
        $table_u = "acces_usr_". $this->dom['id'];
        $q = "show tables like '$table_u'";
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            $qu = "delete from acces_usr_" . $this->dom['id'] . " ";
            pmb_mysql_query($qu);
        }
    }


    /**
     * nettoyage
     *
     */
    public function cleanResources()
    {
        $this->dom['id'] = intval($this->dom['id']);
        if (!$this->dom['id']) {
            return;
        }
        $table = "acces_res_". $this->dom['id'];
        $q = "show tables like '$table'";
        $r = pmb_mysql_query($q);
        if (!pmb_mysql_num_rows($r)) {
            return;
        }
        $q4 = "delete from $table where res_prf_num !=0 and res_prf_num not in (select distinct prf_used from acces_profiles)";
        pmb_mysql_query($q4);
        $q5 = "delete from $table where usr_prf_num != 0 and usr_prf_num not in (select distinct prf_used from acces_profiles)";
        pmb_mysql_query($q5);
        $q6 = "delete from $table where res_num not in (select " . $this->dom['resource']['ref']['key'] . " from " . $this->dom['resource']['ref']['name'] . ") ";
        pmb_mysql_query($q6);
    }


    /**
     * lecture des droits pour la ressource
     *
     * @param number $res_id
     * @return array
     */
    public function getResourceRights($res_id = 0)
    {
        if ($res_id != 0) {
            $res_prf = $this->getResourceProfile($res_id);
            $q = "select usr_prf_num, (res_rights ^ res_mask) from acces_res_" . $this->dom['id'] . " where res_num=" . $res_id;
            $r = pmb_mysql_query($q);
            $t_r = array();
            if (pmb_mysql_num_rows($r)) {
                while (($row = pmb_mysql_fetch_row($r))) {
                    $t_r[$row[0]][$res_prf] = $row[1];
                }
                return $t_r;
            }
        }
        return $this->loadDomainRights();
    }


    /**
     * lecture de l'ensemble des droits du domaine
     *
     * @return array [usr_prf_num][res_prf_num]=dom_rights
     */
    public function loadDomainRights()
    {
        $q = "select usr_prf_num, res_prf_num, dom_rights from acces_rights where dom_num=" . $this->dom['id'] . " ";
        $r = pmb_mysql_query($q);
        $t_r = array();
        if (pmb_mysql_num_rows($r)) {
            while (($row = pmb_mysql_fetch_object($r))) {
                $t_r[$row->usr_prf_num][$row->res_prf_num] = $row->dom_rights;
            }
        }
        return $t_r;
    }


    /**
     * stocke les droits des utilisateurs a la creation/modification de la ressource
     *
     * @param number $old_res_id
     * @param number $res_id
     * @param array $res_prf
     * @param array $chk_rights
     * @param array $prf_rad
     * @param array $r_rad
     */
    public function storeUserRights($old_res_id = 0, $res_id = 0, $res_prf = array(), $chk_rights = array(), $prf_rad = array(), $r_rad = array())
    {
        if (!$res_id) {
            die('Error: ressource id not defined');
        }

        if (!$old_res_id) { //creation ressource

            $res_prf = $this->defineResourceProfile($res_id, true);

            //recuperation de la liste des droits du domaine
            $t_r = $this->loadDomainRights();

            //recuperation de la liste des controles
            $t_ctl = $this->getControls(2);

            //recuperation des profils utilisateurs utiles
            $t_usr = array_keys($t_r);

            //pour chaque profil utilisateur
            foreach ($t_usr as $k_usr => $v_usr) {
                $res_rights = $t_r[$v_usr][$res_prf];
                $q = "replace into acces_res_" . $this->dom['id'] . " set res_num=" . $res_id . ", res_prf_num=" . $res_prf . ", ";
                $q .= "usr_prf_num=" . $v_usr . ", res_rights=" . $res_rights . ", res_mask=0 ";
                pmb_mysql_query($q);
            }
        } else { //modification ressource
            if (!is_array($res_prf)) {
                $res_prf = array();
            }
            if (!is_array($res_prf[$this->dom['id']])) {
                $res_prf[$this->dom['id']] = array();
            }
            if (!is_array($chk_rights)) {
                $chk_rights = array();
            }
            if (!is_array($chk_rights[$this->dom['id']])) {
                $chk_rights[$this->dom['id']] = array();
            }
            if ((count($res_prf[$this->dom['id']]) == 0) && (count($chk_rights[$this->dom['id']]) == 0)) {
                $res_prf = $this->defineResourceProfile($res_id, true);
            } elseif (count($res_prf[$this->dom['id']]) != 0) { //Profil ressource fourni
                switch ($prf_rad[$this->dom['id']]) {
                    case 'R': //Profil recalcule
                        $res_prf = $this->defineResourceProfile($res_id, true);
                        break;
                    case 'C': //Profil choisi
                        $res_prf = $res_prf[$this->dom['id']];
                        break;
                    default:
                        break;
                }
            } else if (count($res_prf[$this->dom['id']]) == 0) {
                $res_prf = $this->defineResourceProfile($res_id, true);
            }

            //recuperation de la liste des droits du domaine
            $t_r = $this->loadDomainRights();

            //recuperation de la liste des controles
            $t_ctl = $this->getControls(2);

            //recuperation des profils utilisateurs utiles
            $t_usr = array_keys($t_r);

            //pour chaque profil utilisateur
            foreach ($t_usr as $k_usr => $v_usr) {

                //representation decimale du masque utilisateur/ressource
                $mask = 0;
                if ($r_rad[$this->dom['id']] == 'C' && count($t_ctl)) {
                    foreach ($t_ctl as $k_ctl => $v_ctl) {
                        if (isset($chk_rights[$this->dom['id']][$v_usr]) && isset($chk_rights[$this->dom['id']][$v_usr][$k_ctl]) && $chk_rights[$this->dom['id']][$v_usr][$k_ctl] == 1) {
                            $mask = $mask + pow(2, ($k_ctl - 1));
                        }
                    }
                } else {
                    $mask = $t_r[$v_usr][$res_prf];
                }
                $res_rights = $t_r[$v_usr][$res_prf];
                $q = "replace into acces_res_" . $this->dom['id'] . " set res_num=" . $res_id . ", res_prf_num=" . $res_prf;
                $q .= ",  usr_prf_num=" . $v_usr . ", res_rights=" . $res_rights . ", res_mask=($res_rights ^ $mask) ";
                pmb_mysql_query($q);
            }
        }
    }


    /**
     * retourne le(s) droit(s) precise(s) de l'utilisateur sur la ressource
     * si $rights=0, retourne tous les droits
     *
     * @param int $usr_id
     * @param int $res_id
     * @param number $rights
     * @return string|number
     */
    public function getRights($usr_id, $res_id, $rights = 0)
    {
        $usr_id = intval($usr_id);
        $res_id = intval($res_id);
        $rights = intval($rights);

        $qr = '';
        if ($rights) {
            $qr = "& $rights";
        }

        //recherche de surcharge sur les droits d'acces
        $q = "select (usr_rights " . $qr . ")
            from acces_usr_" . $this->dom['id'] . "
            where usr_num =" . $usr_id . " and res_prf_num = (select distinct res_prf_num from acces_res_" . $this->dom['id'] . " where res_num =" . $res_id . ")";
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            return pmb_mysql_result($r, 0, 0);
        }

        $usr_prf = $this->getUserProfile($usr_id);
        $q = "select ((res_rights ^ res_mask) " . $qr . ") from acces_res_" . $this->dom['id'] . " where res_num=" . $res_id . " and usr_prf_num IN (" . implode(",", $usr_prf) . ")";
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            while ($row = pmb_mysql_fetch_array($r)) {
                if ($row[0]) {
                    return $row[0];
                }
            }
        }
        return 0;
    }


    /**
     * retourne le(s) droit(s) d'un profil utilisateur sur un profil ressource
     *
     * @param number $usr_prf_num
     * @param number $res_prf_num
     * @return string|number
     */
    public function getDomainRights($usr_prf_num = 0, $res_prf_num = 0)
    {
        $q = "select dom_rights from acces_rights where res_prf_num=" . $res_prf_num . " and usr_prf_num=" . $usr_prf_num . " and dom_num=" . $this->dom['id'];
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            return pmb_mysql_result($r, 0, 0);
        }
        return 0;
    }


    /**
     * retourne le(s) droit(s) d'un utilisateur
     *
     * @param int $usr_id
     * @param int $usr_prf
     *
     * @return array [usr_prf_num][res_prf_num]=rights
     */
    public function get_user_rights($usr_id, $usr_prf = [])
    {
        if (empty($usr_prf)) {
            $usr_prf = $this->getUserProfile($usr_id);
        }
        $t_r = array();

        $q = "select res_prf_num, usr_rights from acces_usr_" . $this->dom['id'] . " where usr_num=" . $usr_id . " ";
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            while ($row = pmb_mysql_fetch_object($r)) {
                foreach ($usr_prf as $prf) {
                    $t_r[$prf][$row->res_prf_num] = $row->usr_rights;
                }
            }
        } else {
            $t_d = $this->loadDomainRights();
            foreach ($usr_prf as $prf) {
                if ($t_d[$prf]) {
                    $t_r[$prf] = $t_d[$prf];
                } else {
                    $t_r = $t_d[0];
                }
            }
        }
        return $t_r;
    }


    /**
     * retourne une requete donnant la liste des ressources repondant aux criteres passes
     *
     * @param int $usr_id
     * @param string $rights
     * @return string
     */
    public function getResourceList($usr_id = 0, $rights = '')
    {
        $usr_prf = $this->getUserProfile($usr_id);
        $q = "select res_num from acces_res_" . $this->dom['id'] . " where usr_prf_num IN (" . implode(",", $usr_prf) . ") and ((res_rights ^ res_mask) & " . $rights . ") ";
        return $q;
    }


    /**
     * retourne la jointure a utiliser pour recuperer les ressources correspondant aux criteres passes
     *
     * @param int $usr_id
     * @param int $rights
     * @param string $join_field
     * @return string
     */
    public function getJoin($usr_id, $rights, $join_field)
    {
        $usr_id = intval($usr_id);
        //recherche de surcharge sur les droits d'acces
        $q = "select count(*) from acces_usr_" . $this->dom['id'] . " where usr_num=" . $usr_id . " ";
        $r = pmb_mysql_query($q);

        if (pmb_mysql_result($r, 0, 0)) {
            $j = " join acces_res_" . $this->dom['id'] . " on " . $join_field . "=acces_res_" . $this->dom['id'] . ".res_num and acces_res_" . $this->dom['id'] . ".usr_prf_num=0 ";
            $j .= "join acces_usr_" . $this->dom['id'] . " on acces_res_" . $this->dom['id'] . ".res_prf_num=acces_usr_" . $this->dom['id'] . ".res_prf_num and acces_usr_" . $this->dom['id'] . ".usr_num=" . $usr_id . " ";
            $j .= "and (acces_usr_" . $this->dom['id'] . ".usr_rights & " . $rights . ") ";
        } else {
            $usr_prf = $this->getUserProfile($usr_id);
            $j = " join acces_res_" . $this->dom['id'] . " on " . $join_field . "=acces_res_" . $this->dom['id'] . ".res_num and acces_res_" . $this->dom['id'] . ".usr_prf_num IN (" . implode(",", $usr_prf) . ") and ((acces_res_" . $this->dom['id'] . ".res_rights ^ acces_res_" . $this->dom['id'] . ".res_mask) & " . $rights . ") ";
        }
        return $j;
    }


    public function getFilterQuery($usr_id = 0, $rights = '', $field = '', $ids = '')
    {
        $usr_id = intval($usr_id);
        //recherche de surcharge sur les droits d'acces
        $q = "select count(*) from acces_usr_" . $this->dom['id'] . " where usr_num=" . $usr_id . " ";
        $r = pmb_mysql_query($q);

        if (pmb_mysql_result($r, 0, 0)) {
            $j = "select res_num as " . $field . " from acces_res_" . $this->dom['id'] . " ar ";
            $j .= "join acces_usr_" . $this->dom['id'] . " au on ar.res_prf_num=au.res_prf_num and usr_num=" . $usr_id . " ";
            $j .= "where usr_prf_num=0 and (usr_rights & " . $rights . ") and res_num in (" . $ids . ") ";
        } else {
            $usr_prf = $this->getUserProfile($usr_id);
            $j = $this->getFilterQueryByProfile($usr_prf, $rights, $field, $ids);
        }
        return $j;
    }


    /**
     * Retourne la requete de calcul de droits en fonction d'un profile et des droits a tester
     */
    public function getFilterQueryByProfile($usr_prf = "", $rights = "", $field = "", $ids = "")
    {
        return "select res_num as ".$field." from acces_res_".$this->dom['id']." where usr_prf_num IN (".implode(",", $usr_prf).") and ((res_rights ^ res_mask) & ".$rights.") and res_num in (".$ids.")";
    }


    /**
     * calcul du role utilisateur
     *
     * @param number $usr_id
     * @return number[]|NULL[]
     */
    public function getUserProfile($usr_id = 0)
    {
        $usr_id = intval($usr_id);
        //Recuperation des regles
        $q = $this->loadUserProfiles();
        $r = pmb_mysql_query($q);
        $prf_id = [];

        if (pmb_mysql_num_rows($r)) {
            $t_rules = array();
            $prf_used = array();
            $t_var = array();
            while (($row = pmb_mysql_fetch_object($r))) {
                $t_rules[$row->prf_id] = unserialize($row->prf_rule);
                $prf_used[$row->prf_id] = $row->prf_used;
            }

            //recuperation des variables necessaires au calcul
            foreach ($this->dom['user']['property_link']['r_query'] as $k_var => $v_var) {
                switch ($v_var['type']) {
                    case 'var':
                        $x = $v_var['value'];
                        global ${$x};
                        $t_var[$k_var] = ${$x};
                        break;
                    case 'session':
                        $x = $v_var['value'];
                        $t_var[$k_var] = (isset($_SESSION[$x]) ? $_SESSION[$x] : '');
                        break;
                    case 'field':
                        $t_var[$k_var] = 0;
                        if ($usr_id !== 0) {
                            $q = "select ".$v_var['value']." from ".$this->dom['user']['ref']['name']." where ".$this->dom['user']['ref']['key']."=".$usr_id.($this->dom['user']['ref']['clause'] ? " and ".$this->dom['user']['ref']['clause'] : "" );
                            $r = pmb_mysql_query($q);
                            if (pmb_mysql_num_rows($r)) {
                                $t_var[$k_var] = pmb_mysql_result($r, 0, 0);
                            }
                        }
                        break;
                    case 'sql':
                        $t_var[$k_var] = 0;
                        if ($usr_id !== 0) {
                            $q = str_replace('!!usr_id!!', $usr_id, $v_var['value']);
                            $r = pmb_mysql_query($q);
                            if (pmb_mysql_num_rows($r)) {
                                $t_var[$k_var] = [];
                                while ($row = pmb_mysql_fetch_array($r)) {
                                    $t_var[$k_var][] = $row[0];
                                }
                                //$t_var[$k_var]= pmb_mysql_result($r,0,0);
                            }
                        }
                        break;
                    case 'callable':
                        $t_var[$k_var] = call_user_func_array(array($v_var["class"], $v_var["method"]), [$usr_id]);
                        break;
                    default:
                        break;
                }
            }
            unset($v_var);

            //Quelle est la regle qui s'applique ?
            foreach ($t_rules as $k_rule => $v_rule) {
                $result = true;
                //construction de la regle
                $prev_p_id = 0;
                foreach ($v_rule['search'] as $k_s => $v_s) {
                    $p_id = substr($v_s, 2);
                    $var_value = 0;
                    if (isset($t_var[$p_id])) {
                        $var_value = $t_var[$p_id];
                    }
                    $t_values = $t_rules[$k_rule]['field_' . $prev_p_id . '_f_' . $p_id];
                    if (is_array($var_value)) {
                        $found = false;
                        for ($i = 0; $i < count($var_value); $i++) {
                            if (in_array($var_value[$i], $t_values)) {
                                $found = true;
                                break;
                            }
                        }
                        if ($found == false) {
                            $result = false;
                            break;
                        }
                    } else {
                        if (!in_array($var_value, $t_values)) {
                            $result = false;
                            break;
                        }
                    }
                    $prev_p_id = $p_id;
                }
                if ($result) { //Toutes les conditions sont reunies, on se sauve
                    $prf_id[] = $prf_used[$k_rule];
                }
            }
        }
        if (empty($prf_id)) {
            $prf_id = [0];
        }
        return $prf_id;
    }


    /**
     * Lecture du profil courant ressource
     *
     * Lit le profil courant de la ressource dans la table acces_res_? si existant
     * sinon retourne une valeur calculee (creation)
     *
     */
    public function getResourceProfile($res_id = 0)
    {
        if ($res_id) {
            $q = "select res_prf_num from acces_res_" . $this->dom['id'] . " join acces_profiles on dom_num='" . $this->dom['id'] . "' and prf_type='1' and prf_id=res_prf_num ";
            $q .= "where res_num = " . $res_id . " limit 1";

            $r = pmb_mysql_query($q);
            if (pmb_mysql_num_rows($r)) {
                return pmb_mysql_result($r, 0, 0);
            }
        }
        return $this->defineResourceProfile($res_id, false);
    }


    /**
     * lecture du nom du profil ressource
     *
     * @param int $res_prf
     * @return string
     */
    public function getResourceProfileName($res_prf)
    {
        $q = "select prf_name from acces_profiles where prf_id='$res_prf' ";
        $r = pmb_mysql_query($q);
        if (pmb_mysql_num_rows($r)) {
            return pmb_mysql_result($r, 0, 0);
        }
        return $this->getComment('res_prf_def_lib');
    }


    /**
     * definition du profil pour la ressource en creation
     *
     * @param number $res_id
     * @param bool $force_c_query : force l'utilisation des criteres de creation
     * @return number|number|NULL
     */
    public function defineResourceProfile($res_id = 0, bool $force_c_query = false)
    {
        //Recuperation des regles
        $q = $this->loadResourceProfiles();
        $r = pmb_mysql_query($q);
        $prf_id = 0;
        if (pmb_mysql_num_rows($r)) {
            $t_rules = array();
            $prf_used = array();
            $t_var = array();
            while (($row = pmb_mysql_fetch_object($r))) {
                $t_rules[$row->prf_id] = unserialize($row->prf_rule);
                $prf_used[$row->prf_id] = $row->prf_used;
            }

            //recuperation des variables necessaires au calcul
            $_query = ($force_c_query) ? 'c_query' : 'i_query';

            foreach ($this->dom['resource']['property_link'][$_query] as $k_var => $v_var) {
                switch ($v_var['type']) {
                    case 'var':
                        $x = $v_var['value'];
                        global ${$x};
                        $t_var[$k_var] = ${$x};
                        break;
                    case 'session':
                        $x = $v_var['value'];
                        $t_var[$k_var] = constant($x);
                        break;
                    case 'field':
                        if ($res_id == 0) {
                            return $prf_id;
                        }
                        $q = "select ".$v_var['value']." from ".$this->dom['resource']['ref']['name']." where ".$this->dom['resource']['ref']['key']."=".$res_id.($this->dom['resource']['ref']['clause'] ? " and ".$this->dom['resource']['ref']['clause'] : "");
                        $r = pmb_mysql_query($q);
                        if (pmb_mysql_num_rows($r)) {
                            $t_var[$k_var] = pmb_mysql_result($r, 0, 0);
                        } else {
                            $t_var[$k_var] = 0;
                        }
                        break;
                    case 'sql':
                        if ($res_id == 0) {
                            return $prf_id;
                        }
                        $q = str_replace('!!res_id!!', $res_id, $v_var['value']);
                        $r = pmb_mysql_query($q);
                        if (pmb_mysql_num_rows($r)) {
                            $t_var[$k_var] = pmb_mysql_result($r, 0, 0);
                        } else {
                            $t_var[$k_var] = 0;
                        }
                        break;
                    case 'callable':
                        if ($res_id == 0) {
                            return $prf_id;
                        }
                        $t_var[$k_var] = call_user_func_array(array($v_var["class"], $v_var["method"]), [$res_id]);
                        break;
                    default:
                        break;
                }
            }
            unset($v_var);

            //Quelle est la regle qui s'applique ?
            $prf_id = 0;
            foreach ($t_rules as $k_rule => $v_rule) {
                $result = TRUE;
                //construction de la regle
                $prev_p_id = 0;
                foreach ($v_rule['search'] as $v_s) {
                    $p_id = substr($v_s, 2);

                    $var_value = $t_var[$p_id];
                    $t_values = $t_rules[$k_rule]['field_' . $prev_p_id . '_f_' . $p_id];

                    if (!in_array($var_value, $t_values)) {
                        $result = FALSE;
                        break;
                    }
                    $prev_p_id = $p_id;
                }
                if ($result == TRUE) { //Toutes les conditions sont reunies, on se sauve
                    $prf_id = $prf_used[$k_rule];
                    break;
                }
            }
        }
        return $prf_id;
    }


    public function delRessource($res_id = 0)
    {
        if (!$res_id) {
            return;
        }
        $q = "delete from acces_res_" . $this->dom['id'] . " where res_num=" . $res_id;
        pmb_mysql_query($q);
    }


    /**
     * generation des droits par profil utilisateur pour une ressource
     *
     * @param int $res_id
     * @param number $chk_sav_spe_rights
     */
    public function applyRessourceRights($res_id, $chk_sav_spe_rights = 0)
    {
        $res_prf = $this->getResourceProfile($res_id);

        //recuperation des droits/profils utilisateurs pour le profil ressource en cours de traitement
        $q1 = "select usr_prf_num, dom_rights from acces_rights where dom_num=" . $this->dom['id'] . " and res_prf_num=" . $res_prf . " ";
        $r1 = pmb_mysql_query($q1);
        $t_d = array();
        if (pmb_mysql_num_rows($r1)) {
            while (($row = pmb_mysql_fetch_object($r1))) {
                $t_d[$row->usr_prf_num] = $row->dom_rights;
            }
        }

        //pour chaque profil utilisateur
        foreach ($t_d as $k_d => $v_d) {
            $q2 = "select res_mask from acces_res_" . $this->dom['id'] . " where res_num=" . $res_id . " and usr_prf_num=" . $k_d;
            $r2 = pmb_mysql_query($q2);
            if (pmb_mysql_num_rows($r2)) {
                $q3 = "update acces_res_" . $this->dom['id'] . " set res_prf_num=" . $res_prf . ", res_rights=" . $v_d;
                if (!$chk_sav_spe_rights) $q3 .= ", res_mask=0";
                $q3 .= " where res_num=" . $res_id . " and usr_prf_num=" . $k_d;
            } else {
                $q3 = "insert into acces_res_" . $this->dom['id'] . " set res_num=" . $res_id . ", res_prf_num=" . $res_prf . ", usr_prf_num=" . $k_d . ", res_rights=" . $v_d . ", res_mask=0";
            }
            pmb_mysql_query($q3);
        }
    }


    /**
     * surcharge des droits utilisateurs
     *
     * @param number $usr_id
     * @param number $override_rights
     * @param array $chk_rights
     */
    public function override_user_rights($usr_id = 0, $override_rights = 0, $chk_rights = array())
    {
        $usr_id = intval($usr_id);
        $override_rights = intval($override_rights);
        $chk_rights = is_array($chk_rights) ? $chk_rights : [];

        if ($usr_id && $override_rights && count($chk_rights)) {
            switch ($override_rights) {

                case '1': //creation surcharge

                    //TODO Il faut pouvoir surcharger
                    if (!empty($chk_rights[$this->dom['id']])) {

                        //recuperation des profils ressources
                        $q_res = $this->loadUsedResourceProfiles();
                        $r_res = pmb_mysql_query($q_res);
                        $t_res = array();
                        $t_res[0] = 0;
                        if (pmb_mysql_num_rows($r_res)) {
                            while (($row = pmb_mysql_fetch_object($r_res))) {
                                $t_res[] = $row->prf_id;
                            }
                        }

                        //recuperation des droits generiques  du domaine pour avoir les droits globaux
                        $t_r = $this->getDomainRights();

                        //recuperation de la liste de tous les controles
                        $t_ctl = $this->getControls(2);
                        //pour chaque profil ressource
                        foreach ($t_res as $k_res => $v_res) {

                            //representation decimale des droits utilisateur/ressource
                            $b_r = 0;
                            if (count($t_ctl)) {
                                foreach ($t_ctl as $k_ctl => $v_ctl) {
                                    if (($this->dom['controls'][$k_ctl]['global'] == 'yes') && ($t_r & $this->dom['controls'][$k_ctl]['id'])) {
                                        $b_r = $b_r + pow(2, ($k_ctl - 1));
                                    } elseif(!empty($chk_rights[$this->dom['id']][$v_res]) && !empty($chk_rights[$this->dom['id']][$v_res][$k_ctl]) && ($chk_rights[$this->dom['id']][$v_res][$k_ctl] == 1)) {
                                        $b_r = $b_r + pow(2, ($k_ctl - 1));
                                    }
                                }
                            }

                            $q = "replace into acces_usr_" . $this->dom['id'] . " set usr_num=" . $usr_id . ",  res_prf_num=" . $v_res . ", usr_rights=" . $b_r . " ";
                            pmb_mysql_query($q);
                        }
                    }

                    break;

                case '2': //suppression surcharge

                    $q = "delete from acces_usr_" . $this->dom['id'] . " where usr_num=" . $usr_id . " ";
                    pmb_mysql_query($q);
                    break;
            }
        }
    }


    protected function get_store($store_name)
    {
        if (empty(self::$store)) {
            $store_config = array(
                /* db */
                'db_name' => DATA_BASE,
                'db_user' => USER_NAME,
                'db_pwd' => USER_PASS,
                'db_host' => SQL_SERVER,
                /* store */
                'store_name' => $this->dom['resource']['ref']['name'],
                /* stop after 100 errors */
                'max_errors' => 100,
                'store_strip_mb_comp_str' => 0
            );
            self::$store = new onto_store_arc2($store_config);
        }
        return self::$store;
    }
}


class accesParser
{
    public $parser;

    public $t = array();

    public $prev_tag = '';

    public $prev_id = '';

    public $path_tag = array();

    public $text = '';


    public function run($file)
    {
        global $charset;

        //Recherche du fichier XML de description
        $subst_file = str_replace('.xml', '_subst.xml', $file);
        if (is_readable($subst_file)) {
            $file = $subst_file;
        }
        $xml = file_get_contents($file, "r") or die(htmlentities("Can't find XML file $file", ENT_QUOTES, $charset));

        unset($this->t);
        $m = [];
        $rx = "/<?xml.*encoding=[\'\"](.*?)[\'\"].*?>/m";
        if (preg_match($rx, $xml, $m)) {
            $encoding = strtoupper($m[1]);
        } else {
            $encoding = "ISO-8859-1";
        }

        $this->parser = xml_parser_create($encoding);
        xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, $charset);
        xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
        xml_parser_set_option($this->parser, XML_OPTION_SKIP_WHITE, true);
        xml_set_element_handler($this->parser, [$this, "tagStart"], [$this, "tagEnd"]);
        xml_set_character_data_handler($this->parser, [$this, "texte"]);

        if (!xml_parse($this->parser, $xml, true)) {
            die(sprintf(
                "XML error (%s) on line: %d",
                xml_error_string(xml_get_error_code($this->parser)),
                xml_get_current_line_number($this->parser)
            ));
        }
        $this->parser = null;
        return $this->t;
    }


    public function tagStart($parser, $tag, $att)
    {
        $this->prev_tag = end($this->path_tag);
        $this->path_tag[] = $tag;
        switch ($tag) {

            //lecture catalog.xml
            case 'catalog':
                break;
            case 'item':
                $this->t[$att['id']]['id'] = $att['id'];
                $this->t[$att['id']]['path'] = $att['path'];
                break;

                //lecture domain.xml
            case 'user':
                $this->t['user']['lib'] = $this->getMsg($att['lib']);
                $this->t['user']['properties'] = $att['properties'];
                break;
            case 'resource':
                $this->t['resource']['lib'] = $this->getMsg($att['lib']);
                $this->t['resource']['properties'] = $att['properties'];
                break;
            case 'ref':
                if ($this->prev_tag == 'resource') {
                    $this->t['resource']['ref']['type'] = $att['type'];
                    $this->t['resource']['ref']['name'] = $att['name'];
                    $this->t['resource']['ref']['key'] = $att['key'];
                    $this->t['resource']['ref']['clause'] = (isset($att['clause']) ? $att['clause'] : '');
                    $this->t['resource']['ref']['value'] = (isset($att['value']) ? $att['value'] : '');
                }
                if ($this->prev_tag == 'user') {
                    $this->t['user']['ref']['type'] = $att['type'];
                    $this->t['user']['ref']['name'] = $att['name'];
                    $this->t['user']['ref']['key'] = $att['key'];
                    $this->t['user']['ref']['clause'] = (isset($att['clause']) ? $att['clause'] : '');
                }
                if ($this->prev_tag == 'property') {
                    $this->t['properties'][$this->prev_id]['ref']['type'] = $att['type'];
                    $this->t['properties'][$this->prev_id]['ref']['name'] = $att['name'];
                    $this->t['properties'][$this->prev_id]['ref']['key'] = (isset($att['key']) ? $att['key'] : '');
                    $this->t['properties'][$this->prev_id]['ref']['value'] = (isset($att['value']) ? $att['value'] : '');
                    $this->t['properties'][$this->prev_id]['ref']['clause'] = (isset($att['clause']) ? $att['clause'] : '');
                    $this->t['properties'][$this->prev_id]['ref']['class'] = (isset($att['class']) ? $att['class'] : '');
                    $this->t['properties'][$this->prev_id]['ref']['method'] = (isset($att['method']) ? $att['method'] : '');
                }
                break;
            case 'property_link':
                $this->prev_id = $att['with'];
                if (!empty($att['enable'])) {
                    $this->t['enable'][$att['with']] = $att['enable'];
                }
                break;
            case 'c_query':
            case 'r_query':
            case 'i_query':
                $tag_2 = $this->path_tag[count($this->path_tag) - 3];
                $this->t[$tag_2][$this->prev_tag][$tag][$this->prev_id]['type'] = $att['type'];
                $this->t[$tag_2][$this->prev_tag][$tag][$this->prev_id]['value'] = (isset($att['value']) ? $att['value'] : '');
                $this->t[$tag_2][$this->prev_tag][$tag][$this->prev_id]['class'] = (isset($att['class']) ? $att['class'] : '');
                $this->t[$tag_2][$this->prev_tag][$tag][$this->prev_id]['method'] = (isset($att['method']) ? $att['method'] : '');
                break;
            case 'properties':
                break;
            case 'property':
                $this->t['properties'][$att['id']]['id'] = $att['id'];
                $this->t['properties'][$att['id']]['lib'] = $att['lib'];
                $this->prev_id = $att['id'];
                break;
            case 'controls':
                break;
            case 'control':
                $this->t['controls'][$att['id']]['id'] = $att['id'];
                $this->t['controls'][$att['id']]['lib'] = $att['lib'];
                $this->t['controls'][$att['id']]['global'] = (isset($att['global']) ? $att['global'] : '');
                $this->prev_id = $att['id'];
                break;
            default:
                break;
        }
        $this->text = '';
    }


    public function tagEnd($parser, $tag)
    {
        if (!count($this->path_tag)) {
            return;
        }
        if (trim($this->text) !== '') {
            $this->t[$tag] = $this->text;
        }
        array_pop($this->path_tag);
    }


    public function texte($parser, $data)
    {
        if (!count($this->path_tag)) {
            return;
        }
        $this->text .= $data;
    }


    public function getMsg($code)
    {
        global $msg;

        if (substr($code, 0, 4) == 'msg:') {
            if (empty($msg[substr($code, 4)])) {
                return '';
            }
            return $msg[substr($code, 4)];
        }
        return $code;
    }
}


/**
 * fonction de comparaison des regles pour tri de la liste
 */
function cmpRules($a, $b)
{
    return (strcmp($a['hrule'], $b['hrule']));
}

