<?php
/* For licensing terms, see /license.txt */

/**
 * Class Plugin
 * Base class for plugins
 *
 * This class has to be extended by every plugin. It defines basic methods
 * to install/uninstall and get information about a plugin
 *
 * @author    Julio Montoya <gugli100@gmail.com>
 * @author    Yannick Warnier <ywarnier@beeznest.org>
 * @author    Laurent Opprecht    <laurent@opprecht.info>
 * @copyright 2012 University of Geneva
 * @license   GNU General Public License - http://www.gnu.org/copyleft/gpl.html
 *
 */
class Plugin
{
    protected $version = '';
    protected $author = '';
    protected $fields = array();
    private $settings = null;
    // Translation strings.
    private $strings = null;
    public $isCoursePlugin = false;
    public $isMailPlugin = false;

    /**
     * When creating a new course, these settings are added to the course, in
     * the course_info/infocours.php
     * To show the plugin course icons you need to add these icons:
     * main/img/icons/22/plugin_name.png
     * main/img/icons/64/plugin_name.png
     * main/img/icons/64/plugin_name_na.png
     * @example
     * $course_settings = array(
          array('name' => 'big_blue_button_welcome_message',  'type' => 'text'),
          array('name' => 'big_blue_button_record_and_store', 'type' => 'checkbox')
       );
     */
    public $course_settings = array();
    /**
     * This indicates whether changing the setting should execute the callback
     * function.
     */
    public $course_settings_callback = false;

    /**
     * Default constructor for the plugin class. By default, it only sets
     * a few attributes of the object
     * @param string $version   of this plugin
     * @param string $author    of this plugin
     * @param array  $settings  settings to be proposed to configure the plugin
     */
    protected function __construct($version, $author, $settings = array())
    {
        $this->version = $version;
        $this->author = $author;
        $this->fields = $settings;

        global $language_files;
        $language_files[] = 'plugin_' . $this->get_name();
    }

    /**
     * Gets an array of information about this plugin (name, version, ...)
     * @return  array Array of information elements about this plugin
     */
    public function get_info()
    {
        $result = array();
        $result['title']            = $this->get_title();
        $result['comment']          = $this->get_comment();
        $result['version']          = $this->get_version();
        $result['author']           = $this->get_author();
        $result['plugin_class']     = get_class($this);
        $result['is_course_plugin'] = $this->isCoursePlugin;
        $result['is_mail_plugin']   = $this->isMailPlugin;

        if ($form = $this->get_settings_form()) {
            $result['settings_form'] = $form;
            foreach ($this->fields as $name => $type) {
                $value = $this->get($name);
                if (is_array($type)) {
                    $value = $type['options'];
                }
                $result[$name] = $value;
            }
        }
        return $result;
    }

    /**
     * Returns the "system" name of the plugin in lowercase letters
     * @return string
     */
    public function get_name()
    {
        $result = get_class($this);
        $result = str_replace('Plugin', '', $result);
        $result = strtolower($result);

        return $result;
    }

    /**
     * Returns the title of the plugin
     * @return string
     */
    public function get_title()
    {
        return $this->get_lang('plugin_title');
    }

    /**
     * Returns the description of the plugin
     * @return string
     */
    public function get_comment()
    {
        return $this->get_lang('plugin_comment');
    }

    /**
     * Returns the version of the plugin
     * @return string
     */
    public function get_version()
    {
        return $this->version;
    }

    /**
     * Returns the author of the plugin
     * @return string
     */
    public function get_author()
    {
        return $this->author;
    }

    /**
     * Returns the contents of the CSS defined by the plugin
     * @return array
     */
    public function get_css()
    {
        $name = $this->get_name();
        $path = api_get_path(SYS_PLUGIN_PATH)."$name/resources/$name.css";
        if (!is_readable($path)) {
            return '';
        }
        $css = array();
        $css[] = file_get_contents($path);
        $result = implode($css);

        return $result;
    }

