<?php

/**
 * i-doit
 *
 * Quick configuration wizard module
 *
 * @package     i-doit
 * @subpackage  Modules
 * @author      Van Quyen Hoang <qhoang@i-doit.org>
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 * @since       1.0
 */
class isys_module_quick_configuration_wizard extends isys_module implements isys_module_interface
{
    const DISPLAY_IN_MAIN_MENU   = false;
    const DISPLAY_IN_SYSTEM_MENU = false;

    /**
     * @var  boolean
     */
    protected static $m_licenced = true;

    /**
     * Instance of module DAO.
     *
     * @var  isys_quick_configuration_wizard_dao
     */
    protected $m_dao;

    /**
     * Variable which holds the database component.
     *
     * @var  isys_component_database
     */
    protected $m_db = null;

    /**
     * Standard encoding.
     *
     * @var  string
     */
    protected $m_encoding = 'utf-8';

    /**
     * User request.
     *
     * @var  isys_module_request
     */
    protected $m_userrequest;

    /**
     * Static factory method for instant method chaining.
     *
     * @static
     * @return  isys_module_quick_configuration_wizard
     */
    public static function factory()
    {
        return new self;
    }

    /**
     * Gets all profile files in an array
     *
     * @static
     * @return array
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    public static function get_config_files()
    {
        global $g_absdir;

        $l_dir_handle = opendir($g_absdir . '/imports/qcw');

        $l_file_arr = [];
        while ($l_file = readdir($l_dir_handle)) {
            if ($l_file == '.' || $l_file == '..' || substr($l_file, strlen($l_file) - 4, strlen($l_file)) != '.xml') {
                continue;
            }

            $l_file_arr[] = $l_file;
        }

        return $l_file_arr;
    }

    /**
     * Deletes the specified profile file
     *
     * @static
     *
     * @param   string $p_file_name
     *
     * @return  bool
     * @author  Van Quyen Hoang <qhoang@i-doit.org>
     */
    public static function delete_config_file($p_file_name)
    {
        if ($p_file_name != '' && substr($p_file_name, strlen($p_file_name) - 4, strlen($p_file_name)) == '.xml') {
            global $g_absdir;

            $l_file_name = $g_absdir . '/imports/qcw/' . $p_file_name;

            if (file_exists($l_file_name)) {
                return unlink($l_file_name);
            } else {
                return false;
            }
        }

        return true;
    }

    //////////////////////////////////
    //			START EXPORT		//
    //////////////////////////////////

    /**
     * Initiates module.
     *
     * @param isys_module_request $p_req
     */
    public function init(isys_module_request $p_req)
    {
        $this->m_userrequest = $p_req;
    }

    /**
     * Builds menu tree.
     *
     * @param  isys_component_tree $p_tree
     * @param  boolean             $p_system_module (optional) Is it a system module? Defaults to true.
     * @param  integer             $p_parent        (optional) Parent identifier. Defaults to null.
     */
    public function build_tree(isys_component_tree $p_tree, $p_system_module = true, $p_parent = null)
    {
        global $g_config;

        $l_tmpget[C__CMDB__GET__VIEWMODE] = C__CMDB__VIEW__LIST_OBJECTTYPE;

        $i = 0;
        if ($p_system_module) {
            $l_root = $p_parent;
        } else {
            $l_root = $p_tree->add_node(C__MODULE__QCW . ++$i, $p_parent, isys_application::instance()->container->get('language')
                ->get('LC__CMDB__OBJTYPE__CONFIGURATION_MODUS'));
        }

        $p_tree->set_tree_sort(false);

        if (isys_auth_system::instance()
            ->is_allowed_to(isys_auth::EXECUTE, 'GLOBALSETTINGS/QCW')) {
            $p_tree->add_node(C__MODULE__QCW . ++$i, $l_root, isys_application::instance()->container->get('language')
                ->get('LC__CMDB__TREE__SYSTEM__CMDB_CONFIGURATION__QOC'), $g_config['www_dir'] . 'index.php?' .
                (($p_system_module) ? C__GET__MODULE_ID . '=' . C__MODULE__SYSTEM . "&" . C__GET__MODULE_SUB_ID . "=" . C__MODULE__QCW : C__GET__MODULE_ID . "=" .
                    C__MODULE__QCW) . '&' . C__GET__TREE_NODE . '=' . C__MODULE__QCW . $i, null, 'images/icons/silk/application_form_edit.png', 0, '', '');
        }

    }

