Subs-List.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. <?php
  2. /**
  3. * This file contains a standard way of displaying lists for SMF.
  4. * Simple Machines Forum (SMF)
  5. *
  6. * @package SMF
  7. * @author Simple Machines http://www.simplemachines.org
  8. * @copyright 2011 Simple Machines
  9. * @license http://www.simplemachines.org/about/smf/license.php BSD
  10. *
  11. * @version 2.1 Alpha 1
  12. */
  13. if (!defined('SMF'))
  14. die('Hacking attempt...');
  15. /**
  16. * Create a new list
  17. * @param array $listOptions
  18. */
  19. function createList($listOptions)
  20. {
  21. global $context, $settings, $options, $txt, $modSettings, $scripturl;
  22. assert(isset($listOptions['id']));
  23. assert(isset($listOptions['columns']));
  24. assert(is_array($listOptions['columns']));
  25. assert((empty($listOptions['items_per_page']) || (isset($listOptions['get_count']['function'], $listOptions['base_href']) && is_numeric($listOptions['items_per_page']))));
  26. assert((empty($listOptions['default_sort_col']) || isset($listOptions['columns'][$listOptions['default_sort_col']])));
  27. assert((!isset($listOptions['form']) || isset($listOptions['form']['href'])));
  28. // All the context data will be easily accessible by using a reference.
  29. $context[$listOptions['id']] = array();
  30. $list_context = &$context[$listOptions['id']];
  31. // Figure out the sort.
  32. if (empty($listOptions['default_sort_col']))
  33. {
  34. $list_context['sort'] = array();
  35. $sort = '1=1';
  36. }
  37. else
  38. {
  39. $request_var_sort = isset($listOptions['request_vars']['sort']) ? $listOptions['request_vars']['sort'] : 'sort';
  40. $request_var_desc = isset($listOptions['request_vars']['desc']) ? $listOptions['request_vars']['desc'] : 'desc';
  41. if (isset($_REQUEST[$request_var_sort], $listOptions['columns'][$_REQUEST[$request_var_sort]], $listOptions['columns'][$_REQUEST[$request_var_sort]]['sort']))
  42. $list_context['sort'] = array(
  43. 'id' => $_REQUEST[$request_var_sort],
  44. 'desc' => isset($_REQUEST[$request_var_desc]) && isset($listOptions['columns'][$_REQUEST[$request_var_sort]]['sort']['reverse']),
  45. );
  46. else
  47. $list_context['sort'] = array(
  48. 'id' => $listOptions['default_sort_col'],
  49. 'desc' => (!empty($listOptions['default_sort_dir']) && $listOptions['default_sort_dir'] == 'desc') || (!empty($listOptions['columns'][$listOptions['default_sort_col']]['sort']['default']) && substr($listOptions['columns'][$listOptions['default_sort_col']]['sort']['default'], -4, 4) == 'desc') ? true : false,
  50. );
  51. // Set the database column sort.
  52. $sort = $listOptions['columns'][$list_context['sort']['id']]['sort'][$list_context['sort']['desc'] ? 'reverse' : 'default'];
  53. }
  54. $list_context['start_var_name'] = isset($listOptions['start_var_name']) ? $listOptions['start_var_name'] : 'start';
  55. // In some cases the full list must be shown, regardless of the amount of items.
  56. if (empty($listOptions['items_per_page']))
  57. {
  58. $list_context['start'] = 0;
  59. $list_context['items_per_page'] = 0;
  60. }
  61. // With items per page set, calculate total number of items and page index.
  62. else
  63. {
  64. // First get an impression of how many items to expect.
  65. if (isset($listOptions['get_count']['file']))
  66. require_once($listOptions['get_count']['file']);
  67. $list_context['total_num_items'] = call_user_func_array($listOptions['get_count']['function'], empty($listOptions['get_count']['params']) ? array() : $listOptions['get_count']['params']);
  68. // Default the start to the beginning...sounds logical.
  69. $list_context['start'] = isset($_REQUEST[$list_context['start_var_name']]) ? (int) $_REQUEST[$list_context['start_var_name']] : 0;
  70. $list_context['items_per_page'] = $listOptions['items_per_page'];
  71. // Then create a page index.
  72. $list_context['page_index'] = constructPageIndex($listOptions['base_href'] . (empty($list_context['sort']) ? '' : ';' . $request_var_sort . '=' . $list_context['sort']['id'] . ($list_context['sort']['desc'] ? ';' . $request_var_desc : '')) . ($list_context['start_var_name'] != 'start' ? ';' . $list_context['start_var_name'] . '=%1$d' : ''), $list_context['start'], $list_context['total_num_items'], $list_context['items_per_page'], $list_context['start_var_name'] != 'start');
  73. }
  74. // Prepare the headers of the table.
  75. $list_context['headers'] = array();
  76. foreach ($listOptions['columns'] as $column_id => $column)
  77. $list_context['headers'][] = array(
  78. 'id' => $column_id,
  79. 'label' => isset($column['header']['eval']) ? eval($column['header']['eval']) : (isset($column['header']['value']) ? $column['header']['value'] : ''),
  80. 'href' => empty($listOptions['default_sort_col']) || empty($column['sort']) ? '' : $listOptions['base_href'] . ';' . $request_var_sort . '=' . $column_id . ($column_id === $list_context['sort']['id'] && !$list_context['sort']['desc'] && isset($column['sort']['reverse']) ? ';' . $request_var_desc : '') . (empty($list_context['start']) ? '' : ';' . $list_context['start_var_name'] . '=' . $list_context['start']),
  81. 'sort_image' => empty($listOptions['default_sort_col']) || empty($column['sort']) || $column_id !== $list_context['sort']['id'] ? null : ($list_context['sort']['desc'] ? 'down' : 'up'),
  82. 'class' => isset($column['header']['class']) ? $column['header']['class'] : '',
  83. 'style' => isset($column['header']['style']) ? $column['header']['style'] : '',
  84. 'colspan' => isset($column['header']['colspan']) ? $column['header']['colspan'] : '',
  85. );
  86. // We know the amount of columns, might be useful for the template.
  87. $list_context['num_columns'] = count($listOptions['columns']);
  88. $list_context['width'] = isset($listOptions['width']) ? $listOptions['width'] : '0';
  89. // Get the file with the function for the item list.
  90. if (isset($listOptions['get_items']['file']))
  91. require_once($listOptions['get_items']['file']);
  92. // Call the function and include which items we want and in what order.
  93. $list_items = call_user_func_array($listOptions['get_items']['function'], array_merge(array($list_context['start'], $list_context['items_per_page'], $sort), empty($listOptions['get_items']['params']) ? array() : $listOptions['get_items']['params']));
  94. // Loop through the list items to be shown and construct the data values.
  95. $list_context['rows'] = array();
  96. foreach ($list_items as $item_id => $list_item)
  97. {
  98. $cur_row = array();
  99. foreach ($listOptions['columns'] as $column_id => $column)
  100. {
  101. $cur_data = array();
  102. // A value straight from the database?
  103. if (isset($column['data']['db']))
  104. $cur_data['value'] = $list_item[$column['data']['db']];
  105. // Take the value from the database and make it HTML safe.
  106. elseif (isset($column['data']['db_htmlsafe']))
  107. $cur_data['value'] = htmlspecialchars($list_item[$column['data']['db_htmlsafe']]);
  108. // Using sprintf is probably the most readable way of injecting data.
  109. elseif (isset($column['data']['sprintf']))
  110. {
  111. $params = array();
  112. foreach ($column['data']['sprintf']['params'] as $sprintf_param => $htmlsafe)
  113. $params[] = $htmlsafe ? htmlspecialchars($list_item[$sprintf_param]) : $list_item[$sprintf_param];
  114. $cur_data['value'] = vsprintf($column['data']['sprintf']['format'], $params);
  115. }
  116. // The most flexible way probably is applying a custom function.
  117. elseif (isset($column['data']['function']))
  118. $cur_data['value'] = $column['data']['function']($list_item);
  119. // A modified value (inject the database values).
  120. elseif (isset($column['data']['eval']))
  121. $cur_data['value'] = eval(preg_replace('~%([a-zA-Z0-9\-_]+)%~', '$list_item[\'$1\']', $column['data']['eval']));
  122. // A literal value.
  123. elseif (isset($column['data']['value']))
  124. $cur_data['value'] = $column['data']['value'];
  125. // Empty value.
  126. else
  127. $cur_data['value'] = '';
  128. // Allow for basic formatting.
  129. if (!empty($column['data']['comma_format']))
  130. $cur_data['value'] = comma_format($list_item[$column['data']['comma_format']]);
  131. elseif (!empty($column['data']['timeformat']))
  132. $cur_data['value'] = timeformat($list_item[$column['data']['timeformat']]);
  133. // Set a style class for this column?
  134. if (isset($column['data']['class']))
  135. $cur_data['class'] = $column['data']['class'];
  136. // Fully customized styling for the cells in this column only.
  137. if (isset($column['data']['style']))
  138. $cur_data['style'] = $column['data']['style'];
  139. // Add the data cell properties to the current row.
  140. $cur_row[$column_id] = $cur_data;
  141. }
  142. $list_context['rows'][$item_id]['class'] = '';
  143. $list_context['rows'][$item_id]['style'] = '';
  144. // Maybe we wat set a custom class for the row based on the data in the row itself
  145. if (isset($listOptions['data_check']))
  146. {
  147. if (isset($listOptions['data_check']['class']))
  148. $list_context['rows'][$item_id]['class'] = ' ' . $listOptions['data_check']['class']($list_item);
  149. if (isset($listOptions['data_check']['style']))
  150. $list_context['rows'][$item_id]['style'] = ' style="' . $listOptions['data_check']['style']($list_item) . '"';
  151. }
  152. // Insert the row into the list.
  153. $list_context['rows'][$item_id]['data'] = $cur_row;
  154. }
  155. // The title is currently optional.
  156. if (isset($listOptions['title']))
  157. $list_context['title'] = $listOptions['title'];
  158. // In case there's a form, share it with the template context.
  159. if (isset($listOptions['form']))
  160. {
  161. $list_context['form'] = $listOptions['form'];
  162. if (!isset($list_context['form']['hidden_fields']))
  163. $list_context['form']['hidden_fields'] = array();
  164. // Always add a session check field.
  165. $list_context['form']['hidden_fields'][$context['session_var']] = $context['session_id'];
  166. // Will this do a token check?
  167. if (isset($listOptions['form']['token']))
  168. $list_context['form']['hidden_fields'][$context[$listOptions['form']['token'] . '_token_var']] = $context[$listOptions['form']['token'] . '_token'];
  169. // Include the starting page as hidden field?
  170. if (!empty($list_context['form']['include_start']) && !empty($list_context['start']))
  171. $list_context['form']['hidden_fields'][$list_context['start_var_name']] = $list_context['start'];
  172. // If sorting needs to be the same after submitting, add the parameter.
  173. if (!empty($list_context['form']['include_sort']) && !empty($list_context['sort']))
  174. {
  175. $list_context['form']['hidden_fields']['sort'] = $list_context['sort']['id'];
  176. if ($list_context['sort']['desc'])
  177. $list_context['form']['hidden_fields']['desc'] = 1;
  178. }
  179. }
  180. // Wanna say something nice in case there are no items?
  181. if (isset($listOptions['no_items_label']))
  182. {
  183. $list_context['no_items_label'] = $listOptions['no_items_label'];
  184. $list_context['no_items_align'] = isset($listOptions['no_items_align']) ? $listOptions['no_items_align'] : '';
  185. }
  186. // A list can sometimes need a few extra rows above and below.
  187. if (isset($listOptions['additional_rows']))
  188. {
  189. $list_context['additional_rows'] = array();
  190. foreach ($listOptions['additional_rows'] as $row)
  191. {
  192. if (empty($row))
  193. continue;
  194. // Supported row positions: top_of_list, after_title,
  195. // above_column_headers, below_table_data, bottom_of_list.
  196. if (!isset($list_context['additional_rows'][$row['position']]))
  197. $list_context['additional_rows'][$row['position']] = array();
  198. $list_context['additional_rows'][$row['position']][] = $row;
  199. }
  200. }
  201. // Add an option for inline JavaScript.
  202. if (isset($listOptions['javascript']))
  203. $list_context['javascript'] = $listOptions['javascript'];
  204. // We want a menu.
  205. if (isset($listOptions['list_menu']))
  206. $list_context['list_menu'] = $listOptions['list_menu'];
  207. // Make sure the template is loaded.
  208. loadTemplate('GenericList');
  209. }
  210. ?>