    /**
     * Returns an HTML form (generated by FormValidator) of the plugin settings
     * @return string FormValidator-generated form
     */
    public function get_settings_form()
    {
        $result = new FormValidator($this->get_name());

        $defaults = array();
        $checkboxGroup = array();
        $checkboxCollection = array();

        if ($checkboxNames = array_keys($this->fields, 'checkbox')) {
            $pluginInfoCollection = api_get_settings('Plugins');
            foreach ($pluginInfoCollection as $pluginInfo) {
                if (array_search($pluginInfo['title'], $checkboxNames) !== false) {
                    $checkboxCollection[$pluginInfo['title']] = $pluginInfo;
                }
            }
        }

        foreach ($this->fields as $name => $type) {
            $options = null;
            if (is_array($type) && isset($type['type']) && $type['type'] === "select") {
                $options = $type['options'];
                $type = $type['type'];
            }

            $value = $this->get($name);

            $defaults[$name] = $value;
            $type = isset($type) ? $type : 'text';

            $help = null;
            if ($this->get_lang_plugin_exists($name.'_help')) {
                $help = $this->get_lang($name.'_help');
                if ($name === "show_main_menu_tab") {
                    $pluginName = strtolower(str_replace('Plugin', '', get_class($this)));
                    $pluginUrl = api_get_path(WEB_PATH)."plugin/$pluginName/index.php";
                    $pluginUrl = "<a href=$pluginUrl>$pluginUrl</a>";
                    $help = sprintf($help, $pluginUrl);
                }
            }

            switch ($type) {
                case 'html':
                    $result->addElement('html', $this->get_lang($name));
                    break;
                case 'wysiwyg':
                    $result->addHtmlEditor($name, $this->get_lang($name), false);
                    break;
                case 'text':
                    $result->addElement($type, $name, array($this->get_lang($name), $help));
                    break;
                case 'boolean':
                    $group = array();
                    $group[] = $result->createElement('radio', $name, '', get_lang('Yes'), 'true');
                    $group[] = $result->createElement('radio', $name, '', get_lang('No'), 'false');
                    $result->addGroup($group, null, array($this->get_lang($name), $help));
                    break;
                case 'checkbox':
                    $selectedValue = null;
                    if (isset($checkboxCollection[$name])) {
                        if ($checkboxCollection[$name]['selected_value'] === 'true') {
                            $selectedValue = 'checked';
                        }
                    }
                    $element = $result->createElement(
                        $type,
                        $name,
                        '',
                        $this->get_lang($name),
                        $selectedValue
                    );
                    $element->_attributes['value'] = 'true';
                    $checkboxGroup[] = $element;
                    break;
                case 'select':
                    $result->addElement(
                        $type,
                        $name,
                        array($this->get_lang($name), $help),
                        $options
                    );
                    break;
            }
        }

        if (!empty($checkboxGroup)) {
            $result->addGroup($checkboxGroup, null, array($this->get_lang('sms_types'), $help));
        }
        $result->setDefaults($defaults);
        $result->addButtonSave($this->get_lang('Save'), 'submit_button');
        return $result;
    }

    /**
     * Returns the value of a given plugin global setting
     * @param string $name of the plugin
     *
     * @return string Value of the plugin
     */
    public function get($name)
    {
        $settings = $this->get_settings();
        foreach ($settings as $setting) {
            if ($setting['variable'] == ($this->get_name() . '_' . $name)) {
                return $setting['selected_value'];
            }
        }

        return false;
    }

    /**
     * Returns an array with the global settings for this plugin
     * @return array Plugin settings as an array
     */
    public function get_settings()
    {
        if (is_null($this->settings)) {
            $settings = api_get_settings_params(
                array(
                    "subkey = ? AND category = ? AND type = ? " => array($this->get_name(), 'Plugins', 'setting')
                )
            );
            $this->settings = $settings;
        }

        return $this->settings;
    }

    /**
     * Tells whether language variables are defined for this plugin or not
     * @param string $name System name of the plugin
     *
     * @return bool True if the plugin has language variables defined, false otherwise
     */
    public function get_lang_plugin_exists($name)
    {
        return isset($this->strings[$name]);
    }

