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

/core/ViewDataTable.php

https://github.com/quarkness/piwik
PHP | 1158 lines | 576 code | 118 blank | 464 comment | 45 complexity | 551456ec70aa76f1e497010e828b659b MD5 | raw file
  1. <?php
  2. /**
  3. * Piwik - Open source web analytics
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. * @version $Id$
  8. *
  9. * @category Piwik
  10. * @package Piwik
  11. */
  12. /**
  13. * This class is used to load (from the API) and customize the output of a given DataTable.
  14. * The main() method will create an object implementing Piwik_View_Interface
  15. * You can customize the dataTable using the disable* methods.
  16. *
  17. * Example:
  18. * In the Controller of the plugin VisitorInterest
  19. * <pre>
  20. * function getNumberOfVisitsPerVisitDuration( $fetch = false)
  21. * {
  22. * $view = Piwik_ViewDataTable::factory( 'cloud' );
  23. * $view->init( $this->pluginName, __FUNCTION__, 'VisitorInterest.getNumberOfVisitsPerVisitDuration' );
  24. * $view->setColumnsToDisplay( array('label','nb_visits') );
  25. * $view->disableSort();
  26. * $view->disableExcludeLowPopulation();
  27. * $view->disableOffsetInformation();
  28. *
  29. * return $this->renderView($view, $fetch);
  30. * }
  31. * </pre>
  32. *
  33. * @see factory() for all the available output (cloud tags, html table, pie chart, vertical bar chart)
  34. * @package Piwik
  35. * @subpackage Piwik_ViewDataTable
  36. */
  37. abstract class Piwik_ViewDataTable
  38. {
  39. /**
  40. * Template file that will be loaded for this view.
  41. * Usually set in the Piwik_ViewDataTable_*
  42. *
  43. * @var string eg. 'CoreHome/templates/cloud.tpl'
  44. */
  45. protected $dataTableTemplate = null;
  46. /**
  47. * Flag used to make sure the main() is only executed once
  48. *
  49. * @var bool
  50. */
  51. protected $mainAlreadyExecuted = false;
  52. /**
  53. * Contains the values set for the parameters
  54. *
  55. * @see getJavascriptVariablesToSet()
  56. * @var array
  57. */
  58. protected $variablesDefault = array();
  59. /**
  60. * Array of properties that are available in the view (from smarty)
  61. * Used to store UI properties, eg. "show_footer", "show_search", etc.
  62. *
  63. * @var array
  64. */
  65. protected $viewProperties = array();
  66. /**
  67. * If the current dataTable refers to a subDataTable (eg. keywordsBySearchEngineId for id=X) this variable is set to the Id
  68. *
  69. * @var bool|int
  70. */
  71. protected $idSubtable = false;
  72. /**
  73. * DataTable loaded from the API for this ViewDataTable.
  74. *
  75. * @var Piwik_DataTable
  76. */
  77. protected $dataTable = null;
  78. /**
  79. * List of filters to apply after the data has been loaded from the API
  80. *
  81. * @var array
  82. */
  83. protected $queuedFilters = array();
  84. /**
  85. * List of filter to apply just before the 'Generic' filters
  86. * These filters should delete rows from the table
  87. * @var array
  88. */
  89. protected $queuedFiltersPriority = array();
  90. /**
  91. * @see init()
  92. * @var string
  93. */
  94. protected $currentControllerAction;
  95. /**
  96. * @see init()
  97. * @var string
  98. */
  99. protected $currentControllerName;
  100. /**
  101. * @see init()
  102. * @var string
  103. */
  104. protected $controllerActionCalledWhenRequestSubTable = null;
  105. /**
  106. * @see init()
  107. * @var string
  108. */
  109. protected $apiMethodToRequestDataTable;
  110. /**
  111. * This view should be an implementation of the Interface Piwik_View_Interface
  112. * The $view object should be created in the main() method.
  113. *
  114. * @var Piwik_View_Interface
  115. */
  116. protected $view = null;
  117. /**
  118. * Array of columns names translations
  119. *
  120. * @var array
  121. */
  122. protected $columnsTranslations = array();
  123. /**
  124. * Documentation for the metrics used in the current report.
  125. * Received from the Plugin API, used for inline help.
  126. *
  127. * @var array
  128. */
  129. protected $metricsDocumentation = false;
  130. /**
  131. * Documentation for the report.
  132. * Received from the Plugin API, used for inline help.
  133. *
  134. * @var array
  135. */
  136. protected $documentation = false;
  137. /**
  138. * Array of columns set to display
  139. *
  140. * @var array
  141. */
  142. protected $columnsToDisplay = array();
  143. /**
  144. * Variable that is used as the DIV ID in the rendered HTML
  145. *
  146. * @var string
  147. */
  148. protected $uniqIdTable = null;
  149. /**
  150. * Method to be implemented by the ViewDataTable_*.
  151. * This method should create and initialize a $this->view object @see Piwik_View_Interface
  152. *
  153. * @return mixed either prints the result or returns the output string
  154. */
  155. abstract public function main();
  156. /**
  157. * Unique string ID that defines the format of the dataTable, eg. "pieChart", "table", etc.
  158. *
  159. * @return string
  160. */
  161. abstract protected function getViewDataTableId();
  162. /**
  163. * Returns a Piwik_ViewDataTable_* object.
  164. * By default it will return a ViewDataTable_Html
  165. * If there is a viewDataTable parameter in the URL, a ViewDataTable of this 'viewDataTable' type will be returned.
  166. * If defaultType is specified and if there is no 'viewDataTable' in the URL, a ViewDataTable of this $defaultType will be returned.
  167. * If force is set to true, a ViewDataTable of the $defaultType will be returned in all cases.
  168. *
  169. * @param string defaultType Any of these: table, cloud, graphPie, graphVerticalBar, graphEvolution, sparkline, generateDataChart*
  170. * @param bool force If set to true, returns a ViewDataTable of the $defaultType
  171. * @return Piwik_ViewDataTable
  172. */
  173. static public function factory( $defaultType = null, $force = false)
  174. {
  175. if(is_null($defaultType))
  176. {
  177. $defaultType = 'table';
  178. }
  179. if($force === true)
  180. {
  181. $type = $defaultType;
  182. }
  183. else
  184. {
  185. $type = Piwik_Common::getRequestVar('viewDataTable', $defaultType, 'string');
  186. }
  187. switch($type)
  188. {
  189. case 'cloud':
  190. return new Piwik_ViewDataTable_Cloud();
  191. break;
  192. case 'graphPie':
  193. return new Piwik_ViewDataTable_GenerateGraphHTML_ChartPie();
  194. break;
  195. case 'graphVerticalBar':
  196. return new Piwik_ViewDataTable_GenerateGraphHTML_ChartVerticalBar();
  197. break;
  198. case 'graphEvolution':
  199. return new Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution();
  200. break;
  201. case 'sparkline':
  202. return new Piwik_ViewDataTable_Sparkline();
  203. break;
  204. case 'generateDataChartVerticalBar':
  205. return new Piwik_ViewDataTable_GenerateGraphData_ChartVerticalBar();
  206. break;
  207. case 'generateDataChartPie':
  208. return new Piwik_ViewDataTable_GenerateGraphData_ChartPie();
  209. break;
  210. case 'generateDataChartEvolution':
  211. return new Piwik_ViewDataTable_GenerateGraphData_ChartEvolution();
  212. break;
  213. case 'tableAllColumns':
  214. return new Piwik_ViewDataTable_HtmlTable_AllColumns();
  215. break;
  216. case 'tableGoals':
  217. return new Piwik_ViewDataTable_HtmlTable_Goals();
  218. break;
  219. case 'table':
  220. default:
  221. return new Piwik_ViewDataTable_HtmlTable();
  222. break;
  223. }
  224. }
  225. /**
  226. * Inits the object given the $currentControllerName, $currentControllerAction of
  227. * the calling controller action, eg. 'Referers' 'getLongListOfKeywords'.
  228. * The initialization also requires the $apiMethodToRequestDataTable of the API method
  229. * to call in order to get the DataTable, eg. 'Referers.getKeywords'.
  230. * The optional $controllerActionCalledWhenRequestSubTable defines the method name of the API to call when there is a idSubtable.
  231. * This value would be used by the javascript code building the GET request to the API.
  232. *
  233. * Example:
  234. * For the keywords listing, a click on the row loads the subTable of the Search Engines for this row.
  235. * In this case $controllerActionCalledWhenRequestSubTable = 'getSearchEnginesFromKeywordId'.
  236. * The GET request will hit 'Referers.getSearchEnginesFromKeywordId'.
  237. *
  238. * @param string $currentControllerName eg. 'Referers'
  239. * @param string $currentControllerAction eg. 'getKeywords'
  240. * @param string $apiMethodToRequestDataTable eg. 'Referers.getKeywords'
  241. * @param string $controllerActionCalledWhenRequestSubTable eg. 'getSearchEnginesFromKeywordId'
  242. */
  243. public function init( $currentControllerName,
  244. $currentControllerAction,
  245. $apiMethodToRequestDataTable,
  246. $controllerActionCalledWhenRequestSubTable = null)
  247. {
  248. $this->currentControllerName = $currentControllerName;
  249. $this->currentControllerAction = $currentControllerAction;
  250. $this->apiMethodToRequestDataTable = $apiMethodToRequestDataTable;
  251. $this->controllerActionCalledWhenRequestSubTable = $controllerActionCalledWhenRequestSubTable;
  252. $this->idSubtable = Piwik_Common::getRequestVar('idSubtable', false, 'int');
  253. $this->viewProperties['show_goals'] = false;
  254. $this->viewProperties['show_ecommerce'] = false;
  255. $this->viewProperties['show_search'] = Piwik_Common::getRequestVar('show_search', true);
  256. $this->viewProperties['show_table'] = Piwik_Common::getRequestVar('show_table', true);
  257. $this->viewProperties['show_table_all_columns'] = Piwik_Common::getRequestVar('show_table_all_columns', true);
  258. $this->viewProperties['show_all_views_icons'] = Piwik_Common::getRequestVar('show_all_views_icons', true);
  259. $this->viewProperties['show_bar_chart'] = Piwik_Common::getRequestVar('show_barchart', true);
  260. $this->viewProperties['show_pie_chart'] = Piwik_Common::getRequestVar('show_piechart', true);
  261. $this->viewProperties['show_tag_cloud'] = Piwik_Common::getRequestVar('show_tag_cloud', true);
  262. $this->viewProperties['show_export_as_image_icon'] = Piwik_Common::getRequestVar('show_export_as_image_icon', false);
  263. $this->viewProperties['show_export_as_rss_feed'] = Piwik_Common::getRequestVar('show_export_as_rss_feed', true);
  264. $this->viewProperties['show_exclude_low_population'] = Piwik_Common::getRequestVar('show_exclude_low_population', true);
  265. $this->viewProperties['show_offset_information'] = Piwik_Common::getRequestVar('show_offset_information', true);
  266. $this->viewProperties['show_pagination_control'] = Piwik_Common::getRequestVar('show_pagination_control', true);
  267. $this->viewProperties['show_footer'] = Piwik_Common::getRequestVar('show_footer', true);
  268. $this->viewProperties['show_footer_icons'] = ($this->idSubtable == false);
  269. $this->viewProperties['apiMethodToRequestDataTable'] = $this->apiMethodToRequestDataTable;
  270. $this->viewProperties['uniqueId'] = $this->getUniqueIdViewDataTable();
  271. $this->viewProperties['exportLimit'] = Zend_Registry::get('config')->General->API_datatable_default_limit;
  272. $standardColumnNameToTranslation = array_merge(
  273. Piwik_API_API::getInstance()->getDefaultMetrics(),
  274. Piwik_API_API::getInstance()->getDefaultProcessedMetrics()
  275. );
  276. $this->setColumnsTranslations($standardColumnNameToTranslation);
  277. }
  278. /**
  279. * Forces the View to use a given template.
  280. * Usually the template to use is set in the specific ViewDataTable_*
  281. * eg. 'CoreHome/templates/cloud.tpl'
  282. * But some users may want to force this template to some other value
  283. *
  284. * @param string $tpl eg .'MyPlugin/templates/templateToUse.tpl'
  285. */
  286. public function setTemplate( $tpl )
  287. {
  288. $this->dataTableTemplate = $tpl;
  289. }
  290. /**
  291. * Returns the View_Interface.
  292. * You can then call render() on this object.
  293. *
  294. * @return Piwik_View_Interface
  295. * @throws exception if the view object was not created
  296. */
  297. public function getView()
  298. {
  299. if(is_null($this->view))
  300. {
  301. throw new Exception('The $this->view object has not been created.
  302. It should be created in the main() method of the Piwik_ViewDataTable_* subclass you are using.');
  303. }
  304. return $this->view;
  305. }
  306. public function getCurrentControllerAction()
  307. {
  308. return $this->currentControllerAction;
  309. }
  310. public function getCurrentControllerName()
  311. {
  312. return $this->currentControllerName;
  313. }
  314. public function getApiMethodToRequestDataTable()
  315. {
  316. return $this->apiMethodToRequestDataTable;
  317. }
  318. public function getControllerActionCalledWhenRequestSubTable()
  319. {
  320. return $this->controllerActionCalledWhenRequestSubTable;
  321. }
  322. /**
  323. * Returns the DataTable loaded from the API
  324. *
  325. * @return Piwik_DataTable
  326. * @throws exception if not yet defined
  327. */
  328. public function getDataTable()
  329. {
  330. if(is_null($this->dataTable))
  331. {
  332. throw new Exception("The DataTable object has not yet been created");
  333. }
  334. return $this->dataTable;
  335. }
  336. /**
  337. * Function called by the ViewDataTable objects in order to fetch data from the API.
  338. * The function init() must have been called before, so that the object knows which API module and action to call.
  339. * It builds the API request string and uses Piwik_API_Request to call the API.
  340. * The requested Piwik_DataTable object is stored in $this->dataTable.
  341. */
  342. protected function loadDataTableFromAPI()
  343. {
  344. // we build the request string (URL) to call the API
  345. $requestString = $this->getRequestString();
  346. // we make the request to the API
  347. $request = new Piwik_API_Request($requestString);
  348. // and get the DataTable structure
  349. $dataTable = $request->process();
  350. $this->dataTable = $dataTable;
  351. }
  352. /**
  353. * Checks that the API returned a normal DataTable (as opposed to DataTable_Array)
  354. * @throws Exception
  355. * @return void
  356. */
  357. protected function checkStandardDataTable()
  358. {
  359. if(!($this->dataTable instanceof Piwik_DataTable))
  360. {
  361. throw new Exception("Unexpected data type to render.");
  362. }
  363. }
  364. /**
  365. * Hook called after the dataTable has been loaded from the API
  366. * Can be used to add, delete or modify the data freshly loaded
  367. */
  368. protected function postDataTableLoadedFromAPI()
  369. {
  370. if(empty($this->dataTable))
  371. {
  372. return false;
  373. }
  374. // First, filters that delete rows
  375. foreach($this->queuedFiltersPriority as $filter)
  376. {
  377. $filterName = $filter[0];
  378. $filterParameters = $filter[1];
  379. $this->dataTable->filter($filterName, $filterParameters);
  380. }
  381. if(0 == Piwik_Common::getRequestVar('disable_generic_filters', '0', 'string'))
  382. {
  383. // Second, generic filters (Sort, Limit, Replace Column Names, etc.)
  384. $requestString = $this->getRequestString();
  385. $request = Piwik_API_Request::getRequestArrayFromString($requestString);
  386. if(!empty($this->variablesDefault['enable_sort'])
  387. && $this->variablesDefault['enable_sort'] === 'false')
  388. {
  389. $request['filter_sort_column'] = $request['filter_sort_order'] = '';
  390. }
  391. $genericFilter = new Piwik_API_DataTableGenericFilter($request);
  392. $genericFilter->filter($this->dataTable);
  393. }
  394. // Finally, apply datatable filters that were queued (should be 'presentation' filters that do not affect the number of rows)
  395. foreach($this->queuedFilters as $filter)
  396. {
  397. $filterName = $filter[0];
  398. $filterParameters = $filter[1];
  399. $this->dataTable->filter($filterName, $filterParameters);
  400. }
  401. }
  402. /**
  403. * @return string URL to call the API, eg. "method=Referers.getKeywords&period=day&date=yesterday"...
  404. */
  405. protected function getRequestString()
  406. {
  407. // we prepare the string to give to the API Request
  408. // we setup the method and format variable
  409. // - we request the method to call to get this specific DataTable
  410. // - the format = original specifies that we want to get the original DataTable structure itself, not rendered
  411. $requestString = 'method='.$this->apiMethodToRequestDataTable;
  412. $requestString .= '&format=original';
  413. $requestString .= '&disable_generic_filters='.Piwik_Common::getRequestVar('disable_generic_filters', 1, 'int');
  414. $toSetEventually = array(
  415. 'filter_limit',
  416. 'filter_sort_column',
  417. 'filter_sort_order',
  418. 'filter_excludelowpop',
  419. 'filter_excludelowpop_value',
  420. 'filter_column',
  421. 'filter_pattern',
  422. 'disable_queued_filters',
  423. );
  424. foreach($toSetEventually as $varToSet)
  425. {
  426. $value = $this->getDefaultOrCurrent($varToSet);
  427. if( false !== $value )
  428. {
  429. if( is_array($value) )
  430. {
  431. foreach($value as $v)
  432. {
  433. $requestString .= "&".$varToSet.'[]='.$v;
  434. }
  435. }
  436. else
  437. {
  438. $requestString .= '&'.$varToSet.'='.$value;
  439. }
  440. }
  441. }
  442. return $requestString;
  443. }
  444. /**
  445. * For convenience, the client code can call methods that are defined in a specific children class
  446. * without testing the children class type, which would trigger an error with a different children class.
  447. *
  448. * Example:
  449. * ViewDataTable/Html.php defines a setColumnsToDisplay(). The client code calls this methods even if
  450. * the ViewDataTable object is a ViewDataTable_Cloud instance (he doesn't know because of the factory()).
  451. * But ViewDataTable_Cloud doesn't define the setColumnsToDisplay() method.
  452. * Because we don't want to force users to test for the object type we simply catch these
  453. * calls when they are not defined in the child and do nothing.
  454. *
  455. * @param string $function
  456. * @param array $args
  457. */
  458. public function __call($function, $args)
  459. {
  460. }
  461. /**
  462. * Returns a unique ID for this ViewDataTable.
  463. * This unique ID is used in the Javascript code:
  464. * Any ajax loaded data is loaded within a DIV that has id=$unique_id
  465. * The jquery code then replaces the existing html div id=$unique_id in the code with this data.
  466. *
  467. * @see datatable.js
  468. * @return string
  469. */
  470. protected function loadUniqueIdViewDataTable()
  471. {
  472. // if we request a subDataTable the $this->currentControllerAction DIV ID is already there in the page
  473. // we make the DIV ID really unique by appending the ID of the subtable requested
  474. if( $this->idSubtable != 0 // parent DIV has a idSubtable = 0 but the html DIV must have the name of the module.action
  475. && $this->idSubtable !== false // case there is no idSubtable
  476. )
  477. {
  478. // see also datatable.js (the ID has to match with the html ID created to be replaced by the result of the ajax call)
  479. $uniqIdTable = 'subDataTable_' . $this->idSubtable;
  480. }
  481. else
  482. {
  483. // the $uniqIdTable variable is used as the DIV ID in the rendered HTML
  484. // we use the current Controller action name as it is supposed to be unique in the rendered page
  485. $uniqIdTable = $this->currentControllerName . $this->currentControllerAction;
  486. }
  487. return $uniqIdTable;
  488. }
  489. /**
  490. * Sets the $uniqIdTable variable that is used as the DIV ID in the rendered HTML
  491. */
  492. public function setUniqueIdViewDataTable($uniqIdTable)
  493. {
  494. $this->viewProperties['uniqueId'] = $uniqIdTable;
  495. $this->uniqIdTable = $uniqIdTable;
  496. }
  497. /**
  498. * Returns current value of $uniqIdTable variable that is used as the DIV ID in the rendered HTML
  499. */
  500. public function getUniqueIdViewDataTable()
  501. {
  502. if( $this->uniqIdTable == null )
  503. {
  504. $this->uniqIdTable = $this->loadUniqueIdViewDataTable();
  505. }
  506. return $this->uniqIdTable;
  507. }
  508. /**
  509. * Returns array of properties, eg. "show_footer", "show_search", etc.
  510. *
  511. * @return array of boolean
  512. */
  513. protected function getViewProperties()
  514. {
  515. return $this->viewProperties;
  516. }
  517. /**
  518. * This functions reads the customization values for the DataTable and returns an array (name,value) to be printed in Javascript.
  519. * This array defines things such as:
  520. * - name of the module & action to call to request data for this table
  521. * - optional filters information, eg. filter_limit and filter_offset
  522. * - etc.
  523. *
  524. * The values are loaded:
  525. * - from the generic filters that are applied by default @see Piwik_API_DataTableGenericFilter.php::getGenericFiltersInformation()
  526. * - from the values already available in the GET array
  527. * - from the values set using methods from this class (eg. setSearchPattern(), setLimit(), etc.)
  528. *
  529. * @return array eg. array('show_offset_information' => 0, 'show_...
  530. */
  531. protected function getJavascriptVariablesToSet()
  532. {
  533. // build javascript variables to set
  534. $javascriptVariablesToSet = array();
  535. $genericFilters = Piwik_API_DataTableGenericFilter::getGenericFiltersInformation();
  536. foreach($genericFilters as $filter)
  537. {
  538. foreach($filter as $filterVariableName => $filterInfo)
  539. {
  540. // if there is a default value for this filter variable we set it
  541. // so that it is propagated to the javascript
  542. if(isset($filterInfo[1]))
  543. {
  544. $javascriptVariablesToSet[$filterVariableName] = $filterInfo[1];
  545. // we set the default specified column and Order to sort by
  546. // when this javascript variable is not set already
  547. // for example during an AJAX call this variable will be set in the URL
  548. // so this will not be executed (and the default sorted not be used as the sorted column might have changed in the meanwhile)
  549. if( false !== ($defaultValue = $this->getDefault($filterVariableName)))
  550. {
  551. $javascriptVariablesToSet[$filterVariableName] = $defaultValue;
  552. }
  553. }
  554. }
  555. }
  556. foreach($_GET as $name => $value)
  557. {
  558. try {
  559. $requestValue = Piwik_Common::getRequestVar($name);
  560. }
  561. catch(Exception $e) {
  562. $requestValue = '';
  563. }
  564. $javascriptVariablesToSet[$name] = $requestValue;
  565. }
  566. // at this point there are some filters values we may have not set,
  567. // case of the filter without default values and parameters set directly in this class
  568. // for example setExcludeLowPopulation
  569. // we go through all the $this->variablesDefault array and set the variables not set yet
  570. foreach($this->variablesDefault as $name => $value)
  571. {
  572. if(!isset($javascriptVariablesToSet[$name] ))
  573. {
  574. $javascriptVariablesToSet[$name] = $value;
  575. }
  576. }
  577. if($this->dataTable instanceof Piwik_DataTable)
  578. {
  579. // we override the filter_sort_column with the column used for sorting,
  580. // which can be different from the one specified (eg. if the column doesn't exist)
  581. $javascriptVariablesToSet['filter_sort_column'] = $this->dataTable->getSortedByColumnName();
  582. // datatable can return "2" but we want to write "nb_visits" in the js
  583. if(isset(Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']]))
  584. {
  585. $javascriptVariablesToSet['filter_sort_column'] = Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']];
  586. }
  587. }
  588. $javascriptVariablesToSet['module'] = $this->currentControllerName;
  589. $javascriptVariablesToSet['action'] = $this->currentControllerAction;
  590. $javascriptVariablesToSet['viewDataTable'] = $this->getViewDataTableId();
  591. $javascriptVariablesToSet['controllerActionCalledWhenRequestSubTable'] = $this->controllerActionCalledWhenRequestSubTable;
  592. if($this->dataTable)
  593. {
  594. $javascriptVariablesToSet['totalRows'] = $this->dataTable->getRowsCountBeforeLimitFilter();
  595. }
  596. // we escape the values that will be displayed in the javascript footer of each datatable
  597. // to make sure there is malicious code injected (the value are already htmlspecialchar'ed as they
  598. // are loaded with Piwik_Common::getRequestVar()
  599. foreach($javascriptVariablesToSet as &$value)
  600. {
  601. if(is_array($value))
  602. {
  603. $value = array_map('addslashes',$value);
  604. }
  605. else
  606. {
  607. $value = addslashes($value);
  608. }
  609. }
  610. $deleteFromJavascriptVariables = array(
  611. 'filter_excludelowpop',
  612. 'filter_excludelowpop_value',
  613. );
  614. foreach($deleteFromJavascriptVariables as $name)
  615. {
  616. if(isset($javascriptVariablesToSet[$name]))
  617. {
  618. unset($javascriptVariablesToSet[$name]);
  619. }
  620. }
  621. return $javascriptVariablesToSet;
  622. }
  623. /**
  624. * Returns, for a given parameter, the value of this parameter in the REQUEST array.
  625. * If not set, returns the default value for this parameter @see getDefault()
  626. *
  627. * @param string $nameVar
  628. * @return string|mixed Value of this parameter
  629. */
  630. protected function getDefaultOrCurrent( $nameVar )
  631. {
  632. if(isset($_GET[$nameVar]))
  633. {
  634. return Piwik_Common::sanitizeInputValue($_GET[$nameVar]);
  635. }
  636. $default = $this->getDefault($nameVar);
  637. return $default;
  638. }
  639. /**
  640. * Returns the default value for a given parameter.
  641. * For example, these default values can be set using the disable* methods.
  642. *
  643. * @param string $nameVar
  644. * @return mixed
  645. */
  646. protected function getDefault($nameVar)
  647. {
  648. if(!isset($this->variablesDefault[$nameVar]))
  649. {
  650. return false;
  651. }
  652. return $this->variablesDefault[$nameVar];
  653. }
  654. /**
  655. * The generic filters (limit, offset, sort by visit desc) will not be applied to this datatable.
  656. */
  657. public function disableGenericFilters()
  658. {
  659. $this->variablesDefault['disable_generic_filters'] = true;
  660. }
  661. /**
  662. * The queued filters (replace column names, enhance column with percentage signs, add logo metadata information, etc.)
  663. * will not be applied to this datatable. They can be manually applied by calling applyQueuedFilters on the datatable.
  664. */
  665. public function disableQueuedFilters()
  666. {
  667. $this->variablesDefault['disable_queued_filters'] = true;
  668. }
  669. /**
  670. * The "X-Y of Z" and the "< Previous / Next >"-Buttons won't be displayed under this table
  671. */
  672. public function disableOffsetInformationAndPaginationControls()
  673. {
  674. $this->viewProperties['show_offset_information'] = false;
  675. $this->viewProperties['show_pagination_control'] = false;
  676. }
  677. /**
  678. * The "< Previous / Next >"-Buttons won't be displayed under this table
  679. */
  680. public function disableShowPaginationControl()
  681. {
  682. $this->viewProperties['show_pagination_control'] = false;
  683. }
  684. /**
  685. * The "X-Y of Z" won't be displayed under this table
  686. */
  687. public function disableOffsetInformation()
  688. {
  689. $this->viewProperties['show_offset_information'] = false;
  690. }
  691. /**
  692. * The search box won't be displayed under this table
  693. */
  694. public function disableSearchBox()
  695. {
  696. $this->viewProperties['show_search'] = false;
  697. }
  698. /**
  699. * Do not sort this table, leave it as it comes out of the API
  700. */
  701. public function disableSort()
  702. {
  703. $this->variablesDefault['enable_sort'] = 'false';
  704. }
  705. /**
  706. * Do not show the footer icons (show all columns icon, "plus" icon)
  707. */
  708. public function disableFooterIcons()
  709. {
  710. $this->viewProperties['show_footer_icons'] = false;
  711. }
  712. /**
  713. * When this method is called, the output will not contain the template datatable_footer.tpl
  714. */
  715. public function disableFooter()
  716. {
  717. $this->viewProperties['show_footer'] = false;
  718. }
  719. /**
  720. * The "Include low population" link won't be displayed under this table
  721. */
  722. public function disableExcludeLowPopulation()
  723. {
  724. $this->viewProperties['show_exclude_low_population'] = false;
  725. }
  726. /**
  727. * Whether or not to show the "View table" icon
  728. */
  729. public function disableShowTable()
  730. {
  731. $this->viewProperties['show_table'] = false;
  732. }
  733. /**
  734. * Whether or not to show the "View more data" icon
  735. */
  736. public function disableShowAllColumns()
  737. {
  738. $this->viewProperties['show_table_all_columns'] = false;
  739. }
  740. /**
  741. * Whether or not to show the tag cloud, pie charts, bar chart icons
  742. */
  743. public function disableShowAllViewsIcons()
  744. {
  745. $this->viewProperties['show_all_views_icons'] = false;
  746. }
  747. /**
  748. * Whether or not to show the bar chart icon.
  749. */
  750. public function disableShowBarChart()
  751. {
  752. $this->viewProperties['show_bar_chart'] = false;
  753. }
  754. /**
  755. * Whether or not to show the pie chart icon.
  756. */
  757. public function disableShowPieChart()
  758. {
  759. $this->viewProperties['show_pie_chart'] = false;
  760. }
  761. /**
  762. * Whether or not to show the tag cloud icon.
  763. */
  764. public function disableTagCloud()
  765. {
  766. $this->viewProperties['show_tag_cloud'] = false;
  767. }
  768. /**
  769. * Whether or not to show the export to RSS feed icon
  770. */
  771. public function disableShowExportAsRssFeed()
  772. {
  773. $this->viewProperties['show_export_as_rss_feed'] = false;
  774. }
  775. /**
  776. * Whether or not to show the "goal" icon
  777. */
  778. public function enableShowGoals()
  779. {
  780. if(Piwik_PluginsManager::getInstance()->isPluginActivated('Goals'))
  781. {
  782. $this->viewProperties['show_goals'] = true;
  783. }
  784. }
  785. /**
  786. * Whether or not to show the "Ecommerce orders/cart" icons
  787. */
  788. public function enableShowEcommerce()
  789. {
  790. $this->viewProperties['show_ecommerce'] = true;
  791. }
  792. /**
  793. * Sets the value to use for the Exclude low population filter.
  794. *
  795. * @param int|float If a row value is less than this value, it will be removed from the dataTable
  796. * @param string The name of the column for which we compare the value to $minValue
  797. */
  798. public function setExcludeLowPopulation( $columnName = null, $minValue = null )
  799. {
  800. if(is_null($columnName))
  801. {
  802. $columnName = 'nb_visits';
  803. }
  804. $this->variablesDefault['filter_excludelowpop'] = $columnName;
  805. $this->variablesDefault['filter_excludelowpop_value'] = $minValue;
  806. }
  807. /**
  808. * Sets the pattern to look for in the table (only rows matching the pattern will be kept)
  809. *
  810. * @param string $pattern to look for
  811. * @param string $column to compare the pattern to
  812. */
  813. public function setSearchPattern($pattern, $column)
  814. {
  815. $this->variablesDefault['filter_pattern'] = $pattern;
  816. $this->variablesDefault['filter_column'] = $column;
  817. }
  818. /**
  819. * Sets the maximum number of rows of the table
  820. *
  821. * @param int $limit
  822. */
  823. public function setLimit( $limit )
  824. {
  825. if($limit !== 0)
  826. {
  827. $this->variablesDefault['filter_limit'] = $limit;
  828. }
  829. }
  830. /**
  831. * Will display a message in the DataTable footer.
  832. *
  833. * @param string $message Message
  834. */
  835. public function setFooterMessage( $message )
  836. {
  837. $this->viewProperties['show_footer_message'] = $message;
  838. }
  839. /**
  840. * Sets the dataTable column to sort by. This sorting will be applied before applying the (offset, limit) filter.
  841. *
  842. * @param int|string $columnId eg. 'nb_visits' for some tables, or Piwik_Archive::INDEX_NB_VISITS for others
  843. * @param string $order desc or asc
  844. */
  845. public function setSortedColumn( $columnId, $order = 'desc')
  846. {
  847. // debug_print_backtrace();
  848. $this->variablesDefault['filter_sort_column'] = $columnId;
  849. $this->variablesDefault['filter_sort_order'] = $order;
  850. }
  851. /**
  852. * Returns the column name on which the table will be sorted
  853. *
  854. * @return string
  855. */
  856. public function getSortedColumn()
  857. {
  858. return isset($this->variablesDefault['filter_sort_column']) ? $this->variablesDefault['filter_sort_column'] : false;
  859. }
  860. /**
  861. * Sets translation string for given column
  862. *
  863. * @param string $columnName column name
  864. * @param string $columnTranslation column name translation
  865. */
  866. public function setColumnTranslation( $columnName, $columnTranslation )
  867. {
  868. if(empty($columnTranslation))
  869. {
  870. throw new Exception('Unknown column: '.$columnName);
  871. }
  872. $this->columnsTranslations[$columnName] = $columnTranslation;
  873. }
  874. /**
  875. * Returns column translation if available, in other case given column name
  876. *
  877. * @param string $columnName column name
  878. */
  879. public function getColumnTranslation( $columnName )
  880. {
  881. if( isset($this->columnsTranslations[$columnName]) )
  882. {
  883. return $this->columnsTranslations[$columnName];
  884. }
  885. return $columnName;
  886. }
  887. /**
  888. * Set the documentation of a metric used in the report.
  889. * Please note, that the default way of doing this is by using
  890. * getReportMetadata. Only use this method, if you have a good
  891. * reason to do so.
  892. *
  893. * @param string $metricIdentifier The idenentifier string of
  894. * the metric
  895. * @param string $documentation The metric documentation as a
  896. * translated string
  897. */
  898. public function setMetricDocumentation($metricIdentifier, $documentation) {
  899. $this->metricsDocumentation[$metricIdentifier] = $documentation;
  900. }
  901. /**
  902. * Returns metric documentation, or false
  903. * @param string $columnName column name
  904. */
  905. public function getMetricDocumentation($columnName)
  906. {
  907. if ($this->metricsDocumentation === false)
  908. {
  909. $this->loadDocumentation();
  910. }
  911. if (!empty($this->metricsDocumentation[$columnName]))
  912. {
  913. return $this->metricsDocumentation[$columnName];
  914. }
  915. return false;
  916. }
  917. /**
  918. * Set the documentation of the report.
  919. * Please note, that the default way of doing this is by using
  920. * getReportMetadata. Only use this method, if you have a good
  921. * reason to do so.
  922. *
  923. * @param string $documentation The report documentation as a
  924. * translated string
  925. */
  926. public function setReportDocumentation($documentation) {
  927. $this->documentation = $documentation;
  928. }
  929. /** Returns report documentation, or false */
  930. public function getReportDocumentation()
  931. {
  932. if ($this->metricsDocumentation === false)
  933. {
  934. $this->loadDocumentation();
  935. }
  936. return $this->documentation;
  937. }
  938. /** Load documentation from the API */
  939. private function loadDocumentation()
  940. {
  941. $this->metricsDocumentation = array();
  942. $report = Piwik_API_API::getInstance()->getMetadata(0, $this->currentControllerName, $this->currentControllerAction);
  943. $report = $report[0];
  944. if (isset($report['metricsDocumentation']))
  945. {
  946. $this->metricsDocumentation = $report['metricsDocumentation'];
  947. }
  948. if (isset($report['documentation']))
  949. {
  950. $this->documentation = $report['documentation'];
  951. }
  952. }
  953. /**
  954. * Sets the columns that will be displayed in the HTML output
  955. * By default all columns are displayed ($columnsNames = array() will display all columns)
  956. *
  957. * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
  958. */
  959. public function setColumnsToDisplay( $columnsNames )
  960. {
  961. if(!is_array($columnsNames))
  962. {
  963. if (strpos($columnsNames, ',') !== false)
  964. {
  965. // array values are comma separated
  966. $columnsNames = explode(',', $columnsNames);
  967. }
  968. else
  969. {
  970. $columnsNames = array($columnsNames);
  971. }
  972. }
  973. $this->columnsToDisplay = $columnsNames;
  974. }
  975. /**
  976. * Returns columns names to display, in order.
  977. * If no columns were specified to be displayed, return all columns found in the first row.
  978. * @param array PHP array conversion of the data table
  979. * @return array
  980. */
  981. public function getColumnsToDisplay()
  982. {
  983. if(empty($this->columnsToDisplay))
  984. {
  985. $row = $this->dataTable->getFirstRow();
  986. if(empty($row))
  987. {
  988. return array();
  989. }
  990. return array_keys($row->getColumns());
  991. }
  992. $this->columnsToDisplay = array_filter($this->columnsToDisplay);
  993. return $this->columnsToDisplay;
  994. }
  995. /**
  996. * Sets columns translations array.
  997. *
  998. * @param array $columnsTranslations An associative array indexed by column names, eg. array('nb_visit'=>"Numer of visits")
  999. */
  1000. public function setColumnsTranslations( $columnsTranslations )
  1001. {
  1002. $this->columnsTranslations += $columnsTranslations;
  1003. }
  1004. /**
  1005. * Sets a custom parameter, that will be printed in the javascript array associated with each datatable
  1006. *
  1007. * @param string parameter name
  1008. * @param mixed $value
  1009. */
  1010. public function setCustomParameter($parameter, $value)
  1011. {
  1012. if(isset($this->variablesDefault[$parameter]))
  1013. {
  1014. throw new Exception("$parameter is already defined for this DataTable.");
  1015. }
  1016. $this->variablesDefault[$parameter] = $value;
  1017. }
  1018. /**
  1019. * Queues a Datatable filter, that will be applied once the datatable is loaded from the API.
  1020. * Useful when the controller needs to add columns, or decorate existing columns, when these filters don't
  1021. * necessarily make sense directly in the API.
  1022. *
  1023. * @param string $filterName
  1024. * @param mixed $parameters
  1025. * @param bool $runBeforeGenericFilters Set to true if the filter will delete rows from the table,
  1026. * and should therefore be ran before Sort, Limit, etc.
  1027. * @return void
  1028. */
  1029. public function queueFilter($filterName, $parameters, $runBeforeGenericFilters = false)
  1030. {
  1031. if($runBeforeGenericFilters)
  1032. {
  1033. $this->queuedFiltersPriority[] = array($filterName, $parameters);
  1034. }
  1035. else
  1036. {
  1037. $this->queuedFilters[] = array($filterName, $parameters);
  1038. }
  1039. }
  1040. }