    /**
     * Start method.
     *
     * @global  array $index_includes
     * @author  Van Quyen Hoang <qhoang@i-doit.org>
     * @author  Leonard Fischer <lfischer@i-doit.org>
     * @return  isys_module_events|void
     */
    public function start()
    {
        global $index_includes, $g_absdir;

        if (isset($_GET['dl']) && !empty($_GET['dl'])) {
            // Remove slashes for a little bit more security...
            $_GET['dl'] = str_replace([
                '/',
                '\\'
            ], '', $_GET['dl']);

            if (!file_exists($g_absdir . '/imports/qcw/' . $_GET['dl'])) {
                unset($_GET['dl']);

                header('Location: ' . isys_helper_link::create_url($_GET));
            }

            isys_factory::get_instance('isys_component_filemanager')
                ->set_upload_path($g_absdir . '/imports/qcw/')
                ->send($_GET['dl']);
        }

        $l_tree = $this->m_userrequest->get_menutree();

        if ($_GET[C__GET__MODULE_SUB_ID] != C__MODULE__QCW) {
            $this->build_tree($l_tree, false, -1);
        }

        isys_auth_system_globals::instance()
            ->qcw(isys_auth::EXECUTE);

        $l_alphabetical_sort = (isys_tenantsettings::get('cmdb.registry.object_type_sorting', C__CMDB__VIEW__OBJECTTYPE_SORTING__AUTOMATIC) ==
            C__CMDB__VIEW__OBJECTTYPE_SORTING__AUTOMATIC);

        $this->m_userrequest->get_template()
            ->activate_editmode()
            ->assign('encType', 'multipart/form-data')
            ->assign('obj_type_sorting_alphabetical', $l_alphabetical_sort)
            ->assign('menu_tree', $l_tree->process($_GET[C__GET__TREE_NODE]));

        isys_component_template_navbar::getInstance()
            ->append_button('LC__MODULE__QCW__PROFILE_POPUP', 'profile_popup', [
                'tooltip'    => 'LC__MODULE__QCW__PROFILE_POPUP_TOOLTIP',
                'icon'       => 'icons/silk/application_cascade.png',
                'js_onclick' => "get_popup('qcw_profiles', '', '800', '550', {}, '');"
            ]);

        $index_includes['contentbottomcontent'] = $this->get_template_dir() . 'quick_configuration_wizard.tpl';
    }

