PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Adapto/Datagrid/List.php

http://github.com/egeniq/adapto
PHP | 820 lines | 523 code | 126 blank | 171 comment | 203 complexity | a46f8fc71d1ad0adc8d5b1e3a1df7553 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Adapto Toolkit.
  4. * Detailed copyright and licensing information can be found
  5. * in the doc/COPYRIGHT and doc/LICENSE files which should be
  6. * included in the distribution.
  7. *
  8. * @package adapto
  9. * @subpackage utils
  10. *
  11. * @copyright (c) 2000-2007 Ibuildings.nl BV
  12. *
  13. * @license http://www.achievo.org/atk/licensing ATK Open Source License
  14. */
  15. /**
  16. * The data grid list component renders the recordlist.
  17. *
  18. * Options:
  19. * - alwaysShowGrid: always show datagrid, even if there are no records?
  20. * by default the grid won't display the grid headers
  21. * in embedded mode when there are no existing records
  22. *
  23. * @author petercv
  24. * @package adapto
  25. * @subpackage datagrid
  26. *
  27. * @todo At the moment the grid component is based on atkRecordList legacy code. This code
  28. * should be refactored / optimized but this also means that some backwards incompatible
  29. * changes have to be made to the differen ATK attributes. For example, the component
  30. * still uses the recordlist flags when calling attribute methods because the attributes
  31. * are not 100% aware yet of the new datagrid.
  32. *
  33. * @todo Keyboard navigation is at the moment broken because we don't supply the navigation array.
  34. * However, this should be done in a different way anyhow.
  35. */
  36. class Adapto_Datagrid_List extends Adapto_DGComponent
  37. {
  38. protected $m_hasActionColumn = null;
  39. /**
  40. * Render the list.
  41. *
  42. * @return string rendered list HTML
  43. */
  44. public function render()
  45. {
  46. $alwaysShowGrid = $this->getOption('alwaysShowGrid', false);
  47. if (!$alwaysShowGrid && $this->getGrid()->isEmbedded() && !$this->getGrid()->isUpdate() && count($this->getGrid()->getRecords()) == 0) {
  48. return '';
  49. }
  50. $grid = $this->getGrid();
  51. $data = $this->getRecordlistData($grid->getRecords(), $grid->getDefaultActions(), $grid->getExcludes());
  52. $ui = $grid->getEntity()->getUi();
  53. return $ui->render($grid->getEntity()->getTemplate("admin"), $data, $grid->getEntity()->m_module);
  54. }
  55. /**
  56. * Get records for a recordlist without actually rendering the recordlist.
  57. * @param atkEntity $entity the atkentity of the grid
  58. * @param Array $recordset the list of records
  59. * @param Array $actions the default actions array
  60. * @param Integer $flags recordlist flags (see the top of this file)
  61. * @param Array $suppressList fields we don't display
  62. * @return String The rendered recordlist
  63. */
  64. private function getRecordlistData($recordset, $actions, $suppressList = "")
  65. {
  66. $grid = $this->getGrid();
  67. $theme = $this->getTheme();
  68. $page = $this->getPage();
  69. $edit = $grid->isEditing();
  70. $page->register_style($theme->stylePath("recordlist.css", $grid->getEntity()->m_module));
  71. $page->register_script(Adapto_Config::getGlobal("atkroot") . "atk/javascript/recordlist.js");
  72. $listName = $grid->getName();
  73. $defaulthighlight = $theme->getAttribute("highlight");
  74. $selectcolor = $theme->getAttribute("select");
  75. /* retrieve list array */
  76. $list = $this->listArray($recordset, "", $actions, $suppressList);
  77. /* Check if some flags are still valid or not... */
  78. $hasMRA = $grid->hasFlag(atkDataGrid::MULTI_RECORD_ACTIONS);
  79. if ($hasMRA && (count($list["mra"]) == 0 || count($list["rows"]) == 0)) {
  80. $hasMRA = false;
  81. }
  82. $hasSearch = $grid->hasFlag(atkDataGrid::SEARCH) && !$grid->isEditing();
  83. if ($hasSearch && count($list["search"]) == 0) {
  84. $hasSearch = false;
  85. }
  86. if ($grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS) && (count($grid->getEntity()->m_priority_actions) == 0 || count($list["rows"]) == 0)) {
  87. $grid->removeFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS);
  88. } else if ($grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS)) {
  89. $grid->removeFlag(atkDataGrid::MULTI_RECORD_ACTIONS);
  90. if ($grid->getEntity()->m_priority_max == 0)
  91. $grid->getEntity()->m_priority_max = $grid->getEntity()->m_priority_min + count($list["rows"]) - 1;
  92. }
  93. $hasActionCol = $this->_hasActionColumn($list, $hasSearch);
  94. $orientation = Adapto_Config::getGlobal('recordlist_orientation', $theme->getAttribute("recordlist_orientation"));
  95. $vorientation = trim(Adapto_Config::getGlobal('recordlist_vorientation', $theme->getAttribute("recordlist_vorientation")));
  96. /**************/
  97. /* HEADER ROW */
  98. /**************/
  99. $headercols = array();
  100. if ($hasActionCol && count($list["rows"]) == 0) {
  101. if ($orientation == "left" || $orientation == "both") {
  102. // empty cell above search button, if zero rows
  103. // if $orientation is empty, no search button is shown, so no empty cell is needed
  104. $headercols[] = array("content" => "&nbsp;");
  105. }
  106. }
  107. if (!$edit && ($hasMRA || $grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS))) {
  108. $headercols[] = array("content" => ""); // Empty leader on top of mra action list.
  109. }
  110. if ($grid->hasFlag(atkDataGrid::LOCKING)) {
  111. $lockHeadIcon = atkTheme::getInstance()->iconPath('lock_' . $grid->getEntity()->getLockMode() . '_head', 'lock', $grid->getEntity()->m_module);
  112. $headercols[] = array("content" => '<img src="' . $lockHeadIcon . '">');
  113. }
  114. if (($orientation == "left" || $orientation == "both") && ($hasActionCol && count($list["rows"]) > 0)) {
  115. $headercols[] = array("content" => "");
  116. }
  117. foreach (array_values($list["heading"]) as $head) {
  118. if (!$grid->hasFlag(atkDataGrid::SORT) || empty($head["order"])) {
  119. $headercols[] = array("content" => $head["title"]);
  120. } else {
  121. $call = $grid->getUpdateCall(array('atkorderby' => $head['order'], 'atkstartat' => 0));
  122. $headercols[] = array("content" => $this->_getHeadingAnchorHtml($call, $head['title']));
  123. }
  124. }
  125. if (($orientation == "right" || $orientation == "both") && ($hasActionCol && count($list["rows"]) > 0)) {
  126. $headercols[] = array("content" => "");
  127. }
  128. if ($hasActionCol && count($list["rows"]) == 0) {
  129. if ($orientation == "right" || $orientation == "both") {
  130. // empty cell above search button, if zero rows
  131. // if $orientation is empty, no search button is shown, so no empty cell is needed
  132. $headercols[] = array("content" => "&nbsp;");
  133. }
  134. }
  135. /**************/
  136. /* SORT ROW */
  137. /**************/
  138. $sortcols = array();
  139. $sortstart = "";
  140. $sortend = "";
  141. if ($grid->hasFlag(atkDataGrid::EXTENDED_SORT)) {
  142. $call = Adapto_htmlentities($grid->getUpdateCall(array('atkstartat' => 0), array(), 'ATK.DataGrid.extractExtendedSortOverrides'));
  143. $button = '<input type="button" value="' . atktext("sort") . '" onclick="' . $call . '">';
  144. if (!$edit && ($hasMRA || $grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS))) {
  145. $sortcols[] = array("content" => ""); // Empty leader on top of mra action list.
  146. }
  147. if ($grid->hasFlag(atkDataGrid::LOCKING)) {
  148. $sortcols[] = array("content" => "");
  149. }
  150. if ($orientation == "left" || $orientation == "both") {
  151. $sortcols[] = array("content" => $button);
  152. }
  153. foreach (array_keys($list["heading"]) as $key) {
  154. if (isset($list["sort"][$key]))
  155. $sortcols[] = array("content" => $list["sort"][$key]);
  156. }
  157. if ($orientation == "right" || $orientation == "both") {
  158. $sortcols[] = array("content" => $button);
  159. }
  160. }
  161. /**************/
  162. /* SEARCH ROW */
  163. /**************/
  164. $searchcols = array();
  165. $searchstart = "";
  166. $searchend = "";
  167. if ($hasSearch) {
  168. $call = Adapto_htmlentities($grid->getUpdateCall(array('atkstartat' => 0), array(), 'ATK.DataGrid.extractSearchOverrides'));
  169. $buttonType = $grid->isEmbedded() ? "button" : "submit";
  170. $button = '<input type="' . $buttonType . '" class="btn_search" value="' . atktext("search") . '" onclick="' . $call . ' return false;">';
  171. if ($grid->hasFlag(atkDataGrid::EXTENDED_SEARCH)) {
  172. $button .= '<br>'
  173. . href(
  174. atkSelf() . "?atkentitytype=" . $grid->getActionEntity()->atkEntityType() . "&atkaction="
  175. . $grid->getActionEntity()->getExtendedSearchAction(), "(" . atktext("search_extended") . ")", SESSION_NESTED);
  176. }
  177. // $searchstart = '<a name="searchform"></a>';
  178. $searchstart = "";
  179. if (!$edit && ($hasMRA || $grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS))) {
  180. $searchcols[] = array("content" => "");
  181. }
  182. if ($grid->hasFlag(atkDataGrid::LOCKING)) {
  183. $searchcols[] = array("content" => "");
  184. }
  185. if ($orientation == "left" || $orientation == "both") {
  186. $searchcols[] = array("content" => $button);
  187. }
  188. foreach (array_keys($list["heading"]) as $key) {
  189. if (isset($list["search"][$key])) {
  190. $searchcols[] = array("content" => $list["search"][$key]);
  191. } else {
  192. $searchcols[] = array("content" => "");
  193. }
  194. }
  195. if ($orientation == "right" || $orientation == "both") {
  196. $searchcols[] = array("content" => $button);
  197. }
  198. }
  199. /*******************************************/
  200. /* MULTI-RECORD-(PRIORITY-)ACTIONS FORM DATA */
  201. /*******************************************/
  202. $liststart = "";
  203. $listend = "";
  204. if (!$edit && ($hasMRA || $grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS))) {
  205. $page->register_script(Adapto_Config::getGlobal("atkroot") . "atk/javascript/formselect.js");
  206. if ($hasMRA) {
  207. $liststart .= '<script language="javascript" type="text/javascript">var ' . $listName . ' = new Object();</script>';
  208. }
  209. }
  210. /********/
  211. /* ROWS */
  212. /********/
  213. $records = array();
  214. $keys = array_keys($actions);
  215. $actionurl = (count($actions) > 0) ? $actions[$keys[0]] : '';
  216. $actionloader = "rl_a['" . $listName . "'] = {};";
  217. $actionloader .= "\nrl_a['" . $listName . "']['base'] = '" . session_vars($grid->getActionSessionStatus(), 1, $actionurl) . "';";
  218. $actionloader .= "\nrl_a['" . $listName . "']['embed'] = " . ($grid->isEmbedded() ? 'true' : 'false') . ";";
  219. for ($i = 0, $_i = count($list["rows"]); $i < $_i; $i++) {
  220. $record = array();
  221. /* Special rowColor method makes it possible to change the row color based on the record data.
  222. * the method can return a simple value (which will be used for the normal row color), or can be
  223. * an array, in which case the first element will be the normal row color, and the second the mouseover
  224. * row color, example: function rowColor(&$record, $num) { return array('red', 'blue'); }
  225. */
  226. $method = "rowColor";
  227. $bgn = "";
  228. $bgh = $defaulthighlight;
  229. if (method_exists($grid->getEntity(), $method)) {
  230. $bgn = $grid->getEntity()->$method($recordset[$i], $i);
  231. if (is_array($bgn))
  232. list($bgn, $bgh) = $bgn;
  233. }
  234. $record['class'] = $grid->getEntity()->rowClass($recordset[$i], $i);
  235. foreach ($grid->getEntity()->getRowClassCallback() as $callback) {
  236. $record['class'] .= " " . call_user_func_array($callback, array($recordset[$i], $i));
  237. }
  238. /* alternate colors of rows */
  239. $record["background"] = $bgn;
  240. $record["highlight"] = $bgh;
  241. $record["rownum"] = $i;
  242. $record["id"] = $listName . '_' . $i;
  243. $record["type"] = $list["rows"][$i]["type"];
  244. /* multi-record-priority-actions -> priority selection */
  245. if (!$edit && $grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS)) {
  246. $select = '<select name="' . $listName . '_atkselector[]">' . '<option value="' . Adapto_htmlentities($list["rows"][$i]["selector"])
  247. . '"></option>';
  248. for ($j = $grid->getEntity()->m_priority_min; $j <= $grid->getEntity()->m_priority_max; $j++)
  249. $select .= '<option value="' . $j . '">' . $j . '</option>';
  250. $select .= '</select>';
  251. $record["cols"][] = array("content" => $select, "type" => "mrpa");
  252. }
  253. /* multi-record-actions -> checkbox */
  254. elseif (!$edit && $hasMRA) {
  255. if (count($list["rows"][$i]["mra"]) > 0) {
  256. $inputHTML = '';
  257. switch ($grid->getMRASelectionMode()) {
  258. case MRA_SINGLE_SELECT:
  259. $inputHTML = '<input type="radio" name="' . $listName . '_atkselector[]" value="' . $list["rows"][$i]["selector"]
  260. . '" class="atkradiobutton" onclick="if (this.disabled) this.checked = false">';
  261. break;
  262. case MRA_NO_SELECT:
  263. $inputHTML = '<input type="checkbox" disabled="disabled" checked="checked">' . '<input type="hidden" name="' . $listName
  264. . '_atkselector[]" value="' . $list["rows"][$i]["selector"] . '">';
  265. break;
  266. case MRA_MULTI_SELECT:
  267. default:
  268. $inputHTML = '<input type="checkbox" name="' . $listName . '_atkselector[' . $i . ']" value="' . $list["rows"][$i]["selector"]
  269. . '" class="atkcheckbox" onclick="if (this.disabled) this.checked = false">';
  270. }
  271. $record["cols"][] = array(
  272. "content" => $inputHTML . '
  273. <script language="javascript" type="text/javascript">' . $listName . '["' . Adapto_htmlentities($list["rows"][$i]["selector"])
  274. . '"] =
  275. new Array("' . implode($list["rows"][$i]["mra"], '","') . '");
  276. </script>', "type" => "mra");
  277. } else
  278. $record["cols"][] = array("content" => "");
  279. }
  280. // editable row, add selector
  281. else if ($edit && $list["rows"][$i]['edit']) {
  282. $liststart .= '<input type="hidden" name="atkdatagriddata_AE_' . $i . '_AE_atkprimkey" value="' . htmlentities($list["rows"][$i]["selector"])
  283. . '">';
  284. }
  285. /* locked? */
  286. if ($grid->hasFlag(atkDataGrid::LOCKING)) {
  287. if (is_array($list["rows"][$i]["lock"])) {
  288. $this->getPage()->register_script(Adapto_Config::getGlobal('atkroot') . 'atk/javascript/overlibmws/overlibmws.js');
  289. $lockIcon = atkTheme::getInstance()->iconPath('lock_' . $grid->getEntity()->getLockMode(), 'lock', $grid->getEntity()->m_module);
  290. $lockInfo = addslashes(str_replace(array("\r\n", "\r", "\n"), " ", Adapto_htmlentities($this->getLockInfo($list["rows"][$i]["lock"]))));
  291. $record["cols"][] = array(
  292. "content" => '<img src="' . $lockIcon . '" onmouseover="return overlib(\'' . $lockInfo
  293. . '\', NOFOLLOW, FULLHTML);" onmouseout="nd();" border="0">', "type" => "lock");
  294. } else
  295. $record["cols"][] = array("content" => "");
  296. }
  297. $str_actions = "<span class=\"actions\">";
  298. $actionloader .= "\nrl_a['" . $listName . "'][" . $i . "] = {};";
  299. $icons = (Adapto_Config::getGlobal('recordlist_icons', $theme->getAttribute("recordlist_icons")) === false
  300. || Adapto_Config::getGlobal('recordlist_icons', $theme->getAttribute("recordlist_icons")) === 'false' ? false : true);
  301. foreach ($list["rows"][$i]["actions"] as $name => $url) {
  302. if (substr($url, 0, 11) == 'javascript:') {
  303. $call = substr($url, 11);
  304. $actionloader .= "\nrl_a['{$listName}'][{$i}]['{$name}'] = function() { $call; };";
  305. } else {
  306. $actionloader .= "\nrl_a['{$listName}'][{$i}]['{$name}'] = '$url';";
  307. }
  308. $module = $grid->getEntity()->m_module;
  309. $entitytype = $grid->getEntity()->m_type;
  310. $actionKeys = array('action_' . $module . '_' . $entitytype . '_' . $name, 'action_' . $entitytype . '_' . $name, 'action_' . $name, $name);
  311. $link = Adapto_htmlentities($this->text($actionKeys));
  312. if ($icons == true) {
  313. $icon = $theme->iconPath($module . '_' . $entitytype . '_' . strtolower($name), "recordlist", $module, '', false);
  314. if (!$icon) {
  315. $icon = $theme->iconPath($module . '_' . strtolower($name), "recordlist", $module, '', false);
  316. }
  317. if (!$icon) {
  318. $icon = $theme->iconPath(strtolower($name), "recordlist", $grid->getEntity()->m_module);
  319. }
  320. if (is_file($icon)) {
  321. $link = sprintf('<img class="recordlist" border="0" src="%1$s" alt="%2$s" title="%2$s">', $icon, $link);
  322. } else {
  323. atkwarning("Icon for action '$name' not found!");
  324. }
  325. }
  326. $confirmtext = "false";
  327. if (Adapto_Config::getGlobal("recordlist_javascript_delete") && $name == "delete")
  328. $confirmtext = "'" . $grid->getEntity()->confirmActionText($name) . "'";
  329. $str_actions .= $this->_renderRecordActionLink($url, $link, $listName, $i, $name, $confirmtext);
  330. }
  331. $str_actions .= "</span>";
  332. /* actions (left) */
  333. if ($orientation == "left" || $orientation == "both") {
  334. if (!empty($list["rows"][$i]["actions"])) {
  335. $record["cols"][] = array("content" => $str_actions, "type" => "actions");
  336. } else if ($hasActionCol) {
  337. $record["cols"][] = array("content" => "");
  338. }
  339. }
  340. /* columns */
  341. foreach ($list["rows"][$i]["data"] as $html)
  342. $record["cols"][] = array("content" => $html, "type" => "data");
  343. /* actions (right) */
  344. if ($orientation == "right" || $orientation == "both") {
  345. if (!empty($list["rows"][$i]["actions"]))
  346. $record["cols"][] = array("content" => $str_actions, "type" => "actions");
  347. else if ($hasActionCol) {
  348. $record["cols"][] = array("content" => "");
  349. }
  350. }
  351. $records[] = $record;
  352. }
  353. $page->register_scriptcode($actionloader);
  354. $this->m_actionloader = $actionloader;
  355. /*************/
  356. /* TOTAL ROW */
  357. /*************/
  358. $totalcols = array();
  359. if (count($list["total"]) > 0) {
  360. if (!$edit && ($hasMRA || $grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS)))
  361. $totalcols[] = array("content" => "");
  362. if ($grid->hasFlag(atkDataGrid::LOCKING))
  363. $totalcols[] = array("content" => "");
  364. if (($orientation == "left" || $orientation == "both") && ($hasActionCol && count($list["rows"]) > 0))
  365. $totalcols[] = array("content" => "");
  366. foreach (array_keys($list["heading"]) as $key) {
  367. $totalcols[] = array("content" => (isset($list["total"][$key]) ? $list["total"][$key] : ""));
  368. }
  369. if (($orientation == "right" || $orientation == "both") && ($hasActionCol && count($list["rows"]) > 0))
  370. $totalcols[] = array("content" => "");
  371. }
  372. /*************************************************/
  373. /* MULTI-RECORD-PRIORITY-ACTION FORM (CONTINUED) */
  374. /*************************************************/
  375. $mra = "";
  376. if (!$edit && $grid->hasFlag(atkDataGrid::MULTI_RECORD_PRIORITY_ACTIONS)) {
  377. $target = session_url(atkSelf() . '?atkentitytype=' . $grid->getActionEntity()->atkEntityType(), SESSION_NESTED);
  378. /* multiple actions -> dropdown */
  379. if (count($grid->getEntity()->m_priority_actions) > 1) {
  380. $mra = '<select name="' . $listName . '_atkaction">' . '<option value="">' . atktext("with_selected") . ':</option>';
  381. foreach ($grid->getEntity()->m_priority_actions as $name)
  382. $mra .= '<option value="' . $name . '">' . atktext($name) . '</option>';
  383. $mra .= '</select>&nbsp;' . $this->getCustomMraHtml() . '<input type="button" class="btn" value="' . atktext("submit")
  384. . '" onclick="atkSubmitMRPA(\'' . $listName . '\', this.form, \'' . $target . '\')">';
  385. }
  386. /* one action -> only the submit button */
  387. else {
  388. $mra = $this->getCustomMraHtml() . '<input type="hidden" name="' . $listName . '_atkaction" value="' . $grid->getEntity()->m_priority_actions[0]
  389. . '">' . '<input type="button" class="btn" value="' . atktext($grid->getEntity()->m_priority_actions[0]) . '" onclick="atkSubmitMRPA(\''
  390. . $listName . '\', this.form, \'' . $target . '\')">';
  391. }
  392. }
  393. /****************************************/
  394. /* MULTI-RECORD-ACTION FORM (CONTINUED) */
  395. /****************************************/
  396. elseif (!$edit && $hasMRA) {
  397. $postvars = $grid->getEntity()->m_postvars;
  398. $target = session_url(
  399. atkSelf() . '?atkentitytype=' . $grid->getEntity()->atkEntityType() . '&atktarget='
  400. . (!empty($postvars['atktarget']) ? $postvars['atktarget'] : '') . '&atktargetvar='
  401. . (!empty($postvars['atktargetvar']) ? $postvars['atktargetvar'] : '') . '&atktargetvartpl='
  402. . (!empty($postvars['atktargetvartpl']) ? $postvars['atktargetvartpl'] : ''), SESSION_NESTED);
  403. $mra = (count($list["rows"]) > 1 && $grid->getMRASelectionMode() == MRA_MULTI_SELECT ? '<a href="javascript:void(0)" onclick="updateSelection(\''
  404. . $listName . '\', $(this).up(\'form\'), \'all\')">' . atktext("select_all") . '</a> | '
  405. . '<a href="javascript:void(0)" onclick="updateSelection(\'' . $listName . '\', $(this).up(\'form\'), \'none\')">'
  406. . atktext("deselect_all") . '</a> | ' . '<a href="javascript:void(0)" onclick="updateSelection(\'' . $listName
  407. . '\', $(this).up(\'form\'), \'invert\')">' . atktext("select_invert") . '</a> ' . '<div style="height: 8px"></div>' : '');
  408. $module = $grid->getEntity()->m_module;
  409. $entitytype = $grid->getEntity()->m_type;
  410. /* multiple actions -> dropdown */
  411. if (count($list["mra"]) > 1) {
  412. $default = $this->getGrid()->getMRADefaultAction();
  413. $mra .= '<select name="' . $listName . '_atkaction" onchange="javascript:updateSelectable(\'' . $listName . '\', this.form)">'
  414. . '<option value="">' . atktext("with_selected") . ':</option>';
  415. foreach ($list["mra"] as $name) {
  416. if ($grid->getEntity()->allowed($name)) {
  417. $actionKeys = array('action_' . $module . '_' . $entitytype . '_' . $name, 'action_' . $entitytype . '_' . $name, 'action_' . $name, $name);
  418. $mra .= '<option value="' . $name . '"';
  419. if ($default == $name) {
  420. $mra .= 'selected="selected"';
  421. }
  422. $mra .= '>' . atktext($actionKeys, $grid->getEntity()->m_module, $grid->getEntity()->m_type) . '</option>';
  423. }
  424. }
  425. $embedded = $this->getGrid()->isEmbedded() ? 'true' : 'false';
  426. $mra .= '</select>&nbsp;' . $this->getCustomMraHtml() . '<input type="button" class="btn" value="' . atktext("submit")
  427. . '" onclick="atkSubmitMRA(\'' . $listName . '\', this.form, \'' . $target . '\', ' . $embedded . ', false)">';
  428. }
  429. /* one action -> only the submit button */
  430. else {
  431. if ($grid->getEntity()->allowed($list["mra"][0])) {
  432. $name = $list["mra"][0];
  433. $actionKeys = array('action_' . $module . '_' . $entitytype . '_' . $name, 'action_' . $entitytype . '_' . $name, 'action_' . $name, $name);
  434. $embedded = $this->getGrid()->isEmbedded() ? 'true' : 'false';
  435. $mra .= '<input type="hidden" name="' . $listName . '_atkaction" value="' . $name . '">' . $this->getCustomMraHtml()
  436. . '<input type="button" class="btn" value="' . atktext($actionKeys, $grid->getEntity()->m_module, $grid->getEntity()->m_type)
  437. . '" onclick="atkSubmitMRA(\'' . $listName . '\', this.form, \'' . $target . '\', ' . $embedded . ', false)">';
  438. }
  439. }
  440. }
  441. else if ($edit) {
  442. $embedded = $this->getGrid()->isEmbedded() ? 'true' : 'false';
  443. $mra = '<input type="button" class="btn" value="' . atktext('save') . '" onclick="' . Adapto_htmlentities($this->getGrid()->getSaveCall()) . '">';
  444. }
  445. if (Adapto_Config::getGlobal("use_keyboard_handler")) {
  446. $kb = &atkKeyboard::getInstance();
  447. $kb->addRecordListHandler($listName, $selectcolor, count($records));
  448. }
  449. $recordListData = array("vorientation" => $vorientation, "rows" => $records, "header" => $headercols, "search" => $searchcols, "sort" => $sortcols,
  450. "total" => $totalcols, "searchstart" => $searchstart, "searchend" => $searchend, "sortstart" => $sortstart, "sortend" => $sortend,
  451. "liststart" => $liststart, "listend" => $listend, "listid" => $listName, "mra" => $mra, "editing" => $this->getGrid()->isEditing());
  452. return $recordListData;
  453. }
  454. /**
  455. * Returns the link for heading anchors
  456. *
  457. * @param string $onClickCall the value for in the onclick
  458. * @param the title of the link $title
  459. * @return string
  460. */
  461. protected function _getHeadingAnchorHtml($onClickCall, $title)
  462. {
  463. return '<a href="javascript:void(0)" onclick="' . Adapto_htmlentities($onClickCall) . '">' . $title . '</a>';
  464. }
  465. /**
  466. * Renders a link for a row action with the specified parameters
  467. * @param string $url The URL for the record action
  468. * @param string $link HTML for displaying the link (between the <a></a>)
  469. * @param string $listName The name of the recordlist
  470. * @param string $i The row index to render the action for
  471. * @param string $name The action name
  472. * @param bool|string $confirmtext The text for the confirmation if set
  473. */
  474. protected function _renderRecordActionLink($url, $link, $listName, $i, $name, $confirmtext = "false")
  475. {
  476. return '<a href="' . "javascript:rl_do('$listName',$i,'$name',$confirmtext);" . '">' . $link . '</a>&nbsp;';
  477. }
  478. /**
  479. * Returns an HTML snippet which is used to display information about locks
  480. * on a certain record in a small popup.
  481. *
  482. * @param array $locks lock(s) array
  483. */
  484. protected function getLockInfo($locks)
  485. {
  486. return $this->getUi()->render('lockinfo.tpl', array('locks' => $locks), $this->getEntity()->m_module);
  487. }
  488. /**
  489. * Checks wether the recordlist should display a column which holds the actions.
  490. *
  491. * @access private
  492. * @param Array $list The recordlist data
  493. * @return bool Wether the list should display an extra column to hold the actions
  494. */
  495. function _hasActionColumn($list, $hasSearch)
  496. {
  497. $grid = $this->getGrid();
  498. if ($this->m_hasActionColumn === null) {
  499. // when there's a search bar, we always need an extra column (for the button)
  500. if ($hasSearch) {
  501. $this->m_hasActionColumn = true;
  502. }
  503. // when there's an extended sort bar, we also need the column (for the sort button)
  504. else if ($grid->hasFlag(atkDataGrid::EXTENDED_SORT)) {
  505. $this->m_hasActionColumn = true;
  506. } else {
  507. // otherwise, it depends on whether one of the records has actions defined.
  508. $this->m_hasActionColumn = false;
  509. foreach ($list["rows"] as $record) {
  510. if (!empty($record['actions'])) {
  511. $this->m_hasActionColumn = true;
  512. break;
  513. }
  514. }
  515. }
  516. }
  517. return $this->m_hasActionColumn;
  518. }
  519. /**
  520. * Get custom mra html
  521. *
  522. * @return string The custom mra html
  523. */
  524. function getCustomMraHtml()
  525. {
  526. $grid = $this;
  527. if (method_exists($grid->getEntity(), "getcustommrahtml")) {
  528. $output = $grid->getEntity()->getCustomMraHtml();
  529. return $output;
  530. }
  531. }
  532. /**
  533. * Function outputs an array with all information necessary to output a recordlist.
  534. *
  535. * @param Array $recordset List of records that need to be displayed
  536. * @param String $prefix Prefix for each column name (used for subcalls)
  537. * @param Array $actions List of default actions for each record
  538. * @param Array $suppress An array of fields that you want to hide
  539. *
  540. * The result array contains the following information:
  541. * "name" => the name of the recordlist
  542. * "heading" => for each visible column an array containing: "title" {, "url"}
  543. * "search" => for each visible column HTML input field(s) for searching
  544. * "rows" => list of rows, per row: "data", "actions", "mra", "record"
  545. * "totalraw" => for each totalisable column the sum value field(s) (raw)
  546. * "total" => for each totalisable column the sum value (display)
  547. * "mra" => list of all multi-record actions
  548. *
  549. * @return see above
  550. */
  551. private function listArray(&$recordset, $prefix = "", $actions = array(), $suppress = array())
  552. {
  553. $grid = $this->getGrid();
  554. $flags = $this->convertDataGridFlags();
  555. if (!is_array($suppress))
  556. $suppress = array();
  557. $result = array("name" => $grid->getName(), "heading" => array(), "search" => array(), "rows" => array(), "totalraw" => array(), "total" => array(),
  558. "mra" => array());
  559. $columnConfig = &$grid->getEntity()->getColumnConfig($grid->getName());
  560. if (!hasFlag($flags, RL_NO_SEARCH) || $grid->isEditing()) {
  561. $grid->getEntity()->setAttribSizes();
  562. }
  563. $this->_addListArrayHeader($result, $prefix, $suppress, $flags, $columnConfig);
  564. /* actions array can contain multi-record-actions */
  565. if (count($actions) == 2 && count(array_diff(array_keys($actions), array("actions", "mra"))) == 0) {
  566. $mra = $actions["mra"];
  567. $actions = $actions["actions"];
  568. } else
  569. $mra = $grid->getEntity()->hasFlag(EF_NO_DELETE) ? array() : array("delete");
  570. /* get the rows */
  571. for ($i = 0, $_i = count($recordset); $i < $_i; $i++) {
  572. $result["rows"][$i] = array("columns" => array(), "actions" => $actions, "mra" => $mra, "record" => &$recordset[$i], "data" => array());
  573. $result["rows"][$i]["selector"] = $grid->getEntity()->primaryKey($recordset[$i]);
  574. $result["rows"][$i]["type"] = "data";
  575. $row = &$result["rows"][$i];
  576. /* locked */
  577. if ($grid->hasFlag(atkDataGrid::LOCKING)) {
  578. $result["rows"][$i]["lock"] = $grid->getEntity()->m_lock
  579. ->isLocked($result["rows"][$i]["selector"], $grid->getEntity()->m_table, $grid->getEntity()->getLockMode());
  580. if (is_array($result["rows"][$i]["lock"]) && $grid->getEntity()->getLockMode() == atkLock::EXCLUSIVE) {
  581. unset($row["actions"]["edit"]);
  582. unset($row["actions"]["delete"]);
  583. $row["mra"] = array();
  584. }
  585. }
  586. /* actions / mra */
  587. $grid->getEntity()->collectRecordActions($row["record"], $row["actions"], $row["mra"]);
  588. // filter actions we are allowed to execute
  589. foreach ($row["actions"] as $name => $url) {
  590. if (!empty($url) && $grid->getEntity()->allowed($name, $row["record"])) {
  591. /* dirty hack */
  592. $atkencoded = strpos($url, "_15B") > 0;
  593. $url = str_replace("%5B", "[", $url);
  594. $url = str_replace("%5D", "]", $url);
  595. $url = str_replace("_1" . "5B", "[", $url);
  596. $url = str_replace("_1" . "5D", "]", $url);
  597. if ($atkencoded)
  598. $url = str_replace('[pk]', atkurlencode(rawurlencode($row["selector"]), false), $url);
  599. else
  600. $url = str_replace('[pk]', rawurlencode($row["selector"]), $url);
  601. $parser = new Adapto_StringParser($url);
  602. $url = $parser->parse($row["record"], true, false);
  603. $row["actions"][$name] = $url;
  604. } else {
  605. unset($row["actions"][$name]);
  606. }
  607. }
  608. // filter multi-record-actions we are allowed to execute
  609. foreach ($row["mra"] as $j => $name) {
  610. if (!$grid->getEntity()->allowed($name, $row["record"])) {
  611. unset($row["mra"][$j]);
  612. }
  613. }
  614. $row['mra'] = array_values($row['mra']);
  615. $result["mra"] = array_merge($result["mra"], $row["mra"]);
  616. /* columns */
  617. $editAllowed = $grid->getPostvar('atkgridedit', false) && $grid->getEntity()->allowed('edit', $result["rows"][$i]["record"]);
  618. $result["rows"][$i]["edit"] = $editAllowed;
  619. $this->_addListArrayRow($result, $prefix, $suppress, $flags, $i, $editAllowed);
  620. }
  621. if (hasFlag($flags, RL_EXT_SORT) && $columnConfig->hasSubTotals()) {
  622. $totalizer = new Adapto_Totalizer($grid->getEntity(), $columnConfig);
  623. $result["rows"] = $totalizer->totalize($result["rows"]);
  624. }
  625. if (hasFlag($flags, RL_MRA))
  626. $result["mra"] = array_values(array_unique($result["mra"]));
  627. return $result;
  628. }
  629. /**
  630. * Returns the list attributes and their possible child column
  631. * names for this list.
  632. */
  633. protected function _getColumns()
  634. {
  635. $result = array();
  636. $columns = $this->getOption('columns');
  637. if ($columns == null) {
  638. foreach ($this->getEntity()->getAttributeNames() as $attrName) {
  639. $entry = new stdClass();
  640. $entry->attrName = $attrName;
  641. $entry->columnName = '*';
  642. $result[] = $entry;
  643. }
  644. } else {
  645. foreach ($columns as $column) {
  646. $parts = explode('.', $column);
  647. $entry = new stdClass();
  648. $entry->attrName = $parts[0];
  649. $entry->columnName = isset($parts[1]) ? $parts[1] : null;
  650. $result[] = $entry;
  651. }
  652. }
  653. return $result;
  654. }
  655. /**
  656. * Add the list array header to the result list.
  657. */
  658. private function _addListArrayHeader(&$listArray, $prefix, $suppressList, $flags, $columnConfig)
  659. {
  660. $columns = $this->_getColumns();
  661. foreach ($columns as $column) {
  662. if (in_array($column->attrName, $suppressList)) {
  663. continue;
  664. }
  665. $attr = $this->getEntity()->getAttribute($column->attrName);
  666. if (!is_object($attr)) {
  667. throw new Exception("Invalid attribute {$column->attrName} for entity " . $this->getEntity()->atkEntityType());
  668. }
  669. $attr
  670. ->addToListArrayHeader($this->getEntity()->getAction(), $listArray, $prefix, $flags, $this->getGrid()->getPostvar('atksearch'),
  671. $columnConfig, $this->getGrid(), $column->columnName);
  672. }
  673. }
  674. /**
  675. * Adds the given row the the list array.
  676. */
  677. private function _addListArrayRow(&$listArray, $prefix, $suppressList, $flags, $rowIndex, $editAllowed)
  678. {
  679. $columns = $this->_getColumns();
  680. foreach ($columns as $column) {
  681. if (in_array($column->attrName, $suppressList)) {
  682. continue;
  683. }
  684. $attr = $this->getEntity()->getAttribute($column->attrName);
  685. if (!is_object($attr)) {
  686. throw new Exception("Invalid attribute {$column->attrName} for entity " . $this->getEntity()->atkEntityType());
  687. }
  688. $edit = $editAllowed && in_array($column->attrName, $this->getEntity()->m_editableListAttributes);
  689. $attr
  690. ->addToListArrayRow($this->getEntity()->getAction(), $listArray, $rowIndex, $prefix, $flags, $edit, $this->getGrid(), $column->columnName);
  691. }
  692. }
  693. }