    /**
     * Hook for the get_lang() function to check for plugin-defined language terms
     * @param string $name of the language variable we are looking for
     *
     * @return string The translated language term of the plugin
     */
    public function get_lang($name)
    {
        // Check whether the language strings for the plugin have already been
        // loaded. If so, no need to load them again.

        if (is_null($this->strings)) {
            global $language_interface;
            $root = api_get_path(SYS_PLUGIN_PATH);
            $plugin_name = $this->get_name();

            $interfaceLanguageId = api_get_language_id($language_interface);
            $interfaceLanguageInfo = api_get_language_info($interfaceLanguageId);
            $languageParentId = intval($interfaceLanguageInfo['parent_id']);

            //1. Loading english if exists
            $english_path = $root.$plugin_name."/lang/english.php";
            if (is_readable($english_path)) {
                $strings = array();
                include $english_path;
                $this->strings = $strings;
            }

            $path = $root.$plugin_name."/lang/$language_interface.php";
            //2. Loading the system language
            if (is_readable($path)) {
                include $path;
                if (!empty($strings)) {
                    foreach ($strings as $key => $string) {
                        $this->strings[$key] = $string;
                    }
                }
            } elseif ($languageParentId > 0) {
                $languageParentInfo = api_get_language_info($languageParentId);
                $languageParentFolder = $languageParentInfo['dokeos_folder'];

                include "{$root}{$plugin_name}/lang/{$languageParentFolder}.php";

                if (!empty($strings)) {
                    foreach ($strings as $key => $string) {
                        $this->strings[$key] = $string;
                    }
                }
            }
        }

        if (isset($this->strings[$name])) {
            return $this->strings[$name];
        }

        return get_lang($name);
    }

    /**
     * Caller for the install_course_fields() function
     * @param int $courseId
     * @param boolean $addToolLink Whether to add a tool link on the course homepage
     *
     * @return void
     */
    public function course_install($courseId, $addToolLink = true)
    {
        $this->install_course_fields($courseId, $addToolLink);
    }

    /**
     * Add course settings and, if not asked otherwise, add a tool link on the course homepage
     * @param int $courseId Course integer ID
     * @param boolean $add_tool_link Whether to add a tool link or not
     * (some tools might just offer a configuration section and act on the backend)
     * @return boolean  False on error, null otherwise
     */
    public function install_course_fields($courseId, $add_tool_link = true)
    {
        $plugin_name = $this->get_name();
        $t_course = Database::get_course_table(TABLE_COURSE_SETTING);
        $courseId = intval($courseId);

        if (empty($courseId)) {
            return false;
        }

        // Adding course settings.
        if (!empty($this->course_settings)) {
            foreach ($this->course_settings as $setting) {
                $variable = Database::escape_string($setting['name']);
                $value ='';

                if (isset($setting['init_value'])) {
                    $value = Database::escape_string($setting['init_value']);
                }

                $type = 'textfield';
                if (isset($setting['type'])) {
                    $type = Database::escape_string($setting['type']);
                }

                if (isset($setting['group'])) {
                    $group = Database::escape_string($setting['group']);
                    $sql = "SELECT value FROM $t_course
                            WHERE c_id = $courseId AND variable = '$group' AND subkey = '$variable' ";
                    $result = Database::query($sql);
                    if (!Database::num_rows($result)) {
                        $sql = "INSERT INTO $t_course (c_id, variable, subkey, value, category, type) VALUES
                                ($courseId, '$group', '$variable', '$value', 'plugins', '$type')";
                        Database::query($sql);
                    }
                } else {
                    $sql = "SELECT value FROM $t_course
                            WHERE c_id = $courseId AND variable = '$variable' ";
                    $result = Database::query($sql);
                    if (!Database::num_rows($result)) {
                        $sql = "INSERT INTO $t_course (c_id, variable, value, category, subkey, type) VALUES
                                ($courseId, '$variable','$value', 'plugins', '$plugin_name', '$type')";
                        Database::query($sql);
                    }
                }
            }
        }

        // Stop here if we don't want a tool link on the course homepage
        if (!$add_tool_link) {
            return true;
        }

        //Add an icon in the table tool list
        $t_tool = Database::get_course_table(TABLE_TOOL_LIST);
        $sql = "SELECT name FROM $t_tool
                WHERE c_id = $courseId AND name = '$plugin_name' ";
        $result = Database::query($sql);
        if (!Database::num_rows($result)) {
            $tool_link = "$plugin_name/start.php";
            $visibility = AddCourse::string2binary(api_get_setting('course_create_active_tools', $plugin_name));

            $cToolId = AddCourse::generateToolId($courseId);

            Database::insert(
                $t_tool,
                [
                    'id' => $cToolId,
                    'c_id' => $courseId,
                    'name' => $plugin_name,
                    'link' => $tool_link,
                    'image' => "$plugin_name.png",
                    'visibility' => $visibility,
                    'admin' => 0,
                    'address' => 'squaregrey.gif',
                    'added_tool' => 'NO',
                    'target' => '_self',
                    'category' => 'plugin',
                    'session_id' => 0
                ]
            );
        }
    }