    /**
     * This function generates a xml file with the current objecttype configuration.
     *
     * @param   string $p_config_title
     *
     * @return  string
     * @throws  Exception
     * @author  Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function generate_export($p_config_title)
    {
        global $g_absdir;

        if ($p_config_title == '') {
            $p_config_title = 'default';
        }

        $l_config_title = isys_helper_upload::prepare_filename($p_config_title);

        $l_qcw_folder = '/imports/qcw/';
        $l_dir = $g_absdir . $l_qcw_folder;
        $l_filename = $l_config_title . '.xml';
        $l_full_filename = $l_dir . $l_filename;

        // Check if folder is writable
        if (is_writeable($l_dir)) {
            $l_export = $this->export_current_config($l_full_filename);
            if (!$l_export) {
                throw new isys_exception_general('An error occurred while exporting the config.');
            }
        } else {
            throw new isys_exception_general('Please check the rights of the folder "' . $l_dir . '".');
        }

        return $l_full_filename;
    }

    /**
     * This function creates the xml file
     *
     * @param                         $p_file
     *
     * @return int
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function export_current_config($p_file)
    {

        $l_xml = "<?xml version=\"1.0\" encoding=\"" . $this->m_encoding . "\" standalone=\"yes\"?>\n" . "<isys_export>\n" . $this->export_objtype_groups() . "</isys_export>";

        return file_put_contents($p_file, $l_xml);
    }

    /**
     * This function appends the objecttype groups to the xml file
     *
     * @todo    Use a XML class - NOT string concatenation...
     * @author  Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function export_objtype_groups()
    {
        $l_root = '';
        $l_object_type_group_result = $this->m_dao->load_object_type_groups();

        while ($l_objtype_group = $l_object_type_group_result->get_row()) {
            $l_root .= "\t<objecttypegroup>\n";
            $l_root .= $this->format_objtype_group_head($l_objtype_group);
            $l_root .= $this->export_objtypes($l_objtype_group['isys_obj_type_group__const']);
            $l_root .= "\t</objecttypegroup>\n";
        }

        return $l_root;
    }

    /**
     * This function adds the objecttype group header information to the xml file
     *
     * @param             $p_data
     * @param DOMDocument $p_doc
     * @param             $p_parent
     *
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function format_objtype_group_head($p_data)
    {
        $l_root = "\t\t<head>\n";

        foreach ($p_data AS $l_key => $l_data) {
            $l_tag = str_replace('isys_obj_type_group__', '', $l_key);
            $l_root .= "\t\t\t<" . $l_tag . ">" . $l_data . "</" . $l_tag . ">\n";
        }
        $l_root .= "\t\t</head>\n";

        return $l_root;
    }

    //////////////////////////////////
    //			END EXPORT			//
    //////////////////////////////////

    //////////////////////////////////
    //			START IMPORT		//
    //////////////////////////////////

    /**
     * This function appends the objecttypes to the xml file
     *
     * @param                         $p_objtype_group_const
     *
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function export_objtypes($p_objtype_group_const)
    {
        $l_objtypes = $this->m_dao->load_objecttypes($p_objtype_group_const, true);
        $l_root = "\t\t<objecttypes>\n";
        foreach ($l_objtypes AS $l_row) {
            $l_root .= "\t\t\t<objecttype>\n";
            $l_root .= $this->format_objtype_head($l_row);
            $l_root .= $this->export_assigned_categories($l_row['isys_obj_type__const']);
            $l_root .= "\t\t\t</objecttype>\n";
        }
        $l_root .= "\t\t</objecttypes>\n";

        return $l_root;
    }

    //////////////////////////////////
    //			END IMPORT			//
    //////////////////////////////////

    /**
     * This function adds the objecttype header information to the xml file
     *
     * @param             $p_data
     * @param DOMDocument $p_doc
     * @param             $p_parent
     *
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function format_objtype_head($p_data)
    {
        $l_root = "\t\t\t\t<head>\n";

        $l_show_in_tree = '0';
        $l_status = '1';

        foreach ($p_data AS $l_key => $l_data) {
            if (strpos($l_key, 'isys_obj_type__') !== false) {
                $l_tag = str_replace('isys_obj_type__', '', $l_key);
                switch ($l_tag) {
                    case 'isysgui_cats__id':
                        $l_data = $p_data['isysgui_cats__const'];
                        break;
                    case 'show_in_tree':
                        $l_show_in_tree = $l_data;
                        break;
                    case 'status':
                        $l_status = $l_data;
                        break;
                    default:
                        break;
                }
                $l_root .= "\t\t\t\t\t<" . $l_tag . ">" . $l_data . "</" . $l_tag . ">\n";
            }
        }

        $l_query = 'UPDATE isys_obj_type SET isys_obj_type__show_in_tree = \'' . $l_show_in_tree . '\', isys_obj_type__status = \'' . $l_status .
            '\', isys_obj_type__isys_obj_type_group__id = ' . '(SELECT isys_obj_type_group__id FROM isys_obj_type_group WHERE isys_obj_type_group__const = \'' .
            $p_data['isys_obj_type_group__const'] . '\') ' . 'WHERE isys_obj_type__const = \'' . $p_data['isys_obj_type__const'] . '\'';

        $l_root .= "\t\t\t\t\t<query><![CDATA[" . $l_query . "]]></query>\n";

        $l_root .= "\t\t\t\t</head>\n";

        return $l_root;
    }

    /**
     * This function appends the categories to the xml file
     *
     * @param                         $p_objtype_const
     * @param DOMDocument             $p_doc
     * @param                         $p_parent
     *
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function export_assigned_categories($p_objtype_const)
    {
        $l_categories = $this->m_dao->load_assigned_categories($p_objtype_const);
        $l_root = "\t\t\t\t<assigned_catgories>";

        foreach ($l_categories AS $l_category) {
            $l_root .= $l_category . ",";
        }
        $l_root = rtrim($l_root, ',');
        $l_root .= "</assigned_catgories>\n";

        if (isset($this->m_dao->m_assigned_custom_categories[$p_objtype_const])) {
            $l_root .= "\t\t\t\t<assigned_custom_catgories>";
            $l_root .= "<![CDATA[";
            foreach ($this->m_dao->m_assigned_custom_categories[$p_objtype_const] AS $l_category) {
                $l_root .= serialize($this->m_dao->m_custom_categories[$l_category]) . "||";
            }
            $l_root = rtrim($l_root, '||') . "]]>";
            $l_root .= "</assigned_custom_catgories>\n";
        }

        return $l_root;
    }

    /**
     * Loads the config files and reorganizes the objecttype structure
     *
     * @param                         $p_filename_selection
     * @param isys_caching            $p_caching
     * @param bool                    $p_clear_category_assignments
     *
     * @author Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function load_config($p_filename_selection, isys_caching $p_caching)
    {
        global $g_absdir;

        try {
            $l_dir = $g_absdir . '/imports/qcw/';

            if (!is_array($p_filename_selection)) {
                $l_files_arr = [$p_filename_selection];
            } else {
                $l_files_arr = $p_filename_selection;
            }

            $this->m_dao->disable_all_objecttypes();

            $l_libxml = new isys_import_xml();
            $l_dao_custom = isys_custom_fields_dao::instance(isys_application::instance()->database);

            $l_res = $l_dao_custom->get_data();
            $l_custom_categories = [];
            if ($l_res->num_rows()) {
                while ($l_row = $l_res->get_row()) {
                    $l_custom_categories[$l_row['isysgui_catg_custom__const']] = $l_row['isysgui_catg_custom__id'];
                }
            }

            foreach ($l_files_arr AS $l_filename_item) {
                $l_filename = $l_filename_item;
                $l_full_filename = $l_dir . $l_filename;

                $l_libxml->load_xml_file($l_full_filename);

                $l_xml_data = array_shift($l_libxml->get_xml_data());

                foreach ($l_xml_data AS $l_arr) {
                    foreach ($l_arr AS $l_objtype_group) {
                        $l_objtype_group_const = '';
                        $l_objtype_group_id = '';
                        $l_objtype_group_data = [];

                        $l_head = $l_objtype_group[0]['head'];
                        foreach ($l_head AS $l_key => $l_value) {
                            // skip the id field
                            if ($l_key == 'id') {
                                continue;
                            }

                            if ($l_key == 'const') {
                                $l_objtype_group_const = $l_value;
                                $l_objtype_group_id = $this->m_dao->get_objtype_group_id($l_objtype_group_const);
                            }
                            $l_objtype_group_data['isys_obj_type_group__' . $l_key] = $l_value;
                        }
                        if (!$l_objtype_group_id) {
                            $l_objtype_group_id = $this->m_dao->add_new_objtype_group($l_objtype_group_data);

                            // Just in case if the object type group id is = 0
                            if ((int)$l_objtype_group_id === 0) {
                                $l_objtype_group_id = $this->m_dao->get_objtype_group_id($l_objtype_group_const);
                            }
                        }

                        /* Set status of object group */
                        $this->m_dao->objtypegroup_change_status($l_objtype_group_const, (bool)($l_head['status'] - 1));

