<?php

// +-------------------------------------------------+
// | 2002-2007 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: plugins.class.php,v 1.18.6.1.2.2 2026/02/25 15:11:00 qvarin Exp $

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

global $class_path, $include_path;
require_once $include_path.'/parser.inc.php';
require_once($class_path.'/plugin.class.php');

/**
 * Classe de gestion du sysètme de plugins
 * @author arenou
 *
 */
class plugins
{
    private static $_instance = null;
    private static $messages = [];
    protected $plugins = [];
    private static $plugins_instances = [];

    /**
     * Constructeur...
     */
    private function __construct()
    {
        $this->parse();
    }

    public static function get_instance()
    {
        if (is_null(self::$_instance)) {
            self::$_instance = new plugins();
        }
        return self::$_instance;
    }

    public static function get_plugin_instance($plugin_name)
    {
        if (!isset(self::$plugins_instances[$plugin_name])) {
            self::$plugins_instances[$plugin_name] = new plugin($plugin_name);
        }
        return self::$plugins_instances[$plugin_name];
    }

    /**
     * Méthode de parcours du répertoire de plugins...
     * Elle déclenche l'analyse de chaque plugin présent
     */
    private function parse()
    {
        global $base_path;
        if (file_exists($base_path.'/plugins')) {
            $dh = opendir($base_path.'/plugins');
            while (($plugin = readdir($dh)) !== false) {
                if ($plugin != "." && $plugin != ".." && $plugin != "CVS") {
                    if (is_dir($base_path.'/plugins/'.$plugin) && $this->is_activated($plugin)) {
                        $this->analyze($base_path.'/plugins/'.$plugin);
                    }
                }
            }
        }
    }

    /**
     * Détermine si un plugin est activé ou non. Toujours vrai pour le moment, cela permet d'envisager une évolution plus tard!
     * @param string $plugin_path
     * @return bool
     */
    private function is_activated($plugin)
    {
        return true;
    }