    /**
     * Delete the fields added to the course settings page and the link to the
     * tool on the course's homepage
     * @param int $courseId
     * @return void
     */
    public function uninstall_course_fields($courseId)
    {
        $courseId = intval($courseId);
        if (empty($courseId)) {
            return false;
        }
        $plugin_name = $this->get_name();

        $t_course = Database::get_course_table(TABLE_COURSE_SETTING);
        $t_tool = Database::get_course_table(TABLE_TOOL_LIST);

        if (!empty($this->course_settings)) {
            foreach ($this->course_settings as $setting) {
                $variable = Database::escape_string($setting['name']);
                if (!empty($setting['group'])) {
                    $variable = Database::escape_string($setting['group']);
                }
                if (empty($variable)) {
                    continue;
                }
                $sql = "DELETE FROM $t_course
                        WHERE c_id = $courseId AND variable = '$variable'";
                Database::query($sql);
            }
        }

        $plugin_name = Database::escape_string($plugin_name);
        $sql = "DELETE FROM $t_tool
                WHERE c_id = $courseId AND name = '$plugin_name'";
        Database::query($sql);
    }

    /**
     * Install the course fields and tool link of this plugin in all courses
     * @param boolean Whether we want to add a plugin link on the course homepage
     *
     * @return void
     */
    public function install_course_fields_in_all_courses($add_tool_link = true)
    {
        // Update existing courses to add plugin settings
        $t_courses = Database::get_main_table(TABLE_MAIN_COURSE);
        $sql = "SELECT id FROM $t_courses ORDER BY id";
        $res = Database::query($sql);
        while ($row = Database::fetch_assoc($res)) {
            $this->install_course_fields($row['id'], $add_tool_link);
        }
    }

    /**
     * Uninstall the plugin settings fields from all courses
     * @return void
     */
    public function uninstall_course_fields_in_all_courses()
    {
        // Update existing courses to add conference settings
        $t_courses = Database::get_main_table(TABLE_MAIN_COURSE);
        $sql = "SELECT id FROM $t_courses ORDER BY id";
        $res = Database::query($sql);
        while ($row = Database::fetch_assoc($res)) {
            $this->uninstall_course_fields($row['id']);
        }
    }

    /**
     * @return array
     */
    public function getCourseSettings()
    {
        $settings = array();
        if (is_array($this->course_settings)) {
            foreach ($this->course_settings as $item) {
                if (isset($item['group'])) {
                    if (!in_array($item['group'], $settings)) {
                        $settings[] = $item['group'];
                    }
                } else {
                    $settings[] = $item['name'];
                }
            }
        }

        return $settings;
    }

    /**
     * Method to be extended when changing the setting in the course
     * configuration should trigger the use of a callback method
     * @param array $values sent back from the course configuration script
     *
     * @return void
     */
    public function course_settings_updated($values = array())
    {

    }

