Plugins.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Generic plugin interface.
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. namespace PhpMyAdmin;
  9. use PhpMyAdmin\Properties\Options\Groups\OptionsPropertySubgroup;
  10. use PhpMyAdmin\Properties\Options\OptionsPropertyItem;
  11. use PhpMyAdmin\Properties\Plugins\ExportPluginProperties;
  12. use PhpMyAdmin\Properties\Plugins\PluginPropertyItem;
  13. use PhpMyAdmin\Properties\Plugins\SchemaPluginProperties;
  14. /**
  15. * PhpMyAdmin\Plugins class
  16. *
  17. * @package PhpMyAdmin
  18. */
  19. class Plugins
  20. {
  21. /**
  22. * Includes and instantiates the specified plugin type for a certain format
  23. *
  24. * @param string $plugin_type the type of the plugin (import, export, etc)
  25. * @param string $plugin_format the format of the plugin (sql, xml, et )
  26. * @param string $plugins_dir directory with plugins
  27. * @param mixed $plugin_param parameter to plugin by which they can
  28. * decide whether they can work
  29. *
  30. * @return object|null new plugin instance
  31. */
  32. public static function getPlugin(
  33. $plugin_type,
  34. $plugin_format,
  35. $plugins_dir,
  36. $plugin_param = false
  37. ) {
  38. $GLOBALS['plugin_param'] = $plugin_param;
  39. $class_name = mb_strtoupper($plugin_type[0])
  40. . mb_strtolower(mb_substr($plugin_type, 1))
  41. . mb_strtoupper($plugin_format[0])
  42. . mb_strtolower(mb_substr($plugin_format, 1));
  43. $file = $class_name . ".php";
  44. if (is_file($plugins_dir . $file)) {
  45. //include_once $plugins_dir . $file;
  46. $fqnClass = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($plugins_dir, 18)) . $class_name;
  47. // check if class exists, could be caused by skip_import
  48. if (class_exists($fqnClass)) {
  49. return new $fqnClass;
  50. }
  51. }
  52. return null;
  53. }
  54. /**
  55. * Reads all plugin information from directory $plugins_dir
  56. *
  57. * @param string $plugin_type the type of the plugin (import, export, etc)
  58. * @param string $plugins_dir directory with plugins
  59. * @param mixed $plugin_param parameter to plugin by which they can
  60. * decide whether they can work
  61. *
  62. * @return array list of plugin instances
  63. */
  64. public static function getPlugins($plugin_type, $plugins_dir, $plugin_param)
  65. {
  66. $GLOBALS['plugin_param'] = $plugin_param;
  67. /* Scan for plugins */
  68. $plugin_list = array();
  69. if (!($handle = @opendir($plugins_dir))) {
  70. return $plugin_list;
  71. }
  72. $namespace = 'PhpMyAdmin\\' . str_replace('/', '\\', mb_substr($plugins_dir, 18));
  73. $class_type = mb_strtoupper($plugin_type[0], 'UTF-8')
  74. . mb_strtolower(mb_substr($plugin_type, 1), 'UTF-8');
  75. $prefix_class_name = $namespace . $class_type;
  76. while ($file = @readdir($handle)) {
  77. // In some situations, Mac OS creates a new file for each file
  78. // (for example ._csv.php) so the following regexp
  79. // matches a file which does not start with a dot but ends
  80. // with ".php"
  81. if (is_file($plugins_dir . $file)
  82. && preg_match(
  83. '@^' . $class_type . '([^\.]+)\.php$@i',
  84. $file,
  85. $matches
  86. )
  87. ) {
  88. $GLOBALS['skip_import'] = false;
  89. include_once $plugins_dir . $file;
  90. if (! $GLOBALS['skip_import']) {
  91. $class_name = $prefix_class_name . $matches[1];
  92. $plugin = new $class_name;
  93. if (null !== $plugin->getProperties()) {
  94. $plugin_list[] = $plugin;
  95. }
  96. }
  97. }
  98. }
  99. usort($plugin_list, function($cmp_name_1, $cmp_name_2) {
  100. return strcasecmp(
  101. $cmp_name_1->getProperties()->getText(),
  102. $cmp_name_2->getProperties()->getText()
  103. );
  104. });
  105. return $plugin_list;
  106. }
  107. /**
  108. * Returns locale string for $name or $name if no locale is found
  109. *
  110. * @param string $name for local string
  111. *
  112. * @return string locale string for $name
  113. */
  114. public static function getString($name)
  115. {
  116. return isset($GLOBALS[$name]) ? $GLOBALS[$name] : $name;
  117. }
  118. /**
  119. * Returns html input tag option 'checked' if plugin $opt
  120. * should be set by config or request
  121. *
  122. * @param string $section name of config section in
  123. * $GLOBALS['cfg'][$section] for plugin
  124. * @param string $opt name of option
  125. *
  126. * @return string html input tag option 'checked'
  127. */
  128. public static function checkboxCheck($section, $opt)
  129. {
  130. // If the form is being repopulated using $_GET data, that is priority
  131. if (isset($_GET[$opt])
  132. || ! isset($_GET['repopulate'])
  133. && ((! empty($GLOBALS['timeout_passed']) && isset($_REQUEST[$opt]))
  134. || ! empty($GLOBALS['cfg'][$section][$opt]))
  135. ) {
  136. return ' checked="checked"';
  137. }
  138. return '';
  139. }
  140. /**
  141. * Returns default value for option $opt
  142. *
  143. * @param string $section name of config section in
  144. * $GLOBALS['cfg'][$section] for plugin
  145. * @param string $opt name of option
  146. *
  147. * @return string default value for option $opt
  148. */
  149. public static function getDefault($section, $opt)
  150. {
  151. if (isset($_GET[$opt])) {
  152. // If the form is being repopulated using $_GET data, that is priority
  153. return htmlspecialchars($_GET[$opt]);
  154. }
  155. if (isset($GLOBALS['timeout_passed'])
  156. && $GLOBALS['timeout_passed']
  157. && isset($_REQUEST[$opt])
  158. ) {
  159. return htmlspecialchars($_REQUEST[$opt]);
  160. }
  161. if (!isset($GLOBALS['cfg'][$section][$opt])) {
  162. return '';
  163. }
  164. $matches = array();
  165. /* Possibly replace localised texts */
  166. if (!preg_match_all(
  167. '/(str[A-Z][A-Za-z0-9]*)/',
  168. $GLOBALS['cfg'][$section][$opt],
  169. $matches
  170. )) {
  171. return htmlspecialchars($GLOBALS['cfg'][$section][$opt]);
  172. }
  173. $val = $GLOBALS['cfg'][$section][$opt];
  174. foreach ($matches[0] as $match) {
  175. if (isset($GLOBALS[$match])) {
  176. $val = str_replace($match, $GLOBALS[$match], $val);
  177. }
  178. }
  179. return htmlspecialchars($val);
  180. }
  181. /**
  182. * Returns html select form element for plugin choice
  183. * and hidden fields denoting whether each plugin must be exported as a file
  184. *
  185. * @param string $section name of config section in
  186. * $GLOBALS['cfg'][$section] for plugin
  187. * @param string $name name of select element
  188. * @param array &$list array with plugin instances
  189. * @param string $cfgname name of config value, if none same as $name
  190. *
  191. * @return string html select tag
  192. */
  193. public static function getChoice($section, $name, array $list, $cfgname = null)
  194. {
  195. if (! isset($cfgname)) {
  196. $cfgname = $name;
  197. }
  198. $ret = '<select id="plugins" name="' . $name . '">';
  199. $default = self::getDefault($section, $cfgname);
  200. $hidden = null;
  201. foreach ($list as $plugin) {
  202. $elem = explode('\\', get_class($plugin));
  203. $plugin_name = array_pop($elem);
  204. unset($elem);
  205. $plugin_name = mb_strtolower(
  206. mb_substr(
  207. $plugin_name,
  208. mb_strlen($section)
  209. )
  210. );
  211. $ret .= '<option';
  212. // If the form is being repopulated using $_GET data, that is priority
  213. if (isset($_GET[$name])
  214. && $plugin_name == $_GET[$name]
  215. || ! isset($_GET[$name])
  216. && $plugin_name == $default
  217. ) {
  218. $ret .= ' selected="selected"';
  219. }
  220. /** @var PluginPropertyItem $properties */
  221. $properties = $plugin->getProperties();
  222. $text = null;
  223. if ($properties != null) {
  224. $text = $properties->getText();
  225. }
  226. $ret .= ' value="' . $plugin_name . '">'
  227. . self::getString($text)
  228. . '</option>' . "\n";
  229. // Whether each plugin has to be saved as a file
  230. $hidden .= '<input type="hidden" id="force_file_' . $plugin_name
  231. . '" value="';
  232. /** @var ExportPluginProperties|SchemaPluginProperties $properties */
  233. $properties = $plugin->getProperties();
  234. if (! strcmp($section, 'Import')
  235. || ($properties != null && $properties->getForceFile() != null)
  236. ) {
  237. $hidden .= 'true';
  238. } else {
  239. $hidden .= 'false';
  240. }
  241. $hidden .= '" />' . "\n";
  242. }
  243. $ret .= '</select>' . "\n" . $hidden;
  244. return $ret;
  245. }
  246. /**
  247. * Returns single option in a list element
  248. *
  249. * @param string $section name of config section in $GLOBALS['cfg'][$section] for plugin
  250. * @param string $plugin_name unique plugin name
  251. * @param array|\PhpMyAdmin\Properties\PropertyItem &$propertyGroup options property main group instance
  252. * @param boolean $is_subgroup if this group is a subgroup
  253. *
  254. * @return string table row with option
  255. */
  256. public static function getOneOption(
  257. $section,
  258. $plugin_name,
  259. &$propertyGroup,
  260. $is_subgroup = false
  261. ) {
  262. $ret = "\n";
  263. if (! $is_subgroup) {
  264. // for subgroup headers
  265. if (mb_strpos(get_class($propertyGroup), "PropertyItem")) {
  266. $properties = array($propertyGroup);
  267. } else {
  268. // for main groups
  269. $ret .= '<div class="export_sub_options" id="' . $plugin_name . '_'
  270. . $propertyGroup->getName() . '">';
  271. if (method_exists($propertyGroup, 'getText')) {
  272. $text = $propertyGroup->getText();
  273. }
  274. if ($text != null) {
  275. $ret .= '<h4>' . self::getString($text) . '</h4>';
  276. }
  277. $ret .= '<ul>';
  278. }
  279. }
  280. if (! isset($properties)) {
  281. $not_subgroup_header = true;
  282. if (method_exists($propertyGroup, 'getProperties')) {
  283. $properties = $propertyGroup->getProperties();
  284. }
  285. }
  286. if (isset($properties)) {
  287. /** @var OptionsPropertySubgroup $propertyItem */
  288. foreach ($properties as $propertyItem) {
  289. $property_class = get_class($propertyItem);
  290. // if the property is a subgroup, we deal with it recursively
  291. if (mb_strpos($property_class, "Subgroup")) {
  292. // for subgroups
  293. // each subgroup can have a header, which may also be a form element
  294. /** @var OptionsPropertyItem $subgroup_header */
  295. $subgroup_header = $propertyItem->getSubgroupHeader();
  296. if (isset($subgroup_header)) {
  297. $ret .= self::getOneOption(
  298. $section,
  299. $plugin_name,
  300. $subgroup_header
  301. );
  302. }
  303. $ret .= '<li class="subgroup"><ul';
  304. if (isset($subgroup_header)) {
  305. $ret .= ' id="ul_' . $subgroup_header->getName() . '">';
  306. } else {
  307. $ret .= '>';
  308. }
  309. $ret .= self::getOneOption(
  310. $section,
  311. $plugin_name,
  312. $propertyItem,
  313. true
  314. );
  315. continue;
  316. }
  317. // single property item
  318. $ret .= self::getHtmlForProperty(
  319. $section, $plugin_name, $propertyItem
  320. );
  321. }
  322. }
  323. if ($is_subgroup) {
  324. // end subgroup
  325. $ret .= '</ul></li>';
  326. } else {
  327. // end main group
  328. if (! empty($not_subgroup_header)) {
  329. $ret .= '</ul></div>';
  330. }
  331. }
  332. if (method_exists($propertyGroup, "getDoc")) {
  333. $doc = $propertyGroup->getDoc();
  334. if ($doc != null) {
  335. if (count($doc) == 3) {
  336. $ret .= PhpMyAdmin\Util::showMySQLDocu(
  337. $doc[1],
  338. false,
  339. $doc[2]
  340. );
  341. } elseif (count($doc) == 1) {
  342. $ret .= PhpMyAdmin\Util::showDocu('faq', $doc[0]);
  343. } else {
  344. $ret .= PhpMyAdmin\Util::showMySQLDocu(
  345. $doc[1]
  346. );
  347. }
  348. }
  349. }
  350. // Close the list element after $doc link is displayed
  351. if (isset($property_class)) {
  352. if ($property_class == 'PhpMyAdmin\Properties\Options\Items\BoolPropertyItem'
  353. || $property_class == 'PhpMyAdmin\Properties\Options\Items\MessageOnlyPropertyItem'
  354. || $property_class == 'PhpMyAdmin\Properties\Options\Items\SelectPropertyItem'
  355. || $property_class == 'PhpMyAdmin\Properties\Options\Items\TextPropertyItem'
  356. ) {
  357. $ret .= '</li>';
  358. }
  359. }
  360. $ret .= "\n";
  361. return $ret;
  362. }
  363. /**
  364. * Get HTML for properties items
  365. *
  366. * @param string $section name of config section in
  367. * $GLOBALS['cfg'][$section] for plugin
  368. * @param string $plugin_name unique plugin name
  369. * @param OptionsPropertyItem $propertyItem Property item
  370. *
  371. * @return string
  372. */
  373. public static function getHtmlForProperty(
  374. $section, $plugin_name, $propertyItem
  375. ) {
  376. $ret = null;
  377. $property_class = get_class($propertyItem);
  378. switch ($property_class) {
  379. case 'PhpMyAdmin\Properties\Options\Items\BoolPropertyItem':
  380. $ret .= '<li>' . "\n";
  381. $ret .= '<input type="checkbox" name="' . $plugin_name . '_'
  382. . $propertyItem->getName() . '"'
  383. . ' value="something" id="checkbox_' . $plugin_name . '_'
  384. . $propertyItem->getName() . '"'
  385. . ' '
  386. . self::checkboxCheck(
  387. $section,
  388. $plugin_name . '_' . $propertyItem->getName()
  389. );
  390. if ($propertyItem->getForce() != null) {
  391. // Same code is also few lines lower, update both if needed
  392. $ret .= ' onclick="if (!this.checked &amp;&amp; '
  393. . '(!document.getElementById(\'checkbox_' . $plugin_name
  394. . '_' . $propertyItem->getForce() . '\') '
  395. . '|| !document.getElementById(\'checkbox_'
  396. . $plugin_name . '_' . $propertyItem->getForce()
  397. . '\').checked)) '
  398. . 'return false; else return true;"';
  399. }
  400. $ret .= ' />';
  401. $ret .= '<label for="checkbox_' . $plugin_name . '_'
  402. . $propertyItem->getName() . '">'
  403. . self::getString($propertyItem->getText()) . '</label>';
  404. break;
  405. case 'PhpMyAdmin\Properties\Options\Items\DocPropertyItem':
  406. echo 'PhpMyAdmin\Properties\Options\Items\DocPropertyItem';
  407. break;
  408. case 'PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem':
  409. $ret .= '<li><input type="hidden" name="' . $plugin_name . '_'
  410. . $propertyItem->getName() . '"'
  411. . ' value="' . self::getDefault(
  412. $section,
  413. $plugin_name . '_' . $propertyItem->getName()
  414. )
  415. . '"' . ' /></li>';
  416. break;
  417. case 'PhpMyAdmin\Properties\Options\Items\MessageOnlyPropertyItem':
  418. $ret .= '<li>' . "\n";
  419. $ret .= '<p>' . self::getString($propertyItem->getText()) . '</p>';
  420. break;
  421. case 'PhpMyAdmin\Properties\Options\Items\RadioPropertyItem':
  422. $default = self::getDefault(
  423. $section,
  424. $plugin_name . '_' . $propertyItem->getName()
  425. );
  426. foreach ($propertyItem->getValues() as $key => $val) {
  427. $ret .= '<li><input type="radio" name="' . $plugin_name
  428. . '_' . $propertyItem->getName() . '" value="' . $key
  429. . '" id="radio_' . $plugin_name . '_'
  430. . $propertyItem->getName() . '_' . $key . '"';
  431. if ($key == $default) {
  432. $ret .= ' checked="checked"';
  433. }
  434. $ret .= ' />' . '<label for="radio_' . $plugin_name . '_'
  435. . $propertyItem->getName() . '_' . $key . '">'
  436. . self::getString($val) . '</label></li>';
  437. }
  438. break;
  439. case 'PhpMyAdmin\Properties\Options\Items\SelectPropertyItem':
  440. $ret .= '<li>' . "\n";
  441. $ret .= '<label for="select_' . $plugin_name . '_'
  442. . $propertyItem->getName() . '" class="desc">'
  443. . self::getString($propertyItem->getText()) . '</label>';
  444. $ret .= '<select name="' . $plugin_name . '_'
  445. . $propertyItem->getName() . '"'
  446. . ' id="select_' . $plugin_name . '_'
  447. . $propertyItem->getName() . '">';
  448. $default = self::getDefault(
  449. $section,
  450. $plugin_name . '_' . $propertyItem->getName()
  451. );
  452. foreach ($propertyItem->getValues() as $key => $val) {
  453. $ret .= '<option value="' . $key . '"';
  454. if ($key == $default) {
  455. $ret .= ' selected="selected"';
  456. }
  457. $ret .= '>' . self::getString($val) . '</option>';
  458. }
  459. $ret .= '</select>';
  460. break;
  461. case 'PhpMyAdmin\Properties\Options\Items\TextPropertyItem':
  462. case 'PhpMyAdmin\Properties\Options\Items\NumberPropertyItem':
  463. $ret .= '<li>' . "\n";
  464. $ret .= '<label for="text_' . $plugin_name . '_'
  465. . $propertyItem->getName() . '" class="desc">'
  466. . self::getString($propertyItem->getText()) . '</label>';
  467. $ret .= '<input type="text" name="' . $plugin_name . '_'
  468. . $propertyItem->getName() . '"'
  469. . ' value="' . self::getDefault(
  470. $section,
  471. $plugin_name . '_' . $propertyItem->getName()
  472. ) . '"'
  473. . ' id="text_' . $plugin_name . '_'
  474. . $propertyItem->getName() . '"'
  475. . ($propertyItem->getSize() != null
  476. ? ' size="' . $propertyItem->getSize() . '"'
  477. : '')
  478. . ($propertyItem->getLen() != null
  479. ? ' maxlength="' . $propertyItem->getLen() . '"'
  480. : '')
  481. . ' />';
  482. break;
  483. default:
  484. break;
  485. }
  486. return $ret;
  487. }
  488. /**
  489. * Returns html div with editable options for plugin
  490. *
  491. * @param string $section name of config section in $GLOBALS['cfg'][$section]
  492. * @param array &$list array with plugin instances
  493. *
  494. * @return string html fieldset with plugin options
  495. */
  496. public static function getOptions($section, array $list)
  497. {
  498. $ret = '';
  499. // Options for plugins that support them
  500. foreach ($list as $plugin) {
  501. $properties = $plugin->getProperties();
  502. if ($properties != null) {
  503. $text = $properties->getText();
  504. $options = $properties->getOptions();
  505. }
  506. $elem = explode('\\', get_class($plugin));
  507. $plugin_name = array_pop($elem);
  508. unset($elem);
  509. $plugin_name = mb_strtolower(
  510. mb_substr(
  511. $plugin_name,
  512. mb_strlen($section)
  513. )
  514. );
  515. $ret .= '<div id="' . $plugin_name
  516. . '_options" class="format_specific_options">';
  517. $ret .= '<h3>' . self::getString($text) . '</h3>';
  518. $no_options = true;
  519. if (! is_null($options) && count($options) > 0) {
  520. foreach ($options->getProperties()
  521. as $propertyMainGroup
  522. ) {
  523. // check for hidden properties
  524. $no_options = true;
  525. foreach ($propertyMainGroup->getProperties() as $propertyItem) {
  526. if (strcmp('PhpMyAdmin\Properties\Options\Items\HiddenPropertyItem', get_class($propertyItem))) {
  527. $no_options = false;
  528. break;
  529. }
  530. }
  531. $ret .= self::getOneOption(
  532. $section,
  533. $plugin_name,
  534. $propertyMainGroup
  535. );
  536. }
  537. }
  538. if ($no_options) {
  539. $ret .= '<p>' . __('This format has no options') . '</p>';
  540. }
  541. $ret .= '</div>';
  542. }
  543. return $ret;
  544. }
  545. }