BrowseForeigners.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. <?php
  2. /**
  3. * Contains functions used by browse foreigners
  4. */
  5. declare(strict_types=1);
  6. namespace PhpMyAdmin;
  7. use function asort;
  8. use function ceil;
  9. use function floor;
  10. use function htmlspecialchars;
  11. use function is_array;
  12. use function mb_strlen;
  13. use function mb_substr;
  14. /**
  15. * PhpMyAdmin\BrowseForeigners class
  16. */
  17. class BrowseForeigners
  18. {
  19. /** @var int */
  20. private $limitChars;
  21. /** @var int */
  22. private $maxRows;
  23. /** @var int */
  24. private $repeatCells;
  25. /** @var bool */
  26. private $showAll;
  27. /** @var string */
  28. private $themeImage;
  29. /** @var Template */
  30. public $template;
  31. /**
  32. * @param Template $template Template object
  33. */
  34. public function __construct(Template $template)
  35. {
  36. global $cfg, $PMA_Theme;
  37. $this->template = $template;
  38. $this->limitChars = (int) $cfg['LimitChars'];
  39. $this->maxRows = (int) $cfg['MaxRows'];
  40. $this->repeatCells = (int) $cfg['RepeatCells'];
  41. $this->showAll = (bool) $cfg['ShowAll'];
  42. $this->themeImage = $PMA_Theme->getImgPath();
  43. }
  44. /**
  45. * Function to get html for one relational key
  46. *
  47. * @param int $horizontal_count the current horizontal count
  48. * @param string $header table header
  49. * @param array $keys all the keys
  50. * @param int $indexByKeyname index by keyname
  51. * @param array $descriptions descriptions
  52. * @param int $indexByDescription index by description
  53. * @param string $current_value current value on the edit form
  54. *
  55. * @return array the generated html
  56. */
  57. private function getHtmlForOneKey(
  58. int $horizontal_count,
  59. string $header,
  60. array $keys,
  61. int $indexByKeyname,
  62. array $descriptions,
  63. int $indexByDescription,
  64. string $current_value
  65. ): array {
  66. $horizontal_count++;
  67. $output = '';
  68. // whether the key name corresponds to the selected value in the form
  69. $rightKeynameIsSelected = false;
  70. $leftKeynameIsSelected = false;
  71. if ($this->repeatCells > 0 && $horizontal_count > $this->repeatCells) {
  72. $output .= $header;
  73. $horizontal_count = 0;
  74. }
  75. // key names and descriptions for the left section,
  76. // sorted by key names
  77. $leftKeyname = $keys[$indexByKeyname];
  78. [
  79. $leftDescription,
  80. $leftDescriptionTitle,
  81. ] = $this->getDescriptionAndTitle($descriptions[$indexByKeyname]);
  82. // key names and descriptions for the right section,
  83. // sorted by descriptions
  84. $rightKeyname = $keys[$indexByDescription];
  85. [
  86. $rightDescription,
  87. $rightDescriptionTitle,
  88. ] = $this->getDescriptionAndTitle($descriptions[$indexByDescription]);
  89. $indexByDescription++;
  90. if (! empty($current_value)) {
  91. $rightKeynameIsSelected = $rightKeyname == $current_value;
  92. $leftKeynameIsSelected = $leftKeyname == $current_value;
  93. }
  94. $output .= '<tr class="noclick">';
  95. $output .= $this->template->render('table/browse_foreigners/column_element', [
  96. 'keyname' => $leftKeyname,
  97. 'description' => $leftDescription,
  98. 'title' => $leftDescriptionTitle,
  99. 'is_selected' => $leftKeynameIsSelected,
  100. 'nowrap' => true,
  101. ]);
  102. $output .= $this->template->render('table/browse_foreigners/column_element', [
  103. 'keyname' => $leftKeyname,
  104. 'description' => $leftDescription,
  105. 'title' => $leftDescriptionTitle,
  106. 'is_selected' => $leftKeynameIsSelected,
  107. 'nowrap' => false,
  108. ]);
  109. $output .= '<td width="20%">'
  110. . '<img src="' . $this->themeImage . 'spacer.png" alt=""'
  111. . ' width="1" height="1"></td>';
  112. $output .= $this->template->render('table/browse_foreigners/column_element', [
  113. 'keyname' => $rightKeyname,
  114. 'description' => $rightDescription,
  115. 'title' => $rightDescriptionTitle,
  116. 'is_selected' => $rightKeynameIsSelected,
  117. 'nowrap' => false,
  118. ]);
  119. $output .= $this->template->render('table/browse_foreigners/column_element', [
  120. 'keyname' => $rightKeyname,
  121. 'description' => $rightDescription,
  122. 'title' => $rightDescriptionTitle,
  123. 'is_selected' => $rightKeynameIsSelected,
  124. 'nowrap' => true,
  125. ]);
  126. $output .= '</tr>';
  127. return [
  128. $output,
  129. $horizontal_count,
  130. $indexByDescription,
  131. ];
  132. }
  133. /**
  134. * Function to get html for relational field selection
  135. *
  136. * @param string $db current database
  137. * @param string $table current table
  138. * @param string $field field
  139. * @param array $foreignData foreign column data
  140. * @param string|null $fieldkey field key
  141. * @param string $current_value current columns's value
  142. */
  143. public function getHtmlForRelationalFieldSelection(
  144. string $db,
  145. string $table,
  146. string $field,
  147. array $foreignData,
  148. ?string $fieldkey,
  149. string $current_value
  150. ): string {
  151. $gotopage = $this->getHtmlForGotoPage($foreignData);
  152. $foreignShowAll = $this->template->render('table/browse_foreigners/show_all', [
  153. 'foreign_data' => $foreignData,
  154. 'show_all' => $this->showAll,
  155. 'max_rows' => $this->maxRows,
  156. ]);
  157. $output = '<form class="ajax" '
  158. . 'id="browse_foreign_form" name="browse_foreign_from" action="'
  159. . Url::getFromRoute('/browse-foreigners')
  160. . '" method="post"><fieldset>'
  161. . Url::getHiddenInputs($db, $table)
  162. . '<input type="hidden" name="field" value="' . htmlspecialchars($field)
  163. . '">'
  164. . '<input type="hidden" name="fieldkey" value="'
  165. . (isset($fieldkey) ? htmlspecialchars($fieldkey) : '') . '">';
  166. if (isset($_POST['rownumber'])) {
  167. $output .= '<input type="hidden" name="rownumber" value="'
  168. . htmlspecialchars((string) $_POST['rownumber']) . '">';
  169. }
  170. $filter_value = (isset($_POST['foreign_filter'])
  171. ? htmlspecialchars($_POST['foreign_filter'])
  172. : '');
  173. $output .= '<span class="formelement">'
  174. . '<label for="input_foreign_filter">' . __('Search:') . '</label>'
  175. . '<input type="text" name="foreign_filter" '
  176. . 'id="input_foreign_filter" '
  177. . 'value="' . $filter_value . '" data-old="' . $filter_value . '" '
  178. . '>'
  179. . '<input class="btn btn-primary" type="submit" name="submit_foreign_filter" value="'
  180. . __('Go') . '">'
  181. . '</span>'
  182. . '<span class="formelement">' . $gotopage . '</span>'
  183. . '<span class="formelement">' . $foreignShowAll . '</span>'
  184. . '</fieldset>'
  185. . '</form>';
  186. $output .= '<table class="pma-table" width="100%" id="browse_foreign_table">';
  187. if (! is_array($foreignData['disp_row'])) {
  188. return $output . '</tbody>'
  189. . '</table>';
  190. }
  191. $header = '<tr>
  192. <th>' . __('Keyname') . '</th>
  193. <th>' . __('Description') . '</th>
  194. <td width="20%"></td>
  195. <th>' . __('Description') . '</th>
  196. <th>' . __('Keyname') . '</th>
  197. </tr>';
  198. $output .= '<thead>' . $header . '</thead>' . "\n"
  199. . '<tfoot>' . $header . '</tfoot>' . "\n"
  200. . '<tbody>' . "\n";
  201. $descriptions = [];
  202. $keys = [];
  203. foreach ($foreignData['disp_row'] as $relrow) {
  204. if ($foreignData['foreign_display'] != false) {
  205. $descriptions[] = $relrow[$foreignData['foreign_display']] ?? '';
  206. } else {
  207. $descriptions[] = '';
  208. }
  209. $keys[] = $relrow[$foreignData['foreign_field']];
  210. }
  211. asort($keys);
  212. $horizontal_count = 0;
  213. $indexByDescription = 0;
  214. foreach ($keys as $indexByKeyname => $value) {
  215. [
  216. $html,
  217. $horizontal_count,
  218. $indexByDescription,
  219. ] = $this->getHtmlForOneKey(
  220. $horizontal_count,
  221. $header,
  222. $keys,
  223. $indexByKeyname,
  224. $descriptions,
  225. $indexByDescription,
  226. $current_value
  227. );
  228. $output .= $html;
  229. }
  230. $output .= '</tbody>'
  231. . '</table>';
  232. return $output;
  233. }
  234. /**
  235. * Get the description (possibly truncated) and the title
  236. *
  237. * @param string $description the key name's description
  238. *
  239. * @return array the new description and title
  240. */
  241. private function getDescriptionAndTitle(string $description): array
  242. {
  243. if (mb_strlen($description) <= $this->limitChars) {
  244. $description = htmlspecialchars(
  245. $description
  246. );
  247. $descriptionTitle = '';
  248. } else {
  249. $descriptionTitle = htmlspecialchars(
  250. $description
  251. );
  252. $description = htmlspecialchars(
  253. mb_substr(
  254. $description,
  255. 0,
  256. $this->limitChars
  257. )
  258. . '...'
  259. );
  260. }
  261. return [
  262. $description,
  263. $descriptionTitle,
  264. ];
  265. }
  266. /**
  267. * Function to get html for the goto page option
  268. *
  269. * @param array|null $foreignData foreign data
  270. */
  271. private function getHtmlForGotoPage(?array $foreignData): string
  272. {
  273. $gotopage = '';
  274. isset($_POST['pos']) ? $pos = $_POST['pos'] : $pos = 0;
  275. if ($foreignData === null || ! is_array($foreignData['disp_row'])) {
  276. return $gotopage;
  277. }
  278. $pageNow = (int) floor($pos / $this->maxRows) + 1;
  279. $nbTotalPage = (int) ceil($foreignData['the_total'] / $this->maxRows);
  280. if ($foreignData['the_total'] > $this->maxRows) {
  281. $gotopage = Util::pageselector(
  282. 'pos',
  283. $this->maxRows,
  284. $pageNow,
  285. $nbTotalPage,
  286. 200,
  287. 5,
  288. 5,
  289. 20,
  290. 10,
  291. __('Page number:')
  292. );
  293. }
  294. return $gotopage;
  295. }
  296. /**
  297. * Function to get foreign limit
  298. *
  299. * @param string|null $foreignShowAll foreign navigation
  300. */
  301. public function getForeignLimit(?string $foreignShowAll): ?string
  302. {
  303. if (isset($foreignShowAll) && $foreignShowAll == __('Show all')) {
  304. return null;
  305. }
  306. isset($_POST['pos']) ? $pos = $_POST['pos'] : $pos = 0;
  307. return 'LIMIT ' . $pos . ', ' . $this->maxRows . ' ';
  308. }
  309. }