    /**
     * Méthode d'analyse d'un plugin
     * @param string $plugin_path
     */
    private function analyze($plugin_path)
    {
        if (file_exists($plugin_path.'/manifest.xml')) {
            $parse = _parser_text_no_function_(file_get_contents($plugin_path.'/manifest.xml'));
            $manifest = $parse['MANIFEST']['0'];
            $this->plugins[basename($plugin_path)] = [];
            $this->plugins[basename($plugin_path)]['name'] = $manifest['NAME'][0]['value'];
            if ($manifest['AUTHOR']) {
                $this->plugins[basename($plugin_path)]['author'] = [];
                if ($manifest['AUTHOR'][0]['NAME']) {
                    $this->plugins[basename($plugin_path)]['author']['name'] = $manifest['AUTHOR'][0]['NAME'][0]['value'];
                }
                if ($manifest['AUTHOR'][0]['ORGANISATION']) {
                    $this->plugins[basename($plugin_path)]['author']['organisation'] = $manifest['AUTHOR'][0]['ORGANISATION'][0]['value'];
                }
            }
            if ($manifest['CREATED_DATE']) {
                $this->plugins[basename($plugin_path)]['created_date'] = $manifest['CREATED_DATE'][0]['value'];
            }
            if ($manifest['VERSION']) {
                $this->plugins[basename($plugin_path)]['version'] = $manifest['VERSION'][0]['value'];
            }
            if (isset($manifest['MENUS'][0]['MENU']) && is_array($manifest['MENUS'][0]['MENU'])) {
                $this->plugins[basename($plugin_path)]['menus'] = [];
                for ($i = 0 ; $i < count($manifest['MENUS'][0]['MENU']) ; $i++) {
                    $menu = $manifest['MENUS'][0]['MENU'][$i];

                    $this->plugins[basename($plugin_path)]['menus'][$menu['MODULE']] = [];
                    if (isset($menu['TABS']) && is_array($menu['TABS'])) {

                        for ($j = 0 ; $j < count($menu['TABS'][0]['TAB']) ; $j++) {
                            $this->plugins[basename($plugin_path)]['menus'][$menu['MODULE']][$menu['TABS'][0]['TAB'][$j]['ID']] = [
                                'name' => $menu['TABS'][0]['TAB'][$j]['value'],
                                'items' => [],
                                'conditions' => []
                            ];
                        }

                        if (isset($menu['CONDITION']) && is_array($menu['CONDITION'][0])) {
                            foreach ($menu['CONDITION'][0] as $conditionType => $conditions) {
                                $this->append_conditions(
                                    $this->plugins[basename($plugin_path)]['menus'][$menu['MODULE']],
                                    $conditionType,
                                    $conditions
                                );
                            }
                        }

                        if (isset($menu['ITEMS']) && is_array($menu['ITEMS']) && is_array($menu['ITEMS'][0]['ITEM'])) {
                            $items = $menu['ITEMS'][0]['ITEM'];
                            foreach ($items as $xml_item) {
                                $item = [];
                                $item['sub'] = $xml_item['SUB'];
                                $item['name'] = $xml_item['NAME'];
                                if (isset($xml_item['TITLE'])) {
                                    $item['title'] = $xml_item['TITLE'][0]['value'];
                                }
                                if (isset($xml_item['HMENU']) && is_array($xml_item['HMENU']) && is_array($xml_item['HMENU'][0]['ITEM'])) {
                                    $item['hmenu'] = [];
                                    for ($k = 0 ; $k < count($xml_item['HMENU'][0]['ITEM']) ; $k++) {
                                        $args = '';
                                        foreach ($xml_item['HMENU'][0]['ITEM'][$k] as $key => $value) {
                                            if ($key != 'value') {
                                                if ($args) {
                                                    $args .= "&";
                                                }
                                                $args .= $key.'='.$value;
                                            }
                                        }
                                        $item['hmenu'][strtolower($args)] = $xml_item['HMENU'][0]['ITEM'][$k]['value'];
                                    }
                                }
                                $this->plugins[basename($plugin_path)]['menus'][$menu['MODULE']][$xml_item['TAB']]['items'][] = $item;
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Méthode qui construit le menu pour un module de PMB
     * @param string $module
     */
    public function get_display_tabs($module)
    {
        global $charset;
        $html = '';

        $plugins_tabs = $this->get_tabs($module);
        foreach ($plugins_tabs as $plugin_name => $tabs) {
            $html .= '
            <h3 onclick="menuHide(this,event)">'.htmlentities($tabs['section_label'], ENT_QUOTES, $charset).'</h3>
            <ul>';
            foreach ($tabs['items'] as $tab) {
                $html .= '
                <li><a href="'.$module.'.php?categ=plugin&plugin='.$plugin_name.'&sub='.$tab['sub'].'">'.htmlentities(self::check_for_msg($plugin_name, $tab['name']), ENT_QUOTES, $charset).'</a></li>';
            }
            $html .= '
            </ul>';
        }
        return $html;
    }

    /**
     * Méthode qui retourne les menus pour un module de PMB
     * @param string $module
     */
    public function get_tabs($module)
    {
        $tabs = [];

        foreach ($this->plugins as $plugin_name => $plugin_infos) {
            if (isset($plugin_infos['menus']) && is_array($plugin_infos['menus']) && isset($plugin_infos['menus'][$module]) && is_array($plugin_infos['menus'][$module])) {
                foreach ($plugin_infos['menus'][$module] as $tab) {
                    if ($this->check_conditions($tab)) {
                        if (!isset($tabs[$plugin_name])) {
                            $tabs[$plugin_name] = [];
                        }

                        $tabs[$plugin_name]['section_label'] = self::check_for_msg($plugin_name, $tab['name']);
                        $tabs[$plugin_name]['items'] = $tab['items'];
                    }
                }
            }
        }
        return $tabs;
    }

    private function get_context_menu($module, $plugin, $sub)
    {
        global $charset;
        $html = '';
        if (is_array($this->plugins[$plugin]) && $this->is_activated($plugin) && isset($this->plugins[$plugin]['menus'][$module])) {
            foreach ($this->plugins[$plugin]['menus'][$module] as $menu) {
                for ($i = 0 ; $i < count($menu['items']) ; $i++) {
                    if ($menu['items'][$i]['sub'] == $sub && isset($menu['items'][$i]['hmenu'])) {
                        if (isset($menu['items'][$i]['title'])) {
                            $html = "<h1>".htmlentities(self::check_for_msg($plugin, $menu['items'][$i]['title']), ENT_QUOTES, $charset)."</h1>";
                        }
                        $html .= '
                        <div class="hmenu">';
                        foreach ($menu['items'][$i]['hmenu'] as $query => $label) {
                            $html .= '
                            <span'.ongletSelect('categ=plugin&plugin='.$plugin.'&sub='.$sub.'&'.$query).'>
                                <a title="'.htmlentities(self::check_for_msg($plugin, $label), ENT_QUOTES, $charset).'" href="'.$module.'.php?categ=plugin&plugin='.$plugin.'&sub='.$sub.'&'.$query.'">'.htmlentities(self::check_for_msg($plugin, $label), ENT_QUOTES, $charset).'</a>
                            </span>';
                        }
                        $html .= '
                        </div>';
                        return $html;
                    }
                }
            }
        }
        return '';
    }

    public static function check_for_msg($plugin, $code)
    {
        if (strpos($code, 'msg:') !== false) {
            return self::get_message($plugin, str_replace('msg:', '', $code));
        }
        return $code;
    }

    public function proceed($module, $plugin, $sub, $layout = "!!menu_contextuel!!")
    {
        global $base_path, $dest;

        $module = plugins::clean_string($module);
        $plugin = plugins::clean_string($plugin);
        $sub = plugins::clean_string($sub);

        if (strpos($layout, '!!menu_contextuel!!') !== false) {
            $layout = str_replace('!!menu_contextuel!!', $this->get_context_menu($module, $plugin, $sub), $layout);
        }
        switch ($dest) {
            case "TABLEAU":
            case "TABLEAUCSV":
                break;
            default:
                print $layout;
                break;
        }

        if (file_exists($base_path . '/plugins/'.$plugin.'/'.$module.'/main.inc.php')) {
            return $base_path . '/plugins/'.$plugin.'/'.$module.'/main.inc.php';
        }
        return false;
    }

    public function proceed_ajax($module, $plugin, $sub)
    {
        global $base_path;

        $module = plugins::clean_string($module);
        $plugin = plugins::clean_string($plugin);

        if (file_exists($base_path.'/plugins/'.$plugin.'/'.$module.'/ajax_main.inc.php')) {
            return $base_path.'/plugins/'.$plugin.'/'.$module.'/ajax_main.inc.php';
        }
        return false;
    }

    public static function clean_string($string)
    {
        if ($string) {
            return str_replace(' ', '', pmb_alphabetic('^a-z0-9_\-\s', ' ', pmb_strtolower($string)));
        }
    }

    public static function get_message($plugin, $code)
    {
        global $base_path,$msg, $lang;
        if (!isset(self::$messages[$plugin])) {
            if (file_exists($base_path.'/plugins/'.$plugin.'/includes/messages/'.$lang.'.xml')) {
                $xml = new XMLlist($base_path.'/plugins/'.$plugin.'/includes/messages/'.$lang.'.xml');
                $xml->analyser();
                self::$messages[$plugin] = $xml->table;
            }
        }
        if (isset(self::$messages[$plugin][$code])) {
            return self::$messages[$plugin][$code];
        }
        if (isset($msg[$code])) {
            return $msg[$code];
        }
        return $code;
    }

    public static function get_all_messages($plugin)
    {
        global $base_path, $lang;
        if (!isset(self::$messages[$plugin])) {
            if (file_exists($base_path.'/plugins/'.$plugin.'/includes/messages/'.$lang.'.xml')) {
                $xml = new XMLlist($base_path.'/plugins/'.$plugin.'/includes/messages/'.$lang.'.xml');
                $xml->analyser();
                self::$messages[$plugin] = $xml->table;
            }
        }
        return self::$messages[$plugin];
    }

    public function get_plugins()
    {
        return $this->plugins;
    }

    protected function append_conditions(&$module, $conditionType, $conditions)
    {
        foreach ($conditions as $condition) {
            $condition['TYPE'] = strtolower($conditionType);
            $condition = array_change_key_case($condition, CASE_LOWER);

            if (isset($condition['tab']) && !isset($module[$condition['tab']])) {
                // condition associee a un tab qui n'existe pas
                continue;
            }

            if (isset($condition['tab'])) {
                $module[$condition['tab']]['conditions'][] = $condition;
            } else {
                array_walk($module, function (&$tabMenu) use ($condition) {
                    $tabMenu['conditions'][] = $condition;
                });
            }
        }
    }

    protected function check_conditions(array $tab): bool
    {
        if (isset($tab['conditions']) && is_array($tab['conditions'])) {
            foreach ($tab['conditions'] as $condition) {
                switch ($condition['type']) {
                    case 'param':
                        global ${$condition['name']};
                        if (!(${$condition['name']} == $condition['value'])) {
                            return false;
                        }
                        break;

                    case 'auth':
                        $access_rights = explode('|', $condition['value']);
                        $access_rights = array_map(function (string $name) {
                            return defined($name) ? constant($name) : 0;
                        }, $access_rights);

                        $required_access_rights = 0;
                        foreach ($access_rights as $access_right) {
                            $required_access_rights |= $access_right;
                        }

                        if (!($required_access_rights & SESSrights)) {
                            return false;
                        }
                        break;
                }
            }
        }
        return true;
    }
}
