<?php

/**
 * This file is part of LEPTON Core, released under the GNU GPL
 * Please see LICENSE and COPYING files in your package for details, specially for terms and warranties.
 *
 * NOTICE:LEPTON CMS Package has several different licenses.
 * Please see the individual license in the header of each single file or info.php of modules and templates.
 *
 * @author          LEPTON Project
 * @copyright       2010-2021 LEPTON Project
 * @link            https://lepton-cms.org
 * @license         http://www.gnu.org/licenses/gpl.html
 * @license_terms   please see LICENSE and COPYING files in your package
 *
 */


/**
 *  Class to handle the "admin" in the backend.
 *  The basic "jobs" of this class is e.g. to build/construct the mainmenu
 *  and the footer of the backend-interface.
 *
 */
class LEPTON_admin extends LEPTON_core
{

    /**
     *  The db-handle of this class.
     *
     *  @access	private
     *  @type   PDO handle
     */
    private $database = NULL;
	
    /**
     *  Public header storrage for external/internal files of the used modules.
     *
     *  @access	public
     *  @type   array
     */
    public $header_storrage = array('css' => array(), 'js' => array(), 'html' => array(), 'modules' => array());

    /**
     *  Output storage, needed e.g. inside method print_footer for the leptoken-hashes and/or droplets.
     *
     *  @access	private
     *  @type   string
     */
    private $html_output_storage = "";

    /**
     *	Private flag for the droplets.
     *
     *	@access	private
     *  @type   boolean
     */
    private $droplets_ok = false;

    /**
     * Internal Twig-template-engine instance
     * @var object
     * @since 4.3
     */
    public $oTWIG = NULL;
    
    /**
     *  @type    object  The reference to the *Singleton* instance of this class.
     *  @notice         Keep in mind that a child-object has to have his own one!
     */
    static $instance;

    static private $bUserGotAdminRights = NULL;

