UserPreferences.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * Holds the PhpMyAdmin\UserPreferences class
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. declare(strict_types=1);
  9. namespace PhpMyAdmin;
  10. use PhpMyAdmin\Config\ConfigFile;
  11. use PhpMyAdmin\Config\Forms\User\UserFormList;
  12. use PhpMyAdmin\Core;
  13. use PhpMyAdmin\Message;
  14. use PhpMyAdmin\Relation;
  15. use PhpMyAdmin\Template;
  16. use PhpMyAdmin\Url;
  17. use PhpMyAdmin\Util;
  18. /**
  19. * Functions for displaying user preferences pages
  20. *
  21. * @package PhpMyAdmin
  22. */
  23. class UserPreferences
  24. {
  25. /**
  26. * @var Relation
  27. */
  28. private $relation;
  29. /**
  30. * @var Template
  31. */
  32. public $template;
  33. /**
  34. * Constructor
  35. */
  36. public function __construct()
  37. {
  38. $this->relation = new Relation($GLOBALS['dbi']);
  39. $this->template = new Template();
  40. }
  41. /**
  42. * Common initialization for user preferences modification pages
  43. *
  44. * @param ConfigFile $cf Config file instance
  45. *
  46. * @return void
  47. */
  48. public function pageInit(ConfigFile $cf)
  49. {
  50. $forms_all_keys = UserFormList::getFields();
  51. $cf->resetConfigData(); // start with a clean instance
  52. $cf->setAllowedKeys($forms_all_keys);
  53. $cf->setCfgUpdateReadMapping(
  54. [
  55. 'Server/hide_db' => 'Servers/1/hide_db',
  56. 'Server/only_db' => 'Servers/1/only_db',
  57. ]
  58. );
  59. $cf->updateWithGlobalConfig($GLOBALS['cfg']);
  60. }
  61. /**
  62. * Loads user preferences
  63. *
  64. * Returns an array:
  65. * * config_data - path => value pairs
  66. * * mtime - last modification time
  67. * * type - 'db' (config read from pmadb) or 'session' (read from user session)
  68. *
  69. * @return array
  70. */
  71. public function load()
  72. {
  73. $cfgRelation = $this->relation->getRelationsParam();
  74. if (! $cfgRelation['userconfigwork']) {
  75. // no pmadb table, use session storage
  76. if (! isset($_SESSION['userconfig'])) {
  77. $_SESSION['userconfig'] = [
  78. 'db' => [],
  79. 'ts' => time(),
  80. ];
  81. }
  82. return [
  83. 'config_data' => $_SESSION['userconfig']['db'],
  84. 'mtime' => $_SESSION['userconfig']['ts'],
  85. 'type' => 'session',
  86. ];
  87. }
  88. // load configuration from pmadb
  89. $query_table = Util::backquote($cfgRelation['db']) . '.'
  90. . Util::backquote($cfgRelation['userconfig']);
  91. $query = 'SELECT `config_data`, UNIX_TIMESTAMP(`timevalue`) ts'
  92. . ' FROM ' . $query_table
  93. . ' WHERE `username` = \''
  94. . $GLOBALS['dbi']->escapeString($cfgRelation['user'])
  95. . '\'';
  96. $row = $GLOBALS['dbi']->fetchSingleRow($query, 'ASSOC', DatabaseInterface::CONNECT_CONTROL);
  97. return [
  98. 'config_data' => $row ? json_decode($row['config_data'], true) : [],
  99. 'mtime' => $row ? $row['ts'] : time(),
  100. 'type' => 'db',
  101. ];
  102. }
  103. /**
  104. * Saves user preferences
  105. *
  106. * @param array $config_array configuration array
  107. *
  108. * @return true|Message
  109. */
  110. public function save(array $config_array)
  111. {
  112. $cfgRelation = $this->relation->getRelationsParam();
  113. $server = isset($GLOBALS['server'])
  114. ? $GLOBALS['server']
  115. : $GLOBALS['cfg']['ServerDefault'];
  116. $cache_key = 'server_' . $server;
  117. if (! $cfgRelation['userconfigwork']) {
  118. // no pmadb table, use session storage
  119. $_SESSION['userconfig'] = [
  120. 'db' => $config_array,
  121. 'ts' => time(),
  122. ];
  123. if (isset($_SESSION['cache'][$cache_key]['userprefs'])) {
  124. unset($_SESSION['cache'][$cache_key]['userprefs']);
  125. }
  126. return true;
  127. }
  128. // save configuration to pmadb
  129. $query_table = Util::backquote($cfgRelation['db']) . '.'
  130. . Util::backquote($cfgRelation['userconfig']);
  131. $query = 'SELECT `username` FROM ' . $query_table
  132. . ' WHERE `username` = \''
  133. . $GLOBALS['dbi']->escapeString($cfgRelation['user'])
  134. . '\'';
  135. $has_config = $GLOBALS['dbi']->fetchValue(
  136. $query,
  137. 0,
  138. 0,
  139. DatabaseInterface::CONNECT_CONTROL
  140. );
  141. $config_data = json_encode($config_array);
  142. if ($has_config) {
  143. $query = 'UPDATE ' . $query_table
  144. . ' SET `timevalue` = NOW(), `config_data` = \''
  145. . $GLOBALS['dbi']->escapeString($config_data)
  146. . '\''
  147. . ' WHERE `username` = \''
  148. . $GLOBALS['dbi']->escapeString($cfgRelation['user'])
  149. . '\'';
  150. } else {
  151. $query = 'INSERT INTO ' . $query_table
  152. . ' (`username`, `timevalue`,`config_data`) '
  153. . 'VALUES (\''
  154. . $GLOBALS['dbi']->escapeString($cfgRelation['user']) . '\', NOW(), '
  155. . '\'' . $GLOBALS['dbi']->escapeString($config_data) . '\')';
  156. }
  157. if (isset($_SESSION['cache'][$cache_key]['userprefs'])) {
  158. unset($_SESSION['cache'][$cache_key]['userprefs']);
  159. }
  160. if (! $GLOBALS['dbi']->tryQuery($query, DatabaseInterface::CONNECT_CONTROL)) {
  161. $message = Message::error(__('Could not save configuration'));
  162. $message->addMessage(
  163. Message::rawError(
  164. $GLOBALS['dbi']->getError(DatabaseInterface::CONNECT_CONTROL)
  165. ),
  166. '<br><br>'
  167. );
  168. return $message;
  169. }
  170. return true;
  171. }
  172. /**
  173. * Returns a user preferences array filtered by $cfg['UserprefsDisallow']
  174. * (blacklist) and keys from user preferences form (whitelist)
  175. *
  176. * @param array $config_data path => value pairs
  177. *
  178. * @return array
  179. */
  180. public function apply(array $config_data)
  181. {
  182. $cfg = [];
  183. $blacklist = array_flip($GLOBALS['cfg']['UserprefsDisallow']);
  184. $whitelist = array_flip(UserFormList::getFields());
  185. // whitelist some additional fields which are custom handled
  186. $whitelist['ThemeDefault'] = true;
  187. $whitelist['lang'] = true;
  188. $whitelist['Server/hide_db'] = true;
  189. $whitelist['Server/only_db'] = true;
  190. $whitelist['2fa'] = true;
  191. foreach ($config_data as $path => $value) {
  192. if (! isset($whitelist[$path]) || isset($blacklist[$path])) {
  193. continue;
  194. }
  195. Core::arrayWrite($path, $cfg, $value);
  196. }
  197. return $cfg;
  198. }
  199. /**
  200. * Updates one user preferences option (loads and saves to database).
  201. *
  202. * No validation is done!
  203. *
  204. * @param string $path configuration
  205. * @param mixed $value value
  206. * @param mixed $default_value default value
  207. *
  208. * @return true|Message
  209. */
  210. public function persistOption($path, $value, $default_value)
  211. {
  212. $prefs = $this->load();
  213. if ($value === $default_value) {
  214. if (isset($prefs['config_data'][$path])) {
  215. unset($prefs['config_data'][$path]);
  216. } else {
  217. return true;
  218. }
  219. } else {
  220. $prefs['config_data'][$path] = $value;
  221. }
  222. return $this->save($prefs['config_data']);
  223. }
  224. /**
  225. * Redirects after saving new user preferences
  226. *
  227. * @param string $file_name Filename
  228. * @param array|null $params URL parameters
  229. * @param string $hash Hash value
  230. *
  231. * @return void
  232. */
  233. public function redirect(
  234. $file_name,
  235. $params = null,
  236. $hash = null
  237. ) {
  238. // redirect
  239. $url_params = ['saved' => 1];
  240. if (is_array($params)) {
  241. $url_params = array_merge($params, $url_params);
  242. }
  243. if ($hash) {
  244. $hash = '#' . urlencode($hash);
  245. }
  246. Core::sendHeaderLocation('./' . $file_name
  247. . Url::getCommonRaw($url_params) . $hash);
  248. }
  249. /**
  250. * Shows form which allows to quickly load
  251. * settings stored in browser's local storage
  252. *
  253. * @return string
  254. */
  255. public function autoloadGetHeader()
  256. {
  257. if (isset($_REQUEST['prefs_autoload'])
  258. && $_REQUEST['prefs_autoload'] == 'hide'
  259. ) {
  260. $_SESSION['userprefs_autoload'] = true;
  261. return '';
  262. }
  263. $script_name = basename(basename($GLOBALS['PMA_PHP_SELF']));
  264. $return_url = $script_name . '?' . http_build_query($_GET, '', '&');
  265. return $this->template->render('preferences/autoload', [
  266. 'hidden_inputs' => Url::getHiddenInputs(),
  267. 'return_url' => $return_url,
  268. ]);
  269. }
  270. }