BrowseForeigners.php 11 KB

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