   /**
    * Add a tab to platform
    * @param string   $tabName
    * @param string   $url
    *
    * @return boolean
    */
    public function addTab($tabName, $url)
    {
        $sql = "SELECT * FROM settings_current
            WHERE
            variable = 'show_tabs' AND
            subkey like 'custom_tab_%'";
        $result = Database::query($sql);
        $customTabsNum = Database::num_rows($result);

        $tabNum = $customTabsNum + 1;

        //Avoid Tab Name Spaces
        $tabNameNoSpaces = preg_replace('/\s+/', '', $tabName);
        $subkeytext = "Tabs" . $tabNameNoSpaces;


        //Check if it is already added
        $checkCondition = array(
            'where' =>
                array(
                    "variable = 'show_tabs' AND subkeytext = ?" => array(
                        $subkeytext
                    )
                )
        );
        $checkDuplicate = Database::select('*', 'settings_current', $checkCondition);
        if (!empty($checkDuplicate)) {
            return false;
        }
        //End Check
        $subkey = 'custom_tab_' . $tabNum;
        $attributes = array(
            'variable' => 'show_tabs',
            'subkey' => $subkey,
            'type' => 'checkbox',
            'category' => 'Platform',
            'selected_value' => 'true',
            'title' => $tabName,
            'comment' => $url,
            'subkeytext' => $subkeytext,
            'access_url' => 1,
            'access_url_changeable' => 0,
            'access_url_locked' => 0
        );
        $resp = Database::insert('settings_current', $attributes);

        //Save the id
        $settings = $this->get_settings();
        $setData = array (
            'comment' => $subkey
        );
        $whereCondition = array(
            'id = ?' => key($settings)
        );
        Database::update('settings_current', $setData, $whereCondition);

        return $resp;
    }

    /**
     * Delete a tab to chamilo's platform
     * @param string $key
     * @return boolean $resp Transaction response
     */
    public function deleteTab($key)
    {
        $t = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
        $sql = "SELECT *
                FROM $t
                WHERE variable = 'show_tabs'
                AND subkey <> '$key'
                AND subkey like 'custom_tab_%'
                ";
        $resp = $result = Database::query($sql);
        $customTabsNum = Database::num_rows($result);

        if (!empty($key)) {
            $whereCondition = array(
                'variable = ? AND subkey = ?' => array('show_tabs', $key)
            );
            $resp = Database::delete('settings_current', $whereCondition);

            //if there is more than one tab
            //re enumerate them
            if (!empty($customTabsNum) && $customTabsNum > 0) {
                $tabs = Database::store_result($result, 'ASSOC');
                $i = 1;
                foreach ($tabs as $row) {
                    $attributes = array(
                        'subkey' => 'custom_tab_' . $i
                    );
                    $this->updateTab($row['subkey'], $attributes);
                    $i++;
                }
            }
        }

        return $resp;
    }

    /**
     * Update the tabs attributes
     * @param string $key
     * @param array  $attributes
     *
     * @return boolean
     */
    public function updateTab($key, $attributes)
    {
        $whereCondition = array(
            'variable = ? AND subkey = ?' => array('show_tabs', $key)
        );
        $resp = Database::update('settings_current', $attributes, $whereCondition);

        return $resp;
    }

    /**
     * This method shows or hides plugin's tab
     * @param boolean Shows or hides the main menu plugin tab
     * @param string Plugin starter file path
     */
    public function manageTab($showTab, $filePath = 'index.php')
    {
        $langString = str_replace('Plugin', '', get_class($this));
        $pluginName = strtolower($langString);
        $pluginUrl = 'plugin/'.$pluginName.'/'.$filePath;
        if ($showTab === 'true') {
            $tabAdded = $this->addTab($langString, $pluginUrl);
            if ($tabAdded) {
                // The page must be refreshed to show the recently created tab
                echo "<script>location.href = '".Security::remove_XSS($_SERVER['REQUEST_URI'])."';</script>";
            }
        } else {
            $settingsCurrentTable = Database::get_main_table(TABLE_MAIN_SETTINGS_CURRENT);
            $conditions = array(
                'where' => array(
                    "variable = 'show_tabs' AND title = ? AND comment = ? " => array(
                        $langString,
                        $pluginUrl
                    )
                )
            );
            $result = Database::select('subkey', $settingsCurrentTable, $conditions);
            if (!empty($result)) {
                $this->deleteTab($result[0]['subkey']);
            }
        }
    }
}