    /**
     *  Return the instance of this class.
     *
     *  @param  string  $section_name       The name of the backend-section (e.g. Pages, Addons, Media, Access)
     *  @param  string  $section_permission The permission we want to archive, oiow the current backend interface, e.g. pages
     *  @param  boolean $auto_header        Echos the header of the backend-interface.
     *  @param  boolean $auto_auth          Automatic auth of the current user.
     *  @return object                      The generated instance of theis class
     */
    public static function getInstance() // ="Pages", $section_permission = 'start', $auto_header = true, $auto_auth = true )
    {
        if (null === static::$instance)
        {
            $section_name       = "Pages";
            $section_permission = "start";
            $auto_header        = true;
            $auto_auth          = true;

            switch( func_num_args() )
            {
                case 1:
                    $section_name = func_get_arg(0);
                    break;
                case 2:
                    $section_name = func_get_arg(0);
                    $section_permission = func_get_arg(1);
                    break;
                case 3:
                    $section_name = func_get_arg(0);
                    $section_permission = func_get_arg(1);
                    $auto_header = func_get_arg(2);
                    break;
                case 4:
                    $section_name = func_get_arg(0);
                    $section_permission = func_get_arg(1);
                    $auto_header = func_get_arg(2);
                    $auto_auth = func_get_arg(3);
                    break;
                default:
                    // nothing
                    break;
            }
            static::$instance = new static( $section_name, $section_permission, $auto_header, $auto_auth );
        }
        return static::$instance;
    }
    /**
     *	Constructor of the class
     *
     *	Authenticate user then auto print the header
     *
     *	@param	string  $section_name       The section name.
     *	@param	string  $section_permission The section permissions belongs too.
     *	@param	boolean $auto_header        Boolean to print out the header. Default is 'true'.
     *	@param	boolean $auto_auth          Boolean for the auto authentification. Default is 'true'.
     *
     */
    public function __construct($section_name, $section_permission = 'start', $auto_header = true, $auto_auth = true)
    {
        global $database, $MESSAGE, $section_id, $page_id;

        parent::__construct();

        static::$instance = $this;

        $section_id = (isset ($_POST['section_id'])? intval($_POST['section_id']): 0);
        if ($section_id == 0 ){
            $section_id = (isset ($_GET['section_id'])? intval($_GET['section_id']): 0);
        }

        $page_id = (isset ($_POST['page_id'])? intval($_POST['page_id']): 0);
        if ($page_id == 0 ){
            $page_id = (isset ($_GET['page_id'])? intval($_GET['page_id']): 0);
        }

        /**	*********************
         *  TWIG Template Engine
         */
        if($this->oTWIG === NULL)
        {
            $this->oTWIG = lib_twig_box::getInstance();
            $this->oTWIG->loader->prependPath( THEME_PATH."/templates/", "theme" );
        }
        /**	********
         *  End Twig
         */

        /**
         *	Droplet support
         *
         */
        ob_start();

        $this->database = LEPTON_database::getInstance();

        // Specify the current applications name
        $this->section_name       = $section_name;
        $this->section_permission = $section_permission;
        // Authenticate the user for this application
        if ($auto_auth === true)
        {
            // First check if the user is logged-in
            if ($this->is_authenticated() === false)
            {
                header('Location:'.ADMIN_URL.'/login/index.php');
                exit();
            }

            // Now check whether he has a valid token
            if (!$this->checkToken())
            {
                $pin_set = $this->database->get_one("SELECT `pin_set` FROM `".TABLE_PREFIX."users` WHERE `user_id` = '".$_SESSION['USER_ID']."' ");
                if($pin_set == 2)
                {
                    $this->database->simple_query("UPDATE `".TABLE_PREFIX."users` SET `pin_set` = 1 WHERE user_id = '".$_SESSION['USER_ID']."' ");
                }
                unset($_SESSION['USER_ID']);
                header('Location:'.ADMIN_URL.'/login/index.php');
                exit();
            }

            // Now check if they are allowed in this section
            if ($this->get_permission($section_permission) === false)
            {
                die($MESSAGE['ADMIN_INSUFFICIENT_PRIVELLIGES']);
            }
        }

        // Check if the backend language is also the selected language. If not, send headers again.
        $user_language = array();
        $this->database->execute_query(
            "SELECT `language` FROM `" . TABLE_PREFIX . "users` WHERE `user_id` = '" . (int) $this->get_user_id() . "'",
            true,
            $user_language,
            false
        );
        // prevent infinite loop if language file is not XX.php (e.g. DE_du.php)
        $user_language = (!isset($user_language['language']))
            ? "" 
            : substr($user_language['language'], 0,2)
            ;

        // obtain the admin folder (e.g. /admin)
        $admin_folder      = str_replace(LEPTON_PATH, '', ADMIN_PATH);
        if ((LANGUAGE != $user_language) && file_exists(LEPTON_PATH . '/languages/' . $user_language . '.php') && strpos($_SERVER['SCRIPT_NAME'], $admin_folder . '/') !== false)
        {
            // check if page_id is set
            $page_id_url    = (isset($_GET['page_id'])) ? '&page_id=' . (int) $_GET['page_id'] : '';
            $section_id_url = (isset($_GET['section_id'])) ? '&section_id=' . (int) $_GET['section_id'] : '';
            if (isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING'] != '') // check if there is an query-string
            {
                header('Location: ' . $_SERVER['SCRIPT_NAME'] . '?lang=' . $user_language . $page_id_url . $section_id_url . '&' . $_SERVER['QUERY_STRING']);
            }
            else
            {
                header('Location: ' . $_SERVER['SCRIPT_NAME'] . '?lang=' . $user_language . $page_id_url . $section_id_url);
            }
            exit();
        }

        // Auto header code
        if ($auto_header === true)
        {
            $this->print_header();
        }
    }

    /**
     *	Return a system permission
     *
     *	@param	string  $name   A name.
     *	@param	string  $type   A type - default is 'system'
     *  @return boolean
     */
    public function get_permission($name, $type = 'system')
    {
        // Append to permission type
        $type .= '_permissions';
        // Check if we have a section to check for
        if ($name === 'start')
        {
            return true;
        }
        else
        {
            if(true === self::userHasAdminRights())
            {
                return true;
            }

            switch(strtolower($type))
            {
                case "system_permissions":
                    $aTemp = ( $this->get_session('SYSTEM_PERMISSIONS') ?? [] );
                    break;

                case "module_permissions":
                    $aTemp = ( $this->get_session('MODULE_PERMISSIONS') ?? [] );
                    break;

                case "template_permissions":
                    $aTemp = ( $this->get_session('TEMPLATE_PERMISSIONS') ?? [] );
                    break;

                default:
                    $aTemp = [];

            }
            return in_array( $name, $aTemp);

        }
    }

    /**
     *  Get details from the database about a given user (id)
     *
     *  @param  integer $user_id    A valid user-id.
     *  @return array   Assoc. array with the username and displayname
     */
    public static function get_user_details($user_id)
    {
        $user = array();
        LEPTON_database::getInstance()->execute_query(
            "SELECT `username`,`display_name` FROM `" . TABLE_PREFIX . "users` WHERE `user_id` = '".$user_id."'",
            true,
            $user,
            false
        );

        if (empty($user))
        {
            $user['display_name'] = 'Unknown';
            $user['username']     = 'unknown';
        }
        return $user;
    }

    /**
     *  Get details about a given page via id
     *
     *  @param  integer $page_id    A valid page_id - pass by reference!
     *  @return array   An assoc array with id, title, menu_title and modif. dates.
     *
     */
    public function get_page_details(&$page_id)
    {
        $aResults = [];
        $this->database->execute_query(
            "SELECT * from ".TABLE_PREFIX."pages WHERE page_id = '".$page_id."' ",
            true,
            $aResults,
            false
        );

        if (true === $this->database->is_error())
        {
            $this->print_header();
            $this->print_error($this->database->get_error());
        }
        if (true === empty($aResults))
        {
            $this->print_header();
            $this->print_error($GLOBALS['MESSAGE']['PAGES_NOT_FOUND']);
        }
        return $aResults;
    }

    /**
     *	Function get_page_permission takes either a numerical page_id,
     *	upon which it looks up the permissions in the database,
     *	or an array with keys admin_groups and admin_users
     *
     *  @param  integer $page   A valid page_id
     *  @param  string  $action Currend backend or fronetnd user (default "admin")
     *  @return boolean
     *
     */
    public function get_page_permission($page, $action = 'admin')
    {
        if ($action != 'viewing')
        {
            $action = 'admin';
        }

        $action_groups = $action . '_groups';
        $action_users  = $action . '_users';
        if (is_array($page))
        {
            $groups = $page[$action_groups];
            $users  = $page[$action_users];
        }
        else
        {
            $results = array();
            $this->database->execute_query(
                "SELECT ".$action_groups.",".$action_users." FROM " . TABLE_PREFIX . "pages WHERE page_id = ".$page ,
                true,
                $results,
                false
            );
            $groups  = explode(',', str_replace('_', '', $results[$action_groups]));
            $users   = explode(',', str_replace('_', '', $results[$action_users]));
        }

        $in_group = FALSE;
        foreach ($this->get_groups_id() as $cur_gid)
        {
            if (in_array($cur_gid, $groups))
            {
                $in_group = TRUE;
            }
        }
        if ((!$in_group) && !is_numeric(array_search($this->get_user_id(), $users)))
        {
            return false;
        }
        return true;
    }

    /**
     *	Returns a system permission for a menu link
     *
     *  @param  string  A valid menu item name
     *  @return boolean
     *
     */
    public function get_link_permission($title)
    {
        $title  = strtolower(str_replace('_blank', '', $title));

        // Set system permissions var
        $system_permissions = $this->get_session('SYSTEM_PERMISSIONS');

        if ($title === 'start')
        {
            return true;
        }
        else
        {
            // Return true if system perm = 1
            return (is_numeric(array_search($title, $system_permissions)));
        }
    }

    /**
     *	Print the admin header
     *
     */
    public function print_header()
    {
        // Get vars from the language file
        global $MENU;
        global $MESSAGE;
        global $TEXT;

        // Get website title
        $title = $this->database->get_one("SELECT `value` FROM `" . TABLE_PREFIX . "settings` WHERE `name`='website_title'");

        $charset = (true === defined('DEFAULT_CHARSET')) ? DEFAULT_CHARSET : 'utf-8';

        // Work out the URL for the 'View menu' link in the WB backend
        // if the page_id is set, show this page otherwise show the root directory of WB
        $view_url = LEPTON_URL;
        if (isset($_GET['page_id']))
        {
            // Extract page link from the database
            $result = $this->database->get_one("SELECT `link` FROM `" . TABLE_PREFIX . "pages` WHERE `page_id`= '" . (int) addslashes($_GET['page_id']) . "'");
            if ($result != NULL)
            {
                $view_url .= PAGES_DIRECTORY . $result. PAGE_EXTENSION;
            }
        }

        /**
         *	Try to get the current version of the backend-theme from the database
         *
         */
        $backend_theme_version = "";
        if (defined('DEFAULT_THEME'))
        {
            $backend_theme_version = $this->database->get_one("SELECT `version` from `" . TABLE_PREFIX . "addons` where `directory`='" . DEFAULT_THEME . "'");
        }

        $header_vars = array(
            'SECTION_NAME' => $MENU[strtoupper($this->section_name)],
            'WEBSITE_TITLE' => $title,
            'BACKEND_TITLE' => BACKEND_TITLE,
            'TEXT_ADMINISTRATION' => $TEXT['ADMINISTRATION'],
            'CURRENT_USER' => $MESSAGE['START_CURRENT_USER'],
            'DISPLAY_NAME' => $this->get_display_name(),
            'CHARSET' => $charset,
            'LANGUAGE' => strtolower(LANGUAGE),
            'LEPTON_VERSION' => LEPTON_VERSION,
            'SUBVERSION' => SUBVERSION,
            'CORE' => CORE,
            'LEPTON_URL' => LEPTON_URL,
            'ADMIN_URL' => ADMIN_URL,
            'THEME_URL' => THEME_URL,
            'TITLE_START' => $MENU['START'],
            'TITLE_VIEW' => $MENU['VIEW'],
            'TITLE_HELP' => $MENU['HELP'],
            'TITLE_LOGOUT' => $MENU['LOGOUT'],
// additional marker links/text in semantic BE-header
            'PAGES' => $MENU['PAGES'],
            'MEDIA'	 => $MENU['MEDIA'],
            'ADDONS' => $MENU['ADDONS'],
            'PREFERENCES' => $MENU['PREFERENCES'],
            'SETTINGS' => $MENU['SETTINGS'],
            'ADMINTOOLS' => $MENU['ADMINTOOLS'],
            'ACCESS' => $MENU['ACCESS'],
// end additional marks
            'URL_VIEW' => $view_url,
            'URL_HELP' => ' https://www.lepton-cms.org/',
            'BACKEND_MODULE_FILES' => get_page_headers('backend', false),
            'THEME_VERSION' => $backend_theme_version,
            'THEME_NAME' => DEFAULT_THEME,

            //	permissions
            'p_pages'	=> $this->get_link_permission('pages'),
            'p_media'	=> $this->get_link_permission('media'),
            'p_addons'	=> $this->get_link_permission('addons'),
            'p_preferences' => $this->getUserPermission('preferences'), // true, // Keep in mind: preferences are always 'shown' as managed from the login of the user.
            'p_settings'	=> $this->get_link_permission('settings'),
            'p_admintools'	=> $this->get_link_permission('admintools'),
            'p_access'		=> $this->get_link_permission('access')
        );

        echo $this->oTWIG->render(
            '@theme/header.lte',
            $header_vars
        );
    }

    /**
     *  Print the admin backend footer
     *
     */
    public function print_footer()
    {
        $footer_vars = array(
            'BACKEND_BODY_MODULE_JS' => get_page_footers('backend'),
            'LEPTON_URL' => LEPTON_URL,
            'LEPTON_PATH' => LEPTON_PATH,
            'ADMIN_URL' => ADMIN_URL,
            'THEME_URL' => THEME_URL
        );

        echo $this->oTWIG->render(
        	"@theme/footer.lte",
        	$footer_vars
        );

        /**
         *	Droplet support
         *
         */
        $this->html_output_storage = ob_get_clean();
        if (true === $this->droplets_ok)
        {
            $this->html_output_storage = evalDroplets($this->html_output_storage);
        }

        // CSRF protection - add tokens to internal links
        if ($this->is_authenticated() && file_exists(LEPTON_PATH . '/framework/functions/function.addTokens.php'))
        {
            include_once LEPTON_PATH . '/framework/functions/function.addTokens.php';
            if (function_exists('addTokens'))
            {
                addTokens($this->html_output_storage, $this);
            }
        }

        echo $this->html_output_storage;
    }

    /**
     *	Print a success message which then automatically redirects the user to another page
     *
     *	@param	mixed	$message        A string within the message, or an array with a couple of messages.
     *	@param	string	$redirect       A redirect url. Default is "index.php".
     *	@param	bool	$auto_footer    An optional flag to 'print' the footer. Default is true.
     *
     */
    public function print_success($message, $redirect = 'index.php', $auto_footer = true)
    {
        global $TEXT;
        global $section_id;

        LEPTON_abstract::saveLastEditSection();

        if (true === is_array($message))
        {
            $message = implode("<br />", $message);
        }

        // add template variables
        $page_vars = array(
            'NEXT' => $TEXT['NEXT'],
            'BACK' => $TEXT['BACK'],
            'MESSAGE' => $message,
            'THEME_URL' => THEME_URL,
            'REDIRECT' => $redirect,
            'REDIRECT_TIMER' => REDIRECT_TIMER
        );

        echo $this->oTWIG->render(
            '@theme/success.lte',
            $page_vars
        );

        if (true === $auto_footer)
        {
            $this->print_footer();
        }
        exit();
    }

    /**
     *	Print an error message
     *
     *	@param	mixed	$message        A string or an array within the error messages.
     *	@param	string	$link           A redirect url. Default is "index.php".
     *	@param	bool	$auto_footer    An optional boolean to 'print' the footer. Default is true;
     *
     */
    public function print_error($message, $link = 'index.php', $auto_footer = true)
    {
        global $TEXT;

        LEPTON_abstract::saveLastEditSection();

        if( true === is_array($message) )
        {
            $message = implode("<br />", $message);
        }

        $page_vars = array(
            'MESSAGE' => $message,
            'LINK'	=> $link,
            'BACK'	=> $TEXT['BACK'],
            'THEME_URL' => THEME_URL
        );

        echo $this->oTWIG->render(
            '@theme/error.lte',
            $page_vars
        );

        if( true === $auto_footer &&  method_exists($this, "print_footer"))
        {
            $this->print_footer();
        }
        exit();
    }

    /**
     *  Test a given permission-name against the systempermissions.
     *
     *  @param  string  $sPermissionName    Any valid permission-name, e.g. "backend_access"
     *
     *  @return boolean
     */
    static public function getUserPermission( $sPermissionName = "" )
    {
        if(!isset($_SESSION['SYSTEM_PERMISSIONS']))
        {
            return false;
        }
        return ( in_array( $sPermissionName, $_SESSION['SYSTEM_PERMISSIONS'] ) );
    }

    static public function userHasAdminRights()
    {
        if(self::$bUserGotAdminRights == NULL)
        {
            //  Current user has admin rights?
            $aUser = explode(",", ($_SESSION['GROUPS_ID'] ?? ""));
            self::$bUserGotAdminRights = (in_array(1, $aUser));
        }

        return self::$bUserGotAdminRights;
    }
}
