PageRenderTime 37ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/admincrud/extensions/bootstrap/widgets/TbJsonGridView.php

https://github.com/max-rautkin/yii-admincrud
PHP | 412 lines | 271 code | 45 blank | 96 comment | 47 complexity | 69e8c842c0394242510064ff4eb42752 MD5 | raw file
Possible License(s): LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /**
  3. * TbJsonGridView class file
  4. *
  5. * Converts TbGridView into a Json Javascript grid when using AJAX updates calls. This grid makes use of localStorage or
  6. * a custom in memory plugin to avoid repetitive ajax requests/responses and speed up data visualization.
  7. *
  8. * @author: antonio ramirez <antonio@clevertech.biz>
  9. * @copyright Copyright &copy; Clevertech 2012-
  10. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  11. * @package YiiBooster bootstrap.widgets
  12. */
  13. Yii::import('bootstrap.widgets.TbGridView');
  14. Yii::import('bootstrap.widgets.TbJsonDataColumn');
  15. class TbJsonGridView extends TbGridView
  16. {
  17. /**
  18. * @var boolean $json true when there is an AJAX request. TbJsonGridView expect a JSON response.
  19. */
  20. public $json;
  21. /**
  22. * @var string $template Overridden standard template to add second pager on top.
  23. */
  24. public $template = "{pager}\n{items}\n{summary}\n{pager}";
  25. /**
  26. * @var int $cacheTTL how long we keep the responses on cache? It will depend on cacheTTLType (seconds, minutes, hours)
  27. */
  28. public $cacheTTL = 1;
  29. /**
  30. * @var string the type of cache duration
  31. * s: seconds
  32. * m: minutes
  33. * h: hours
  34. */
  35. public $cacheTTLType = 's';
  36. /**
  37. * @var bool $localCache whether we use client ajax cache or not. True by default.
  38. */
  39. public $localCache = true;
  40. /**
  41. * @var array the configuration for the pager.
  42. * Defaults to <code>array('class'=>'ext.bootstrap.widgets.TbPager')</code>.
  43. */
  44. public $pager = array('class' => 'bootstrap.widgets.TbJsonPager');
  45. /**
  46. * Initializes $json property to find out whether ajax request or not
  47. */
  48. public function init()
  49. {
  50. // parse request to find out whether is an ajax request or not, if so, then return $dataProvider JSON formatted
  51. $this->json = Yii::app()->getRequest()->getIsAjaxRequest();
  52. if ($this->json) {
  53. $this->template = '{items}';
  54. } // going to render only items!
  55. parent::init();
  56. }
  57. /**
  58. * Renders the view.
  59. * This is the main entry of the whole view rendering.
  60. * Child classes should mainly override {@link renderContent} method.
  61. */
  62. public function run()
  63. {
  64. if (!$this->json) {
  65. parent::run();
  66. } else {
  67. $this->registerClientScript();
  68. $this->renderContent();
  69. }
  70. }
  71. /**
  72. * Renders the pager.
  73. */
  74. public function renderPager()
  75. {
  76. if (!$this->json) {
  77. parent::renderPager();
  78. return true;
  79. }
  80. $pager = array();
  81. if (is_string($this->pager)) {
  82. $class = $this->pager;
  83. } else if (is_array($this->pager)) {
  84. $pager = $this->pager;
  85. if (isset($pager['class'])) {
  86. $class = $pager['class'];
  87. unset($pager['class']);
  88. }
  89. }
  90. $pager['pages'] = $this->dataProvider->getPagination();
  91. if ($pager['pages']->getPageCount() > 1) {
  92. $pager['json'] = $this->json;
  93. $widget = $this->createWidget($class, $pager);
  94. return $widget->run();
  95. } else {
  96. return array();
  97. }
  98. }
  99. /**
  100. * Creates column objects and initializes them.
  101. */
  102. protected function initColumns()
  103. {
  104. foreach ($this->columns as $i => $column) {
  105. if (is_array($column) && !isset($column['class'])) {
  106. $this->columns[$i]['class'] = 'bootstrap.widgets.TbJsonDataColumn';
  107. }
  108. }
  109. parent::initColumns();
  110. }
  111. /**
  112. * Renders the data items for the grid view.
  113. */
  114. public function renderItems()
  115. {
  116. if ($this->json) {
  117. echo function_exists('json_encode')
  118. ? json_encode($this->renderTableBody())
  119. : CJSON::encode(
  120. $this->renderTableBody()
  121. );
  122. } elseif ($this->dataProvider->getItemCount() > 0 || $this->showTableOnEmpty) {
  123. echo "<table class=\"{$this->itemsCssClass}\">\n";
  124. $this->renderTableHeader();
  125. ob_start();
  126. $this->renderTableBody();
  127. $body = ob_get_clean();
  128. $this->renderTableFooter();
  129. echo $body; // TFOOT must appear before TBODY according to the standard.
  130. echo "</table>";
  131. $this->renderTemplates();
  132. } else {
  133. $this->renderEmptyText();
  134. }
  135. }
  136. /**
  137. * Renders the required templates for the client engine (jqote2 used)
  138. */
  139. protected function renderTemplates()
  140. {
  141. echo $this->renderTemplate($this->id . '-col-template', '<td <%=this.attrs%>><%=this.content%></td>');
  142. echo $this->renderTemplate(
  143. $this->id . '-row-template',
  144. '<tr class="<%=this.class%>"><% var t = "#' . $this->id . '-col-template"; out += $.jqote(t, this.cols);%></tr>'
  145. );
  146. echo $this->renderTemplate($this->id . '-keys-template', '<span><%=this%></span>');
  147. if ($this->enablePagination) {
  148. echo $this->renderTemplate(
  149. $this->id . '-pager-template',
  150. '<li class="<%=this.class%>"><a href="<%=this.url%>"><%=this.text%></a></li>'
  151. );
  152. }
  153. }
  154. /**
  155. * Encloses the given JavaScript within a script tag.
  156. *
  157. * @param string $id
  158. * @param string $text the JavaScript to be enclosed
  159. *
  160. * @return string the enclosed JavaScript
  161. */
  162. public function renderTemplate($id, $text)
  163. {
  164. return "<script type=\"text/x-jqote-template\" id=\"{$id}\">\n<![CDATA[\n{$text}\n]]>\n</script>";
  165. }
  166. /**
  167. * Renders the table body.
  168. */
  169. public function renderTableBody()
  170. {
  171. $data = $this->dataProvider->getData();
  172. $n = count($data);
  173. if ($this->json) {
  174. return $this->renderTableBodyJSON($n);
  175. }
  176. echo "<tbody>\n";
  177. if ($n > 0) {
  178. for ($row = 0; $row < $n; ++$row) {
  179. $this->renderTableRow($row);
  180. }
  181. } else {
  182. echo '<tr><td colspan="' . count($this->columns) . '" class="empty">';
  183. $this->renderEmptyText();
  184. echo "</td></tr>\n";
  185. }
  186. echo "</tbody>\n";
  187. }
  188. /**
  189. * Renders the body table for JSON requests - assumed ajax is for JSON
  190. *
  191. * @param integer $rows
  192. *
  193. * @return array
  194. */
  195. protected function renderTableBodyJSON($rows)
  196. {
  197. $tbody = array(
  198. 'headers' => array(),
  199. 'rows' => array(),
  200. 'keys' => array()
  201. );
  202. foreach ($this->columns as $column) {
  203. if (property_exists($column, 'json')) {
  204. $column->json = $this->json;
  205. $tbody['headers'][] = $column->renderHeaderCell();
  206. }
  207. }
  208. if ($rows > 0) {
  209. for ($row = 0; $row < $rows; ++$row) {
  210. $tbody['rows'][] = $this->renderTableRowJSON($row);
  211. }
  212. foreach ($this->dataProvider->getKeys() as $key) {
  213. $tbody['keys'][] = CHtml::encode($key);
  214. }
  215. } else {
  216. ob_start();
  217. $this->renderEmptyText();
  218. $content = ob_get_contents();
  219. ob_end_clean();
  220. $tbody['rows'][0]['cols'][] = array(
  221. 'attrs' => "colspan=\"" . count($this->columns) . "\"",
  222. 'content' => $content
  223. );
  224. $tbody['rows'][0]['class'] = " ";
  225. }
  226. $tbody['pager'] = $this->renderPager();
  227. $tbody['url'] = Yii::app()->getRequest()->getUrl();
  228. return $tbody;
  229. }
  230. /**
  231. * Renders a table body row.
  232. *
  233. * @param integer $row the row number (zero-based).
  234. */
  235. public function renderTableRow($row)
  236. {
  237. if ($this->rowCssClassExpression !== null) {
  238. $data = $this->dataProvider->data[$row];
  239. echo '<tr class="' . $this->evaluateExpression(
  240. $this->rowCssClassExpression,
  241. array('row' => $row, 'data' => $data)
  242. ) . '">';
  243. } else if (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0) {
  244. echo '<tr class="' . $this->rowCssClass[$row % $n] . '">';
  245. } else {
  246. echo '<tr>';
  247. }
  248. foreach ($this->columns as $column) {
  249. $column->renderDataCell($row);
  250. }
  251. echo "</tr>\n";
  252. }
  253. /**
  254. * Renders a table body row for JSON requests - assumed ajax is for JSON
  255. *
  256. * @param integer $row
  257. *
  258. * @return array
  259. */
  260. protected function renderTableRowJSON($row)
  261. {
  262. $json = array();
  263. if ($this->rowCssClassExpression !== null) {
  264. $data = $this->dataProvider->data[$row];
  265. $json['class'] = $this->evaluateExpression(
  266. $this->rowCssClassExpression,
  267. array('row' => $row, 'data' => $data)
  268. );
  269. } else if (is_array($this->rowCssClass) && ($n = count($this->rowCssClass)) > 0) {
  270. $json['class'] = $this->rowCssClass[$row % $n];
  271. } else {
  272. echo '<tr>';
  273. }
  274. foreach ($this->columns as $column) {
  275. $json['cols'][] = $column->renderDataCell($row);
  276. }
  277. return $json;
  278. }
  279. /**
  280. * Creates a column based on a shortcut column specification string.
  281. *
  282. * @param mixed $text the column specification string
  283. *
  284. * @return \TbJSONDataColumn|\TbDataColumn|\CDataColumn the column instance
  285. * @throws CException if the column format is incorrect
  286. */
  287. protected function createDataColumn($text)
  288. {
  289. if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $text, $matches)) {
  290. throw new CException(Yii::t(
  291. 'zii',
  292. 'The column must be specified in the format of "Name:Type:Label", where "Type" and "Label" are optional.'
  293. ));
  294. }
  295. $column = new TbJsonDataColumn($this);
  296. $column->name = $matches[1];
  297. if (isset($matches[3]) && $matches[3] !== '') {
  298. $column->type = $matches[3];
  299. }
  300. if (isset($matches[5])) {
  301. $column->header = $matches[5];
  302. }
  303. return $column;
  304. }
  305. /**
  306. * Registers necessary client scripts.
  307. */
  308. public function registerClientScript()
  309. {
  310. $id = $this->getId();
  311. if ($this->ajaxUpdate === false) {
  312. $ajaxUpdate = false;
  313. } else {
  314. $ajaxUpdate = array_unique(
  315. preg_split('/\s*,\s*/', $this->ajaxUpdate . ',' . $id, -1, PREG_SPLIT_NO_EMPTY)
  316. );
  317. }
  318. $options = array(
  319. 'ajaxUpdate' => $ajaxUpdate,
  320. 'ajaxVar' => $this->ajaxVar,
  321. 'pagerClass' => $this->pagerCssClass,
  322. 'loadingClass' => $this->loadingCssClass,
  323. 'filterClass' => $this->filterCssClass,
  324. 'tableClass' => $this->itemsCssClass,
  325. 'selectableRows' => $this->selectableRows,
  326. 'enableHistory' => $this->enableHistory,
  327. 'updateSelector' => $this->updateSelector,
  328. 'cacheTTL' => $this->cacheTTL,
  329. 'cacheTTLType' => $this->cacheTTLType,
  330. 'localCache' => $this->localCache
  331. );
  332. if ($this->ajaxUrl !== null) {
  333. $options['url'] = CHtml::normalizeUrl($this->ajaxUrl);
  334. }
  335. if ($this->enablePagination) {
  336. $options['pageVar'] = $this->dataProvider->getPagination()->pageVar;
  337. }
  338. foreach (array('beforeAjaxUpdate', 'afterAjaxUpdate', 'ajaxUpdateError', 'selectionChanged') as $prop) {
  339. if ($this->{$prop} !== null) {
  340. if ((!$this->{$prop} instanceof CJavaScriptExpression) && strpos($this->{$prop}, 'js:') !== 0) {
  341. $options[$prop] = new CJavaScriptExpression($this->{$prop});
  342. } else {
  343. $options[$prop] = $this->{$prop};
  344. }
  345. }
  346. }
  347. $options = CJavaScript::encode($options);
  348. /** @var $cs CClientScript */
  349. $cs = Yii::app()->getClientScript();
  350. $cs->registerCoreScript('jquery');
  351. $cs->registerCoreScript('bbq');
  352. if ($this->enableHistory) {
  353. $cs->registerCoreScript('history');
  354. }
  355. $assetsUrl = Yii::app()->bootstrap->getAssetsUrl();
  356. $cs->registerScriptFile(
  357. $assetsUrl . '/js/jquery.jqote2.min.js',
  358. CClientScript::POS_END
  359. ); // jqote2 template engine
  360. $cs->registerScriptFile($assetsUrl . '/js/jquery.ajax.cache.js', CClientScript::POS_END); // ajax cache
  361. $cs->registerScriptFile(
  362. $assetsUrl . '/js/jquery.json.yiigridview.js',
  363. CClientScript::POS_END
  364. ); // custom yiiGridView
  365. $cs->registerScript(__CLASS__ . '#' . $id, "jQuery('#$id').yiiJsonGridView($options);");
  366. }
  367. }