Header.php 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. <?php
  2. /**
  3. * Used to render the header of PMA's pages
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin;
  7. use PhpMyAdmin\Html\Generator;
  8. use PhpMyAdmin\Navigation\Navigation;
  9. use function defined;
  10. use function gmdate;
  11. use function header;
  12. use function htmlspecialchars;
  13. use function implode;
  14. use function ini_get;
  15. use function is_bool;
  16. use function strlen;
  17. use function strtolower;
  18. use function urlencode;
  19. /**
  20. * Class used to output the HTTP and HTML headers
  21. */
  22. class Header
  23. {
  24. /**
  25. * Scripts instance
  26. *
  27. * @access private
  28. * @var Scripts
  29. */
  30. private $scripts;
  31. /**
  32. * PhpMyAdmin\Console instance
  33. *
  34. * @access private
  35. * @var Console
  36. */
  37. private $console;
  38. /**
  39. * Menu instance
  40. *
  41. * @access private
  42. * @var Menu
  43. */
  44. private $menu;
  45. /**
  46. * Whether to offer the option of importing user settings
  47. *
  48. * @access private
  49. * @var bool
  50. */
  51. private $userprefsOfferImport;
  52. /**
  53. * The page title
  54. *
  55. * @access private
  56. * @var string
  57. */
  58. private $title;
  59. /**
  60. * The value for the id attribute for the body tag
  61. *
  62. * @access private
  63. * @var string
  64. */
  65. private $bodyId;
  66. /**
  67. * Whether to show the top menu
  68. *
  69. * @access private
  70. * @var bool
  71. */
  72. private $menuEnabled;
  73. /**
  74. * Whether to show the warnings
  75. *
  76. * @access private
  77. * @var bool
  78. */
  79. private $warningsEnabled;
  80. /**
  81. * Whether the page is in 'print view' mode
  82. *
  83. * @access private
  84. * @var bool
  85. */
  86. private $isPrintView;
  87. /**
  88. * Whether we are servicing an ajax request.
  89. *
  90. * @access private
  91. * @var bool
  92. */
  93. private $isAjax;
  94. /**
  95. * Whether to display anything
  96. *
  97. * @access private
  98. * @var bool
  99. */
  100. private $isEnabled;
  101. /**
  102. * Whether the HTTP headers (and possibly some HTML)
  103. * have already been sent to the browser
  104. *
  105. * @access private
  106. * @var bool
  107. */
  108. private $headerIsSent;
  109. /** @var UserPreferences */
  110. private $userPreferences;
  111. /** @var Template */
  112. private $template;
  113. /**
  114. * Creates a new class instance
  115. */
  116. public function __construct()
  117. {
  118. global $db, $table;
  119. $this->template = new Template();
  120. $this->isEnabled = true;
  121. $this->isAjax = false;
  122. $this->bodyId = '';
  123. $this->title = '';
  124. $this->console = new Console();
  125. $this->menu = new Menu(
  126. $db ?? '',
  127. $table ?? ''
  128. );
  129. $this->menuEnabled = true;
  130. $this->warningsEnabled = true;
  131. $this->isPrintView = false;
  132. $this->scripts = new Scripts();
  133. $this->addDefaultScripts();
  134. $this->headerIsSent = false;
  135. // if database storage for user preferences is transient,
  136. // offer to load exported settings from localStorage
  137. // (detection will be done in JavaScript)
  138. $this->userprefsOfferImport = false;
  139. if ($GLOBALS['PMA_Config']->get('user_preferences') === 'session'
  140. && ! isset($_SESSION['userprefs_autoload'])
  141. ) {
  142. $this->userprefsOfferImport = true;
  143. }
  144. $this->userPreferences = new UserPreferences();
  145. }
  146. /**
  147. * Loads common scripts
  148. */
  149. private function addDefaultScripts(): void
  150. {
  151. // Localised strings
  152. $this->scripts->addFile('vendor/jquery/jquery.min.js');
  153. $this->scripts->addFile('vendor/jquery/jquery-migrate.js');
  154. $this->scripts->addFile('vendor/sprintf.js');
  155. $this->scripts->addFile('ajax.js');
  156. $this->scripts->addFile('keyhandler.js');
  157. $this->scripts->addFile('vendor/bootstrap/bootstrap.bundle.min.js');
  158. $this->scripts->addFile('vendor/jquery/jquery-ui.min.js');
  159. $this->scripts->addFile('vendor/js.cookie.js');
  160. $this->scripts->addFile('vendor/jquery/jquery.mousewheel.js');
  161. $this->scripts->addFile('vendor/jquery/jquery.validate.js');
  162. $this->scripts->addFile('vendor/jquery/jquery-ui-timepicker-addon.js');
  163. $this->scripts->addFile('vendor/jquery/jquery.ba-hashchange-2.0.js');
  164. $this->scripts->addFile('vendor/jquery/jquery.debounce-1.0.6.js');
  165. $this->scripts->addFile('menu_resizer.js');
  166. // Cross-framing protection
  167. if ($GLOBALS['cfg']['AllowThirdPartyFraming'] === false) {
  168. $this->scripts->addFile('cross_framing_protection.js');
  169. }
  170. $this->scripts->addFile('rte.js');
  171. if ($GLOBALS['cfg']['SendErrorReports'] !== 'never') {
  172. $this->scripts->addFile('vendor/tracekit.js');
  173. $this->scripts->addFile('error_report.js');
  174. }
  175. // Here would not be a good place to add CodeMirror because
  176. // the user preferences have not been merged at this point
  177. $this->scripts->addFile('messages.php', ['l' => $GLOBALS['lang']]);
  178. $this->scripts->addCode($this->getVariablesForJavaScript());
  179. $this->scripts->addFile('config.js');
  180. $this->scripts->addFile('doclinks.js');
  181. $this->scripts->addFile('functions.js');
  182. $this->scripts->addFile('navigation.js');
  183. $this->scripts->addFile('indexes.js');
  184. $this->scripts->addFile('common.js');
  185. $this->scripts->addFile('page_settings.js');
  186. if ($GLOBALS['cfg']['enable_drag_drop_import'] === true) {
  187. $this->scripts->addFile('drag_drop_import.js');
  188. }
  189. if (! $GLOBALS['PMA_Config']->get('DisableShortcutKeys')) {
  190. $this->scripts->addFile('shortcuts_handler.js');
  191. }
  192. $this->scripts->addCode($this->getJsParamsCode());
  193. }
  194. /**
  195. * Returns, as an array, a list of parameters
  196. * used on the client side
  197. *
  198. * @return array
  199. */
  200. public function getJsParams(): array
  201. {
  202. global $db, $table, $dbi;
  203. $pftext = $_SESSION['tmpval']['pftext'] ?? '';
  204. $params = [
  205. // Do not add any separator, JS code will decide
  206. 'common_query' => Url::getCommonRaw([], ''),
  207. 'opendb_url' => Util::getScriptNameForOption(
  208. $GLOBALS['cfg']['DefaultTabDatabase'],
  209. 'database'
  210. ),
  211. 'lang' => $GLOBALS['lang'],
  212. 'server' => $GLOBALS['server'],
  213. 'table' => $table ?? '',
  214. 'db' => $db ?? '',
  215. 'token' => $_SESSION[' PMA_token '],
  216. 'text_dir' => $GLOBALS['text_dir'],
  217. 'show_databases_navigation_as_tree' => $GLOBALS['cfg']['ShowDatabasesNavigationAsTree'],
  218. 'pma_text_default_tab' => Util::getTitleForTarget(
  219. $GLOBALS['cfg']['DefaultTabTable']
  220. ),
  221. 'pma_text_left_default_tab' => Util::getTitleForTarget(
  222. $GLOBALS['cfg']['NavigationTreeDefaultTabTable']
  223. ),
  224. 'pma_text_left_default_tab2' => Util::getTitleForTarget(
  225. $GLOBALS['cfg']['NavigationTreeDefaultTabTable2']
  226. ),
  227. 'LimitChars' => $GLOBALS['cfg']['LimitChars'],
  228. 'pftext' => $pftext,
  229. 'confirm' => $GLOBALS['cfg']['Confirm'],
  230. 'LoginCookieValidity' => $GLOBALS['cfg']['LoginCookieValidity'],
  231. 'session_gc_maxlifetime' => (int) ini_get('session.gc_maxlifetime'),
  232. 'logged_in' => isset($dbi) ? $dbi->isConnected() : false,
  233. 'is_https' => $GLOBALS['PMA_Config']->isHttps(),
  234. 'rootPath' => $GLOBALS['PMA_Config']->getRootPath(),
  235. 'arg_separator' => Url::getArgSeparator(),
  236. 'PMA_VERSION' => PMA_VERSION,
  237. ];
  238. if (isset($GLOBALS['cfg']['Server'], $GLOBALS['cfg']['Server']['auth_type'])) {
  239. $params['auth_type'] = $GLOBALS['cfg']['Server']['auth_type'];
  240. if (isset($GLOBALS['cfg']['Server']['user'])) {
  241. $params['user'] = $GLOBALS['cfg']['Server']['user'];
  242. }
  243. }
  244. return $params;
  245. }
  246. /**
  247. * Returns, as a string, a list of parameters
  248. * used on the client side
  249. */
  250. public function getJsParamsCode(): string
  251. {
  252. $params = $this->getJsParams();
  253. foreach ($params as $key => $value) {
  254. if (is_bool($value)) {
  255. $params[$key] = $key . ':' . ($value ? 'true' : 'false') . '';
  256. } else {
  257. $params[$key] = $key . ':"' . Sanitize::escapeJsString($value) . '"';
  258. }
  259. }
  260. return 'CommonParams.setAll({' . implode(',', $params) . '});';
  261. }
  262. /**
  263. * Disables the rendering of the header
  264. */
  265. public function disable(): void
  266. {
  267. $this->isEnabled = false;
  268. }
  269. /**
  270. * Set the ajax flag to indicate whether
  271. * we are servicing an ajax request
  272. *
  273. * @param bool $isAjax Whether we are servicing an ajax request
  274. */
  275. public function setAjax(bool $isAjax): void
  276. {
  277. $this->isAjax = $isAjax;
  278. $this->console->setAjax($isAjax);
  279. }
  280. /**
  281. * Returns the Scripts object
  282. *
  283. * @return Scripts object
  284. */
  285. public function getScripts(): Scripts
  286. {
  287. return $this->scripts;
  288. }
  289. /**
  290. * Returns the Menu object
  291. *
  292. * @return Menu object
  293. */
  294. public function getMenu(): Menu
  295. {
  296. return $this->menu;
  297. }
  298. /**
  299. * Setter for the ID attribute in the BODY tag
  300. *
  301. * @param string $id Value for the ID attribute
  302. */
  303. public function setBodyId(string $id): void
  304. {
  305. $this->bodyId = htmlspecialchars($id);
  306. }
  307. /**
  308. * Setter for the title of the page
  309. *
  310. * @param string $title New title
  311. */
  312. public function setTitle(string $title): void
  313. {
  314. $this->title = htmlspecialchars($title);
  315. }
  316. /**
  317. * Disables the display of the top menu
  318. */
  319. public function disableMenuAndConsole(): void
  320. {
  321. $this->menuEnabled = false;
  322. $this->console->disable();
  323. }
  324. /**
  325. * Disables the display of the top menu
  326. */
  327. public function disableWarnings(): void
  328. {
  329. $this->warningsEnabled = false;
  330. }
  331. /**
  332. * Turns on 'print view' mode
  333. */
  334. public function enablePrintView(): void
  335. {
  336. $this->disableMenuAndConsole();
  337. $this->setTitle(__('Print view') . ' - phpMyAdmin ' . PMA_VERSION);
  338. $this->isPrintView = true;
  339. }
  340. /**
  341. * Generates the header
  342. *
  343. * @return string The header
  344. */
  345. public function getDisplay(): string
  346. {
  347. global $db, $table, $PMA_Theme, $dbi;
  348. if ($this->headerIsSent || ! $this->isEnabled) {
  349. return '';
  350. }
  351. $recentTable = '';
  352. if (empty($_REQUEST['recent_table'])) {
  353. $recentTable = $this->addRecentTable($db, $table);
  354. }
  355. if ($this->isAjax) {
  356. return $recentTable;
  357. }
  358. $this->sendHttpHeaders();
  359. $baseDir = defined('PMA_PATH_TO_BASEDIR') ? PMA_PATH_TO_BASEDIR : '';
  360. $uniqueValue = $GLOBALS['PMA_Config']->getThemeUniqueValue();
  361. $themePath = $PMA_Theme !== null ? $PMA_Theme->getPath() : '';
  362. $version = self::getVersionParameter();
  363. // The user preferences have been merged at this point
  364. // so we can conditionally add CodeMirror
  365. if ($GLOBALS['cfg']['CodemirrorEnable']) {
  366. $this->scripts->addFile('vendor/codemirror/lib/codemirror.js');
  367. $this->scripts->addFile('vendor/codemirror/mode/sql/sql.js');
  368. $this->scripts->addFile('vendor/codemirror/addon/runmode/runmode.js');
  369. $this->scripts->addFile('vendor/codemirror/addon/hint/show-hint.js');
  370. $this->scripts->addFile('vendor/codemirror/addon/hint/sql-hint.js');
  371. if ($GLOBALS['cfg']['LintEnable']) {
  372. $this->scripts->addFile('vendor/codemirror/addon/lint/lint.js');
  373. $this->scripts->addFile(
  374. 'codemirror/addon/lint/sql-lint.js'
  375. );
  376. }
  377. }
  378. $this->scripts->addCode(
  379. 'ConsoleEnterExecutes='
  380. . ($GLOBALS['cfg']['ConsoleEnterExecutes'] ? 'true' : 'false')
  381. );
  382. $this->scripts->addFiles($this->console->getScripts());
  383. if ($this->userprefsOfferImport) {
  384. $this->scripts->addFile('config.js');
  385. }
  386. if ($this->menuEnabled && $GLOBALS['server'] > 0) {
  387. $nav = new Navigation(
  388. $this->template,
  389. new Relation($dbi),
  390. $dbi
  391. );
  392. $navigation = $nav->getDisplay();
  393. }
  394. $customHeader = Config::renderHeader();
  395. // offer to load user preferences from localStorage
  396. if ($this->userprefsOfferImport) {
  397. $loadUserPreferences = $this->userPreferences->autoloadGetHeader();
  398. }
  399. if ($this->menuEnabled && $GLOBALS['server'] > 0) {
  400. $menu = $this->menu->getDisplay();
  401. }
  402. $console = $this->console->getDisplay();
  403. $messages = $this->getMessage();
  404. return $this->template->render('header', [
  405. 'lang' => $GLOBALS['lang'],
  406. 'allow_third_party_framing' => $GLOBALS['cfg']['AllowThirdPartyFraming'],
  407. 'is_print_view' => $this->isPrintView,
  408. 'base_dir' => $baseDir,
  409. 'unique_value' => $uniqueValue,
  410. 'theme_path' => $themePath,
  411. 'version' => $version,
  412. 'text_dir' => $GLOBALS['text_dir'],
  413. 'server' => $GLOBALS['server'] ?? null,
  414. 'title' => $this->getPageTitle(),
  415. 'scripts' => $this->scripts->getDisplay(),
  416. 'body_id' => $this->bodyId,
  417. 'navigation' => $navigation ?? '',
  418. 'custom_header' => $customHeader,
  419. 'load_user_preferences' => $loadUserPreferences ?? '',
  420. 'show_hint' => $GLOBALS['cfg']['ShowHint'],
  421. 'is_warnings_enabled' => $this->warningsEnabled,
  422. 'is_menu_enabled' => $this->menuEnabled,
  423. 'menu' => $menu ?? '',
  424. 'console' => $console,
  425. 'messages' => $messages,
  426. 'recent_table' => $recentTable,
  427. ]);
  428. }
  429. /**
  430. * Returns the message to be displayed at the top of
  431. * the page, including the executed SQL query, if any.
  432. */
  433. public function getMessage(): string
  434. {
  435. $retval = '';
  436. $message = '';
  437. if (! empty($GLOBALS['message'])) {
  438. $message = $GLOBALS['message'];
  439. unset($GLOBALS['message']);
  440. } elseif (! empty($_REQUEST['message'])) {
  441. $message = $_REQUEST['message'];
  442. }
  443. if (! empty($message)) {
  444. if (isset($GLOBALS['buffer_message'])) {
  445. $buffer_message = $GLOBALS['buffer_message'];
  446. }
  447. $retval .= Generator::getMessage($message);
  448. if (isset($buffer_message)) {
  449. $GLOBALS['buffer_message'] = $buffer_message;
  450. }
  451. }
  452. return $retval;
  453. }
  454. /**
  455. * Sends out the HTTP headers
  456. */
  457. public function sendHttpHeaders(): void
  458. {
  459. if (defined('TESTSUITE')) {
  460. return;
  461. }
  462. /**
  463. * Sends http headers
  464. */
  465. $GLOBALS['now'] = gmdate('D, d M Y H:i:s') . ' GMT';
  466. /* Prevent against ClickJacking by disabling framing */
  467. if (strtolower((string) $GLOBALS['cfg']['AllowThirdPartyFraming']) === 'sameorigin') {
  468. header(
  469. 'X-Frame-Options: SAMEORIGIN'
  470. );
  471. } elseif ($GLOBALS['cfg']['AllowThirdPartyFraming'] !== true) {
  472. header(
  473. 'X-Frame-Options: DENY'
  474. );
  475. }
  476. header(
  477. 'Referrer-Policy: no-referrer'
  478. );
  479. $cspHeaders = $this->getCspHeaders();
  480. foreach ($cspHeaders as $cspHeader) {
  481. header($cspHeader);
  482. }
  483. // Re-enable possible disabled XSS filters
  484. // see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
  485. header(
  486. 'X-XSS-Protection: 1; mode=block'
  487. );
  488. // "nosniff", prevents Internet Explorer and Google Chrome from MIME-sniffing
  489. // a response away from the declared content-type
  490. // see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
  491. header(
  492. 'X-Content-Type-Options: nosniff'
  493. );
  494. // Adobe cross-domain-policies
  495. // see https://www.adobe.com/devnet/articles/crossdomain_policy_file_spec.html
  496. header(
  497. 'X-Permitted-Cross-Domain-Policies: none'
  498. );
  499. // Robots meta tag
  500. // see https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
  501. header(
  502. 'X-Robots-Tag: noindex, nofollow'
  503. );
  504. Core::noCacheHeader();
  505. if (! defined('IS_TRANSFORMATION_WRAPPER')) {
  506. // Define the charset to be used
  507. header('Content-Type: text/html; charset=utf-8');
  508. }
  509. $this->headerIsSent = true;
  510. }
  511. /**
  512. * If the page is missing the title, this function
  513. * will set it to something reasonable
  514. */
  515. public function getPageTitle(): string
  516. {
  517. if (strlen($this->title) == 0) {
  518. if ($GLOBALS['server'] > 0) {
  519. if (strlen($GLOBALS['table'])) {
  520. $temp_title = $GLOBALS['cfg']['TitleTable'];
  521. } elseif (strlen($GLOBALS['db'])) {
  522. $temp_title = $GLOBALS['cfg']['TitleDatabase'];
  523. } elseif (strlen($GLOBALS['cfg']['Server']['host'])) {
  524. $temp_title = $GLOBALS['cfg']['TitleServer'];
  525. } else {
  526. $temp_title = $GLOBALS['cfg']['TitleDefault'];
  527. }
  528. $this->title = htmlspecialchars(
  529. Util::expandUserString($temp_title)
  530. );
  531. } else {
  532. $this->title = 'phpMyAdmin';
  533. }
  534. }
  535. return $this->title;
  536. }
  537. /**
  538. * Get all the CSP allow policy headers
  539. *
  540. * @return string[]
  541. */
  542. private function getCspHeaders(): array
  543. {
  544. global $cfg;
  545. $mapTileUrls = ' *.tile.openstreetmap.org';
  546. $captchaUrl = '';
  547. $cspAllow = $cfg['CSPAllow'];
  548. if (! empty($cfg['CaptchaApi'])
  549. && ! empty($cfg['CaptchaRequestParam'])
  550. && ! empty($cfg['CaptchaResponseParam'])
  551. && ! empty($cfg['CaptchaLoginPrivateKey'])
  552. && ! empty($cfg['CaptchaLoginPublicKey'])
  553. ) {
  554. $captchaUrl = ' ' . $cfg['CaptchaCsp'] . ' ';
  555. }
  556. return [
  557. "Content-Security-Policy: default-src 'self' "
  558. . $captchaUrl
  559. . $cspAllow . ';'
  560. . "script-src 'self' 'unsafe-inline' 'unsafe-eval' "
  561. . $captchaUrl
  562. . $cspAllow . ';'
  563. . "style-src 'self' 'unsafe-inline' "
  564. . $captchaUrl
  565. . $cspAllow
  566. . ';'
  567. . "img-src 'self' data: "
  568. . $cspAllow
  569. . $mapTileUrls
  570. . $captchaUrl
  571. . ';'
  572. . "object-src 'none';",
  573. "X-Content-Security-Policy: default-src 'self' "
  574. . $captchaUrl
  575. . $cspAllow . ';'
  576. . 'options inline-script eval-script;'
  577. . 'referrer no-referrer;'
  578. . "img-src 'self' data: "
  579. . $cspAllow
  580. . $mapTileUrls
  581. . $captchaUrl
  582. . ';'
  583. . "object-src 'none';",
  584. "X-WebKit-CSP: default-src 'self' "
  585. . $captchaUrl
  586. . $cspAllow . ';'
  587. . "script-src 'self' "
  588. . $captchaUrl
  589. . $cspAllow
  590. . " 'unsafe-inline' 'unsafe-eval';"
  591. . 'referrer no-referrer;'
  592. . "style-src 'self' 'unsafe-inline' "
  593. . $captchaUrl
  594. . ';'
  595. . "img-src 'self' data: "
  596. . $cspAllow
  597. . $mapTileUrls
  598. . $captchaUrl
  599. . ';'
  600. . "object-src 'none';",
  601. ];
  602. }
  603. /**
  604. * Add recently used table and reload the navigation.
  605. *
  606. * @param string $db Database name where the table is located.
  607. * @param string $table The table name
  608. */
  609. private function addRecentTable(string $db, string $table): string
  610. {
  611. $retval = '';
  612. if ($this->menuEnabled
  613. && strlen($table) > 0
  614. && $GLOBALS['cfg']['NumRecentTables'] > 0
  615. ) {
  616. $tmp_result = RecentFavoriteTable::getInstance('recent')->add(
  617. $db,
  618. $table
  619. );
  620. if ($tmp_result === true) {
  621. $retval = RecentFavoriteTable::getHtmlUpdateRecentTables();
  622. } else {
  623. $error = $tmp_result;
  624. $retval = $error->getDisplay();
  625. }
  626. }
  627. return $retval;
  628. }
  629. /**
  630. * Returns the phpMyAdmin version to be appended to the url to avoid caching
  631. * between versions
  632. *
  633. * @return string urlencoded pma version as a parameter
  634. */
  635. public static function getVersionParameter(): string
  636. {
  637. return 'v=' . urlencode(PMA_VERSION);
  638. }
  639. private function getVariablesForJavaScript(): string
  640. {
  641. global $cfg, $PMA_Theme;
  642. $maxInputVars = ini_get('max_input_vars');
  643. $maxInputVarsValue = $maxInputVars === false || $maxInputVars === '' ? 'false' : (int) $maxInputVars;
  644. return $this->template->render('javascript/variables', [
  645. 'first_day_of_calendar' => $cfg['FirstDayOfCalendar'],
  646. 'theme_image_path' => $PMA_Theme !== null ? $PMA_Theme->getImgPath() : '',
  647. 'max_input_vars' => $maxInputVarsValue,
  648. ]);
  649. }
  650. }