aboutsummaryrefslogtreecommitdiff
path: root/srcs/phpmyadmin/libraries/classes/Plugins.php
diff options
context:
space:
mode:
Diffstat (limited to 'srcs/phpmyadmin/libraries/classes/Plugins.php')
-rw-r--r--srcs/phpmyadmin/libraries/classes/Plugins.php633
1 files changed, 633 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/libraries/classes/Plugins.php b/srcs/phpmyadmin/libraries/classes/Plugins.php
new file mode 100644
index 0000000..cd6f6c0
--- /dev/null
+++ b/srcs/phpmyadmin/libraries/classes/Plugins.php
@@ -0,0 +1,633 @@
+<?php
+/* vim: set expandtab sw=4 ts=4 sts=4: */
+/**
+ * Generic plugin interface.
+ *
+ * @package PhpMyAdmin
+ */
+declare(strict_types=1);
+
+namespace PhpMyAdmin;
+
+use PhpMyAdmin\Properties\Options\Groups\OptionsPropertySubgroup;
+use PhpMyAdmin\Properties\Options\Items\BoolPropertyItem;
+use PhpMyAdmin\Properties\Options\Items\DocPropertyItem;
+use PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem;
+use PhpMyAdmin\Properties\Options\Items\MessageOnlyPropertyItem;
+use PhpMyAdmin\Properties\Options\Items\NumberPropertyItem;
+use PhpMyAdmin\Properties\Options\Items\RadioPropertyItem;
+use PhpMyAdmin\Properties\Options\Items\SelectPropertyItem;
+use PhpMyAdmin\Properties\Options\Items\TextPropertyItem;
+use PhpMyAdmin\Properties\Options\OptionsPropertyItem;
+use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
+use PhpMyAdmin\Properties\Plugins\PluginPropertyItem;
+use PhpMyAdmin\Properties\Plugins\SchemaPluginProperties;
+use PhpMyAdmin\Util;
+
+/**
+ * PhpMyAdmin\Plugins class
+ *
+ * @package PhpMyAdmin
+ */
+class Plugins
+{
+ /**
+ * Includes and instantiates the specified plugin type for a certain format
+ *
+ * @param string $plugin_type the type of the plugin (import, export, etc)
+ * @param string $plugin_format the format of the plugin (sql, xml, et )
+ * @param string $plugins_dir directory with plugins
+ * @param mixed $plugin_param parameter to plugin by which they can
+ * decide whether they can work
+ *
+ * @return object|null new plugin instance
+ */
+ public static function getPlugin(
+ $plugin_type,
+ $plugin_format,
+ $plugins_dir,
+ $plugin_param = false
+ ) {
+ $GLOBALS['plugin_param'] = $plugin_param;
+ $class_name = mb_strtoupper($plugin_type[0])
+ . mb_strtolower(mb_substr($plugin_type, 1))
+ . mb_strtoupper($plugin_format[0])
+ . mb_strtolower(mb_substr($plugin_format, 1));
+ $file = $class_name . ".php";
+ if (is_file($plugins_dir . $file)) {
+ //include_once $plugins_dir . $file;
+ $fqnClass = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($plugins_dir, 18)) . $class_name;
+ // check if class exists, could be caused by skip_import
+ if (class_exists($fqnClass)) {
+ return new $fqnClass;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Reads all plugin information from directory $plugins_dir
+ *
+ * @param string $plugin_type the type of the plugin (import, export, etc)
+ * @param string $plugins_dir directory with plugins
+ * @param mixed $plugin_param parameter to plugin by which they can
+ * decide whether they can work
+ *
+ * @return array list of plugin instances
+ */
+ public static function getPlugins($plugin_type, $plugins_dir, $plugin_param)
+ {
+ $GLOBALS['plugin_param'] = $plugin_param;
+ /* Scan for plugins */
+ $plugin_list = [];
+ if (! ($handle = @opendir($plugins_dir))) {
+ return $plugin_list;
+ }
+
+ $namespace = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($plugins_dir, 18));
+ $class_type = mb_strtoupper($plugin_type[0], 'UTF-8')
+ . mb_strtolower(mb_substr($plugin_type, 1), 'UTF-8');
+
+ $prefix_class_name = $namespace . $class_type;
+
+ while ($file = @readdir($handle)) {
+ // In some situations, Mac OS creates a new file for each file
+ // (for example ._csv.php) so the following regexp
+ // matches a file which does not start with a dot but ends
+ // with ".php"
+ if (is_file($plugins_dir . $file)
+ && preg_match(
+ '@^' . $class_type . '([^\.]+)\.php$@i',
+ $file,
+ $matches
+ )
+ ) {
+ $GLOBALS['skip_import'] = false;
+ include_once $plugins_dir . $file;
+ if (! $GLOBALS['skip_import']) {
+ $class_name = $prefix_class_name . $matches[1];
+ $plugin = new $class_name;
+ if (null !== $plugin->getProperties()) {
+ $plugin_list[] = $plugin;
+ }
+ }
+ }
+ }
+
+ usort($plugin_list, function ($cmp_name_1, $cmp_name_2) {
+ return strcasecmp(
+ $cmp_name_1->getProperties()->getText(),
+ $cmp_name_2->getProperties()->getText()
+ );
+ });
+ return $plugin_list;
+ }
+
+ /**
+ * Returns locale string for $name or $name if no locale is found
+ *
+ * @param string $name for local string
+ *
+ * @return string locale string for $name
+ */
+ public static function getString($name)
+ {
+ return isset($GLOBALS[$name]) ? $GLOBALS[$name] : $name;
+ }
+
+ /**
+ * Returns html input tag option 'checked' if plugin $opt
+ * should be set by config or request
+ *
+ * @param string $section name of config section in
+ * $GLOBALS['cfg'][$section] for plugin
+ * @param string $opt name of option
+ *
+ * @return string html input tag option 'checked'
+ */
+ public static function checkboxCheck($section, $opt)
+ {
+ // If the form is being repopulated using $_GET data, that is priority
+ if (isset($_GET[$opt])
+ || ! isset($_GET['repopulate'])
+ && ((! empty($GLOBALS['timeout_passed']) && isset($_REQUEST[$opt]))
+ || ! empty($GLOBALS['cfg'][$section][$opt]))
+ ) {
+ return ' checked="checked"';
+ }
+ return '';
+ }
+
+ /**
+ * Returns default value for option $opt
+ *
+ * @param string $section name of config section in
+ * $GLOBALS['cfg'][$section] for plugin
+ * @param string $opt name of option
+ *
+ * @return string default value for option $opt
+ */
+ public static function getDefault($section, $opt)
+ {
+ if (isset($_GET[$opt])) {
+ // If the form is being repopulated using $_GET data, that is priority
+ return htmlspecialchars($_GET[$opt]);
+ }
+
+ if (isset($GLOBALS['timeout_passed'])
+ && $GLOBALS['timeout_passed']
+ && isset($_REQUEST[$opt])
+ ) {
+ return htmlspecialchars($_REQUEST[$opt]);
+ }
+
+ if (! isset($GLOBALS['cfg'][$section][$opt])) {
+ return '';
+ }
+
+ $matches = [];
+ /* Possibly replace localised texts */
+ if (! preg_match_all(
+ '/(str[A-Z][A-Za-z0-9]*)/',
+ (string) $GLOBALS['cfg'][$section][$opt],
+ $matches
+ )) {
+ return htmlspecialchars((string) $GLOBALS['cfg'][$section][$opt]);
+ }
+
+ $val = $GLOBALS['cfg'][$section][$opt];
+ foreach ($matches[0] as $match) {
+ if (isset($GLOBALS[$match])) {
+ $val = str_replace($match, $GLOBALS[$match], $val);
+ }
+ }
+ return htmlspecialchars($val);
+ }
+
+ /**
+ * Returns html select form element for plugin choice
+ * and hidden fields denoting whether each plugin must be exported as a file
+ *
+ * @param string $section name of config section in
+ * $GLOBALS['cfg'][$section] for plugin
+ * @param string $name name of select element
+ * @param array $list array with plugin instances
+ * @param string $cfgname name of config value, if none same as $name
+ *
+ * @return string html select tag
+ */
+ public static function getChoice($section, $name, array $list, $cfgname = null)
+ {
+ if (! isset($cfgname)) {
+ $cfgname = $name;
+ }
+ $ret = '<select id="plugins" name="' . $name . '">';
+ $default = self::getDefault($section, $cfgname);
+ $hidden = null;
+ foreach ($list as $plugin) {
+ $elem = explode('\\', get_class($plugin));
+ $plugin_name = array_pop($elem);
+ unset($elem);
+ $plugin_name = mb_strtolower(
+ mb_substr(
+ $plugin_name,
+ mb_strlen($section)
+ )
+ );
+ $ret .= '<option';
+ // If the form is being repopulated using $_GET data, that is priority
+ if (isset($_GET[$name])
+ && $plugin_name == $_GET[$name]
+ || ! isset($_GET[$name])
+ && $plugin_name == $default
+ ) {
+ $ret .= ' selected="selected"';
+ }
+
+ /** @var PluginPropertyItem $properties */
+ $properties = $plugin->getProperties();
+ $text = null;
+ if ($properties != null) {
+ $text = $properties->getText();
+ }
+ $ret .= ' value="' . $plugin_name . '">'
+ . self::getString($text)
+ . '</option>' . "\n";
+
+ // Whether each plugin has to be saved as a file
+ $hidden .= '<input type="hidden" id="force_file_' . $plugin_name
+ . '" value="';
+ /** @var ExportPluginProperties|SchemaPluginProperties $properties */
+ $properties = $plugin->getProperties();
+ if (! strcmp($section, 'Import')
+ || ($properties != null && $properties->getForceFile() != null)
+ ) {
+ $hidden .= 'true';
+ } else {
+ $hidden .= 'false';
+ }
+ $hidden .= '">' . "\n";
+ }
+ $ret .= '</select>' . "\n" . $hidden;
+
+ return $ret;
+ }
+
+ /**
+ * Returns single option in a list element
+ *
+ * @param string $section name of config section in $GLOBALS['cfg'][$section] for plugin
+ * @param string $plugin_name unique plugin name
+ * @param OptionsPropertyItem $propertyGroup options property main group instance
+ * @param boolean $is_subgroup if this group is a subgroup
+ *
+ * @return string table row with option
+ */
+ public static function getOneOption(
+ $section,
+ $plugin_name,
+ &$propertyGroup,
+ $is_subgroup = false
+ ) {
+ $ret = "\n";
+
+ $properties = null;
+ if (! $is_subgroup) {
+ // for subgroup headers
+ if (mb_strpos(get_class($propertyGroup), "PropertyItem")) {
+ $properties = [$propertyGroup];
+ } else {
+ // for main groups
+ $ret .= '<div class="export_sub_options" id="' . $plugin_name . '_'
+ . $propertyGroup->getName() . '">';
+
+ $text = null;
+ if (method_exists($propertyGroup, 'getText')) {
+ $text = $propertyGroup->getText();
+ }
+
+ if ($text != null) {
+ $ret .= '<h4>' . self::getString($text) . '</h4>';
+ }
+ $ret .= '<ul>';
+ }
+ }
+
+ if (! isset($properties)) {
+ $not_subgroup_header = true;
+ if (method_exists($propertyGroup, 'getProperties')) {
+ $properties = $propertyGroup->getProperties();
+ }
+ }
+
+ if (isset($properties)) {
+ /** @var OptionsPropertySubgroup $propertyItem */
+ foreach ($properties as $propertyItem) {
+ $property_class = get_class($propertyItem);
+ // if the property is a subgroup, we deal with it recursively
+ if (mb_strpos($property_class, "Subgroup")) {
+ // for subgroups
+ // each subgroup can have a header, which may also be a form element
+ /** @var OptionsPropertyItem $subgroup_header */
+ $subgroup_header = $propertyItem->getSubgroupHeader();
+ if ($subgroup_header !== null) {
+ $ret .= self::getOneOption(
+ $section,
+ $plugin_name,
+ $subgroup_header
+ );
+ }
+
+ $ret .= '<li class="subgroup"><ul';
+ if ($subgroup_header !== null) {
+ $ret .= ' id="ul_' . $subgroup_header->getName() . '">';
+ } else {
+ $ret .= '>';
+ }
+
+ $ret .= self::getOneOption(
+ $section,
+ $plugin_name,
+ $propertyItem,
+ true
+ );
+ continue;
+ }
+
+ // single property item
+ $ret .= self::getHtmlForProperty(
+ $section,
+ $plugin_name,
+ $propertyItem
+ );
+ }
+ }
+
+ if ($is_subgroup) {
+ // end subgroup
+ $ret .= '</ul></li>';
+ } else {
+ // end main group
+ if (! empty($not_subgroup_header)) {
+ $ret .= '</ul></div>';
+ }
+ }
+
+ if (method_exists($propertyGroup, "getDoc")) {
+ $doc = $propertyGroup->getDoc();
+ if ($doc != null) {
+ if (count($doc) === 3) {
+ $ret .= Util::showMySQLDocu(
+ $doc[1],
+ false,
+ null,
+ null,
+ $doc[2]
+ );
+ } elseif (count($doc) === 1) {
+ $ret .= Util::showDocu('faq', $doc[0]);
+ } else {
+ $ret .= Util::showMySQLDocu(
+ $doc[1]
+ );
+ }
+ }
+ }
+
+ // Close the list element after $doc link is displayed
+ if (isset($property_class)) {
+ if ($property_class == 'PhpMyAdmin\Properties\Options\Items\BoolPropertyItem'
+ || $property_class == 'PhpMyAdmin\Properties\Options\Items\MessageOnlyPropertyItem'
+ || $property_class == 'PhpMyAdmin\Properties\Options\Items\SelectPropertyItem'
+ || $property_class == 'PhpMyAdmin\Properties\Options\Items\TextPropertyItem'
+ ) {
+ $ret .= '</li>';
+ }
+ }
+ $ret .= "\n";
+ return $ret;
+ }
+
+ /**
+ * Get HTML for properties items
+ *
+ * @param string $section name of config section in
+ * $GLOBALS['cfg'][$section] for plugin
+ * @param string $plugin_name unique plugin name
+ * @param OptionsPropertyItem $propertyItem Property item
+ *
+ * @return string
+ */
+ public static function getHtmlForProperty(
+ $section,
+ $plugin_name,
+ $propertyItem
+ ) {
+ $ret = null;
+ $property_class = get_class($propertyItem);
+ switch ($property_class) {
+ case BoolPropertyItem::class:
+ $ret .= '<li>' . "\n";
+ $ret .= '<input type="checkbox" name="' . $plugin_name . '_'
+ . $propertyItem->getName() . '"'
+ . ' value="something" id="checkbox_' . $plugin_name . '_'
+ . $propertyItem->getName() . '"'
+ . ' '
+ . self::checkboxCheck(
+ $section,
+ $plugin_name . '_' . $propertyItem->getName()
+ );
+
+ if ($propertyItem->getForce() != null) {
+ // Same code is also few lines lower, update both if needed
+ $ret .= ' onclick="if (!this.checked &amp;&amp; '
+ . '(!document.getElementById(\'checkbox_' . $plugin_name
+ . '_' . $propertyItem->getForce() . '\') '
+ . '|| !document.getElementById(\'checkbox_'
+ . $plugin_name . '_' . $propertyItem->getForce()
+ . '\').checked)) '
+ . 'return false; else return true;"';
+ }
+ $ret .= '>';
+ $ret .= '<label for="checkbox_' . $plugin_name . '_'
+ . $propertyItem->getName() . '">'
+ . self::getString($propertyItem->getText()) . '</label>';
+ break;
+ case DocPropertyItem::class:
+ echo DocPropertyItem::class;
+ break;
+ case HiddenPropertyItem::class:
+ $ret .= '<li><input type="hidden" name="' . $plugin_name . '_'
+ . $propertyItem->getName() . '"'
+ . ' value="' . self::getDefault(
+ $section,
+ $plugin_name . '_' . $propertyItem->getName()
+ )
+ . '"></li>';
+ break;
+ case MessageOnlyPropertyItem::class:
+ $ret .= '<li>' . "\n";
+ $ret .= '<p>' . self::getString($propertyItem->getText()) . '</p>';
+ break;
+ case RadioPropertyItem::class:
+ /**
+ * @var RadioPropertyItem $pitem
+ */
+ $pitem = $propertyItem;
+
+ $default = self::getDefault(
+ $section,
+ $plugin_name . '_' . $pitem->getName()
+ );
+
+ foreach ($pitem->getValues() as $key => $val) {
+ $ret .= '<li><input type="radio" name="' . $plugin_name
+ . '_' . $pitem->getName() . '" value="' . $key
+ . '" id="radio_' . $plugin_name . '_'
+ . $pitem->getName() . '_' . $key . '"';
+ if ($key == $default) {
+ $ret .= ' checked="checked"';
+ }
+ $ret .= '><label for="radio_' . $plugin_name . '_'
+ . $pitem->getName() . '_' . $key . '">'
+ . self::getString($val) . '</label></li>';
+ }
+ break;
+ case SelectPropertyItem::class:
+ /**
+ * @var SelectPropertyItem $pitem
+ */
+ $pitem = $propertyItem;
+ $ret .= '<li>' . "\n";
+ $ret .= '<label for="select_' . $plugin_name . '_'
+ . $pitem->getName() . '" class="desc">'
+ . self::getString($pitem->getText()) . '</label>';
+ $ret .= '<select name="' . $plugin_name . '_'
+ . $pitem->getName() . '"'
+ . ' id="select_' . $plugin_name . '_'
+ . $pitem->getName() . '">';
+ $default = self::getDefault(
+ $section,
+ $plugin_name . '_' . $pitem->getName()
+ );
+ foreach ($pitem->getValues() as $key => $val) {
+ $ret .= '<option value="' . $key . '"';
+ if ($key == $default) {
+ $ret .= ' selected="selected"';
+ }
+ $ret .= '>' . self::getString($val) . '</option>';
+ }
+
+ $ret .= '</select>';
+ break;
+ case TextPropertyItem::class:
+ /**
+ * @var TextPropertyItem $pitem
+ */
+ $pitem = $propertyItem;
+ $ret .= '<li>' . "\n";
+ $ret .= '<label for="text_' . $plugin_name . '_'
+ . $pitem->getName() . '" class="desc">'
+ . self::getString($pitem->getText()) . '</label>';
+ $ret .= '<input type="text" name="' . $plugin_name . '_'
+ . $pitem->getName() . '"'
+ . ' value="' . self::getDefault(
+ $section,
+ $plugin_name . '_' . $pitem->getName()
+ ) . '"'
+ . ' id="text_' . $plugin_name . '_'
+ . $pitem->getName() . '"'
+ . ($pitem->getSize() != null
+ ? ' size="' . $pitem->getSize() . '"'
+ : '')
+ . ($pitem->getLen() != null
+ ? ' maxlength="' . $pitem->getLen() . '"'
+ : '')
+ . '>';
+ break;
+ case NumberPropertyItem::class:
+ $ret .= '<li>' . "\n";
+ $ret .= '<label for="number_' . $plugin_name . '_'
+ . $propertyItem->getName() . '" class="desc">'
+ . self::getString($propertyItem->getText()) . '</label>';
+ $ret .= '<input type="number" name="' . $plugin_name . '_'
+ . $propertyItem->getName() . '"'
+ . ' value="' . self::getDefault(
+ $section,
+ $plugin_name . '_' . $propertyItem->getName()
+ ) . '"'
+ . ' id="number_' . $plugin_name . '_'
+ . $propertyItem->getName() . '"'
+ . ' min="0"'
+ . '>';
+ break;
+ default:
+ break;
+ }
+ return $ret;
+ }
+
+ /**
+ * Returns html div with editable options for plugin
+ *
+ * @param string $section name of config section in $GLOBALS['cfg'][$section]
+ * @param array $list array with plugin instances
+ *
+ * @return string html fieldset with plugin options
+ */
+ public static function getOptions($section, array $list)
+ {
+ $ret = '';
+ // Options for plugins that support them
+ foreach ($list as $plugin) {
+ $properties = $plugin->getProperties();
+ $text = null;
+ $options = null;
+ if ($properties != null) {
+ $text = $properties->getText();
+ $options = $properties->getOptions();
+ }
+
+ $elem = explode('\\', get_class($plugin));
+ $plugin_name = array_pop($elem);
+ unset($elem);
+ $plugin_name = mb_strtolower(
+ mb_substr(
+ $plugin_name,
+ mb_strlen($section)
+ )
+ );
+
+ $ret .= '<div id="' . $plugin_name
+ . '_options" class="format_specific_options">';
+ $ret .= '<h3>' . self::getString($text) . '</h3>';
+
+ $no_options = true;
+ if ($options !== null && count($options) > 0) {
+ foreach ($options->getProperties() as $propertyMainGroup) {
+ // check for hidden properties
+ $no_options = true;
+ foreach ($propertyMainGroup->getProperties() as $propertyItem) {
+ if (strcmp(HiddenPropertyItem::class, get_class($propertyItem))) {
+ $no_options = false;
+ break;
+ }
+ }
+
+ $ret .= self::getOneOption(
+ $section,
+ $plugin_name,
+ $propertyMainGroup
+ );
+ }
+ }
+
+ if ($no_options) {
+ $ret .= '<p>' . __('This format has no options') . '</p>';
+ }
+ $ret .= '</div>';
+ }
+ return $ret;
+ }
+}