                        $l_objtypes = $l_objtype_group[1]['objecttypes'];

                        if (is_array($l_objtypes)) {
                            foreach ($l_objtypes AS $l_objtype) {
                                $l_head = $l_objtype['objecttype'][0]['head'];
                                $l_string_assigned_categories = trim($l_objtype['objecttype']['assigned_catgories']);
                                $l_string_assigned_custom_categories = '';
                                $l_assigned_custom_categories = [];
                                $l_objtype_const = '';
                                $l_assignment_query = '';

                                foreach ($l_head AS $l_key => $l_value) {
                                    switch ($l_key) {
                                        case 'id':
                                            continue 2;
                                            break;
                                        case 'default_template':
                                            // Check if template exists and has status = C__RECORD_STATUS__TEMPLATE
                                            if ($l_value > 0) {
                                                if ((bool)$this->m_dao->retrieve('SELECT isys_obj__id FROM isys_obj WHERE isys_obj__id = ' .
                                                    $this->m_dao->convert_sql_id($l_value) . ' AND isys_obj__status = ' .
                                                    $this->m_dao->convert_sql_int(C__RECORD_STATUS__TEMPLATE) . ';')
                                                    ->num_rows()) {
                                                    $l_objtype_data['isys_obj_type__' . $l_key] = $l_value;
                                                }
                                            }
                                            break;
                                        case 'const':
                                            $l_objtype_const = $l_value;
                                            $l_objtype_data['isys_obj_type__' . $l_key] = $l_value;
                                            break;
                                        case 'query':
                                            $l_assignment_query = $l_value;
                                            break;
                                        default:
                                            $l_objtype_data['isys_obj_type__' . $l_key] = $l_value;
                                            break;
                                    }
                                }
                                $l_objtype_data['isys_obj_type__isys_obj_type_group__id'] = $l_objtype_group_id;

                                $l_check = $this->m_dao->check_objecttype_by_const($l_objtype_const);
                                if (!$l_check) {
                                    $l_objtype_id = $this->m_dao->add_new_objtype($l_objtype_data);
                                } else {
                                    $l_objtype_id = $this->m_dao->get_objtype_id($l_objtype_const);
                                }

                                // Custom categories
                                if (isset($l_objtype['objecttype']['assigned_custom_catgories'])) {
                                    $l_string_assigned_custom_categories = trim($l_objtype['objecttype']['assigned_custom_catgories']);
                                    $l_assigned_custom_categories_res = $l_dao_custom->get_assignments(null, $l_objtype_id);
                                    while ($l_row_cc = $l_assigned_custom_categories_res->get_row()) {
                                        $l_assigned_custom_categories[$l_row_cc['isysgui_catg_custom__const']] = true;
                                    }
                                }

                                // Update objtype group assignment
                                $this->m_dao->update($l_assignment_query);

                                $l_assigned_categories_cached = $p_caching->get($l_objtype_const);
                                if (!$l_assigned_categories_cached) {
                                    $l_assigned_categories_cached = $this->m_dao->cache_objtype($l_objtype_id, $l_objtype_const, $p_caching);
                                }
                                $l_new_assigned_categories = [];
                                $l_new_assigned_custom_categories = [];

                                if ($l_string_assigned_categories != '') {
                                    $l_assigned_categories = [];
                                    $l_raw_assigned_categories = explode(',', $l_string_assigned_categories);
                                    foreach ($l_raw_assigned_categories AS $l_category_const) {
                                        if (strpos($l_category_const, 'C__CATG__CUSTOM_FIELDS') !== false) {
                                            continue;
                                        }

                                        $l_cat_id = $p_caching->get($l_category_const);
                                        if (empty($l_cat_id)) {
                                            $p_caching->add($l_category_const, ($l_cat_id = $this->m_dao->get_catg_id($l_category_const)))
                                                ->save();
                                        }

                                        // Skip categories which were not found for example if a category from a module does not exist then we cannot assign it
                                        if ($l_cat_id) {
                                            $l_assigned_categories[$l_category_const] = $l_cat_id;
                                        }
                                    }

                                    if (!$l_assigned_categories_cached) {
                                        $l_new_assigned_categories = $l_assigned_categories;
                                        $l_assigned_categories_cached = [];
                                    } else {
                                        foreach ($l_assigned_categories AS $l_cat_const => $l_cat_id) {
                                            if (!in_array($l_cat_const, $l_assigned_categories_cached)) {
                                                $l_new_assigned_categories[] = $l_cat_const;
                                            }
                                        }
                                    }
                                }

                                // Handle custom categories
                                if ($l_string_assigned_custom_categories != '') {
                                    $l_raw_assigned_categories = explode('||', $l_string_assigned_custom_categories);

                                    foreach ($l_raw_assigned_categories AS $l_category_info) {
                                        $l_category_data = unserialize($l_category_info);
                                        $l_category_const = $l_category_data[0];
                                        $l_category_title = $l_category_data[1];
                                        $l_category_multivalue = $l_category_data[2];
                                        $l_category_config = unserialize($l_category_data[3]);

                                        if (!isset($l_custom_categories[$l_category_const]) && !defined($l_category_const)) {
                                            // Custom category does not exist create it
                                            $l_custom_id = $l_dao_custom->create($l_category_title, $l_category_config, 0, 0, $l_category_multivalue, $l_category_const);
                                            $l_custom_categories[$l_category_const] = $l_custom_id;

                                            // Assign custom category to object type
                                            $l_dao_custom->assign($l_custom_id, $l_objtype_id);
                                        } else {
                                            // Custom category exists
                                            $l_custom_id = (isset($l_custom_categories[$l_category_const])) ? $l_custom_categories[$l_category_const] : (defined($l_category_const) ? constant($l_category_const) : 0);

                                            // Check if its assigned to the object type
                                            if (!$l_assigned_custom_categories[$l_category_const]) {
                                                // Assign custom category to object type
                                                $l_dao_custom->assign($l_custom_categories[$l_category_const], $l_objtype_id);
                                            }
                                        }

                                        $l_cat_id = $p_caching->get($l_category_const);
                                        if ($l_custom_id > 0) {
                                            if (empty($l_cat_id)) {
                                                $p_caching->add($l_category_const, $l_custom_id)
                                                    ->save();
                                            }

                                            if (!in_array($l_category_const, $l_assigned_categories_cached)) {
                                                $l_new_assigned_custom_categories[] = $l_category_const;
                                            }
                                        }
                                    }
                                }

                                // Assign only global categories categories only if it is needed
                                if (count($l_new_assigned_categories)) {
                                    // Merge categories from selected profile and add them to the cache
                                    $l_assigned_categories_cached = array_merge($l_assigned_categories_cached, $l_new_assigned_categories);
                                    $p_caching->add($l_objtype_const, $l_assigned_categories_cached)
                                        ->save();

                                    // Assign only global categories
                                    $this->m_dao->assign_categories_to_objecttype($l_objtype_id, $l_new_assigned_categories);
                                }
                            }
                        }
                    }
                }
            }
            $this->m_dao->apply_update();
        } catch (Exception $e) {
            isys_notify::warning($e->getMessage());
        }
    }

    /**
     * This methods calls the specified method from the dao class.
     *
     * @param   string $p_method
     * @param   array  $p_params
     *
     * @return  mixed
     * @author  Van Quyen Hoang <qhoang@i-doit.org>
     */
    public function call_dao_method($p_method, $p_params)
    {
        return call_user_func_array([
            $this->m_dao,
            $p_method
        ], $p_params);
    }

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->m_module_id = C__MODULE__QCW;
        $this->m_db = isys_application::instance()->database;
        $this->m_dao = new isys_quick_configuration_wizard_dao($this->m_db);
        parent::__construct();
    }

    /**
     * Method for retrieving the assets WWW dir.
     *
     * @return  string
     */
    public static function get_assets_dir()
    {
        return __DIR__ . '/assets/';
    }
}
