Teknik is a suite of services with attractive and functional interfaces. https://www.teknik.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Config.php 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. <?php
  2. /**
  3. * Piwik - free/libre analytics platform
  4. *
  5. * @link http://piwik.org
  6. * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  7. *
  8. */
  9. namespace Piwik\ViewDataTable;
  10. use Piwik\API\Request as ApiRequest;
  11. use Piwik\DataTable;
  12. use Piwik\DataTable\Filter\PivotByDimension;
  13. use Piwik\Metrics;
  14. use Piwik\Plugin\Report;
  15. use Piwik\Plugins\API\API;
  16. /**
  17. * Contains base display properties for {@link Piwik\Plugin\ViewDataTable}s. Manipulating these
  18. * properties in a ViewDataTable instance will change how its report will be displayed.
  19. *
  20. * <a name="client-side-properties-desc"></a>
  21. * **Client Side Properties**
  22. *
  23. * Client side properties are properties that should be passed on to the browser so
  24. * client side JavaScript can use them. Only affects ViewDataTables that output HTML.
  25. *
  26. * <a name="overridable-properties-desc"></a>
  27. * **Overridable Properties**
  28. *
  29. * Overridable properties are properties that can be set via the query string.
  30. * If a request has a query parameter that matches an overridable property, the property
  31. * will be set to the query parameter value.
  32. *
  33. * **Reusing base properties**
  34. *
  35. * Many of the properties in this class only have meaning for the {@link Piwik\Plugin\Visualization}
  36. * class, but can be set for other visualizations that extend {@link Piwik\Plugin\ViewDataTable}
  37. * directly.
  38. *
  39. * Visualizations that extend {@link Piwik\Plugin\ViewDataTable} directly and want to re-use these
  40. * properties must make sure the properties are used in the exact same way they are used in
  41. * {@link Piwik\Plugin\Visualization}.
  42. *
  43. * **Defining new display properties**
  44. *
  45. * If you are creating your own visualization and want to add new display properties for
  46. * it, extend this class and add your properties as fields.
  47. *
  48. * Properties are marked as client side properties by calling the
  49. * {@link addPropertiesThatShouldBeAvailableClientSide()} method.
  50. *
  51. * Properties are marked as overridable by calling the
  52. * {@link addPropertiesThatCanBeOverwrittenByQueryParams()} method.
  53. *
  54. * ### Example
  55. *
  56. * **Defining new display properties**
  57. *
  58. * class MyCustomVizConfig extends Config
  59. * {
  60. * /**
  61. * * My custom property. It is overridable.
  62. * *\/
  63. * public $my_custom_property = false;
  64. *
  65. * /**
  66. * * Another custom property. It is available client side.
  67. * *\/
  68. * public $another_custom_property = true;
  69. *
  70. * public function __construct()
  71. * {
  72. * parent::__construct();
  73. *
  74. * $this->addPropertiesThatShouldBeAvailableClientSide(array('another_custom_property'));
  75. * $this->addPropertiesThatCanBeOverwrittenByQueryParams(array('my_custom_property'));
  76. * }
  77. * }
  78. *
  79. * @api
  80. */
  81. class Config
  82. {
  83. /**
  84. * The list of ViewDataTable properties that are 'Client Side Properties'.
  85. */
  86. public $clientSideProperties = array(
  87. 'show_limit_control',
  88. 'pivot_by_dimension',
  89. 'pivot_by_column',
  90. 'pivot_dimension_name'
  91. );
  92. /**
  93. * The list of ViewDataTable properties that can be overriden by query parameters.
  94. */
  95. public $overridableProperties = array(
  96. 'show_goals',
  97. 'show_exclude_low_population',
  98. 'show_flatten_table',
  99. 'show_pivot_by_subtable',
  100. 'show_table',
  101. 'show_table_all_columns',
  102. 'show_footer',
  103. 'show_footer_icons',
  104. 'show_all_views_icons',
  105. 'show_active_view_icon',
  106. 'show_related_reports',
  107. 'show_limit_control',
  108. 'show_search',
  109. 'enable_sort',
  110. 'show_bar_chart',
  111. 'show_pie_chart',
  112. 'show_tag_cloud',
  113. 'show_export_as_rss_feed',
  114. 'show_ecommerce',
  115. 'search_recursive',
  116. 'show_export_as_image_icon',
  117. 'show_pagination_control',
  118. 'show_offset_information',
  119. 'hide_annotations_view',
  120. 'export_limit'
  121. );
  122. /**
  123. * Controls what footer icons are displayed on the bottom left of the DataTable view.
  124. * The value of this property must be an array of footer icon groups. Footer icon groups
  125. * have set of properties, including an array of arrays describing footer icons. For
  126. * example:
  127. *
  128. * array(
  129. * array( // footer icon group 1
  130. * 'class' => 'footerIconGroup1CssClass',
  131. * 'buttons' => array(
  132. * 'id' => 'myid',
  133. * 'title' => 'My Tooltip',
  134. * 'icon' => 'path/to/my/icon.png'
  135. * )
  136. * ),
  137. * array( // footer icon group 2
  138. * 'class' => 'footerIconGroup2CssClass',
  139. * 'buttons' => array(...)
  140. * )
  141. * )
  142. *
  143. * By default, when a user clicks on a footer icon, Piwik will assume the 'id' is
  144. * a viewDataTable ID and try to reload the DataTable w/ the new viewDataTable. You
  145. * can provide your own footer icon behavior by adding an appropriate handler via
  146. * DataTable.registerFooterIconHandler in your JavaScript code.
  147. *
  148. * The default value of this property is not set here and will show the 'Normal Table'
  149. * icon, the 'All Columns' icon, the 'Goals Columns' icon and all jqPlot graph columns,
  150. * unless other properties tell the view to exclude them.
  151. */
  152. public $footer_icons = false;
  153. /**
  154. * Controls whether the buttons and UI controls around the visualization or shown or
  155. * if just the visualization alone is shown.
  156. */
  157. public $show_visualization_only = false;
  158. /**
  159. * Controls whether the goals footer icon is shown.
  160. */
  161. public $show_goals = false;
  162. /**
  163. * Controls whether the 'insights' footer icon is shown.
  164. */
  165. public $show_insights = true;
  166. /**
  167. * Array property mapping DataTable column names with their internationalized names.
  168. *
  169. * The default value for this property is set elsewhere. It will contain translations
  170. * of common metrics.
  171. */
  172. public $translations = array();
  173. /**
  174. * Controls whether the 'Exclude Low Population' option (visible in the popup that displays after
  175. * clicking the 'cog' icon) is shown.
  176. */
  177. public $show_exclude_low_population = true;
  178. /**
  179. * Whether to show the 'Flatten' option (visible in the popup that displays after clicking the
  180. * 'cog' icon).
  181. */
  182. public $show_flatten_table = true;
  183. /**
  184. * Whether to show the 'Pivot by subtable' option (visible in the popup that displays after clicking
  185. * the 'cog' icon).
  186. */
  187. public $show_pivot_by_subtable;
  188. /**
  189. * The ID of the dimension to pivot by when the 'pivot by subtable' option is clicked. Defaults
  190. * to the subtable dimension of the report being displayed.
  191. */
  192. public $pivot_by_dimension;
  193. /**
  194. * The column to display in pivot tables. Defaults to the first non-label column if not specified.
  195. */
  196. public $pivot_by_column = '';
  197. /**
  198. * The human readable name of the pivot dimension.
  199. */
  200. public $pivot_dimension_name = false;
  201. /**
  202. * Controls whether the footer icon that allows users to switch to the 'normal' DataTable view
  203. * is shown.
  204. */
  205. public $show_table = true;
  206. /**
  207. * Controls whether the 'All Columns' footer icon is shown.
  208. */
  209. public $show_table_all_columns = true;
  210. /**
  211. * Controls whether the entire view footer is shown.
  212. */
  213. public $show_footer = true;
  214. /**
  215. * Controls whether the row that contains all footer icons & the limit selector is shown.
  216. */
  217. public $show_footer_icons = true;
  218. /**
  219. * Array property that determines which columns will be shown. Columns not in this array
  220. * should not appear in ViewDataTable visualizations.
  221. *
  222. * Example: `array('label', 'nb_visits', 'nb_uniq_visitors')`
  223. *
  224. * If this value is empty it will be defaulted to `array('label', 'nb_visits')` or
  225. * `array('label', 'nb_uniq_visitors')` if the report contains a nb_uniq_visitors column
  226. * after data is loaded.
  227. */
  228. public $columns_to_display = array();
  229. /**
  230. * Controls whether graph and non core viewDataTable footer icons are shown or not.
  231. */
  232. public $show_all_views_icons = true;
  233. /**
  234. * Controls whether to display a tiny upside-down caret over the currently active view icon.
  235. */
  236. public $show_active_view_icon = true;
  237. /**
  238. * Related reports are listed below a datatable view. When clicked, the original report will
  239. * change to the clicked report and the list will change so the original report can be
  240. * navigated back to.
  241. */
  242. public $related_reports = array();
  243. /**
  244. * "Related Reports" is displayed by default before listing the Related reports,
  245. * The string can be changed.
  246. */
  247. public $related_reports_title;
  248. /**
  249. * The report title. Used with related reports so report headings can be changed when switching
  250. * reports.
  251. *
  252. * This must be set if related reports are added.
  253. */
  254. public $title = '';
  255. /**
  256. * Controls whether a report's related reports are listed with the view or not.
  257. */
  258. public $show_related_reports = true;
  259. /**
  260. * Contains the documentation for a report.
  261. */
  262. public $documentation = false;
  263. /**
  264. * Array property containing custom data to be saved in JSON in the data-params HTML attribute
  265. * of a data table div. This data can be used by JavaScript DataTable classes.
  266. *
  267. * e.g. array('typeReferrer' => ...)
  268. *
  269. * It can then be accessed in the twig templates by clientSideParameters.typeReferrer
  270. */
  271. public $custom_parameters = array();
  272. /**
  273. * Controls whether the limit dropdown (which allows users to change the number of data shown)
  274. * is always shown or not.
  275. *
  276. * Normally shown only if pagination is enabled.
  277. */
  278. public $show_limit_control = true;
  279. /**
  280. * Controls whether the search box under the datatable is shown.
  281. */
  282. public $show_search = true;
  283. /**
  284. * Controls whether the user can sort DataTables by clicking on table column headings.
  285. */
  286. public $enable_sort = true;
  287. /**
  288. * Controls whether the footer icon that allows users to view data as a bar chart is shown.
  289. */
  290. public $show_bar_chart = true;
  291. /**
  292. * Controls whether the footer icon that allows users to view data as a pie chart is shown.
  293. */
  294. public $show_pie_chart = true;
  295. /**
  296. * Controls whether the footer icon that allows users to view data as a tag cloud is shown.
  297. */
  298. public $show_tag_cloud = true;
  299. /**
  300. * Controls whether the user is allowed to export data as an RSS feed or not.
  301. */
  302. public $show_export_as_rss_feed = true;
  303. /**
  304. * Controls whether the 'Ecoommerce Orders'/'Abandoned Cart' footer icons are shown or not.
  305. */
  306. public $show_ecommerce = false;
  307. /**
  308. * Stores an HTML message (if any) to display under the datatable view.
  309. */
  310. public $show_footer_message = false;
  311. /**
  312. * Array property that stores documentation for individual metrics.
  313. *
  314. * E.g. `array('nb_visits' => '...', ...)`
  315. *
  316. * By default this is set to values retrieved from report metadata (via API.getReportMetadata API method).
  317. */
  318. public $metrics_documentation = array();
  319. /**
  320. * Row metadata name that contains the tooltip for the specific row.
  321. */
  322. public $tooltip_metadata_name = false;
  323. /**
  324. * The URL to the report the view is displaying. Modifying this means clicking back to this report
  325. * from a Related Report will go to a different URL. Can be used to load an entire page instead
  326. * of a single report when going back to the original report.
  327. *
  328. * The URL used to request the report without generic filters.
  329. */
  330. public $self_url = '';
  331. /**
  332. * CSS class to use in the output HTML div. This is added in addition to the visualization CSS
  333. * class.
  334. */
  335. public $datatable_css_class = false;
  336. /**
  337. * The JavaScript class to instantiate after the result HTML is obtained. This class handles all
  338. * interactive behavior for the DataTable view.
  339. */
  340. public $datatable_js_type = 'DataTable';
  341. /**
  342. * If true, searching through the DataTable will search through all subtables.
  343. */
  344. public $search_recursive = false;
  345. /**
  346. * The unit of the displayed column. Valid if only one non-label column is displayed.
  347. */
  348. public $y_axis_unit = false;
  349. /**
  350. * Controls whether to show the 'Export as Image' footer icon.
  351. */
  352. public $show_export_as_image_icon = false;
  353. /**
  354. * Array of DataTable filters that should be run before displaying a DataTable. Elements
  355. * of this array can either be a closure or an array with at most three elements, including:
  356. * - the filter name (or a closure)
  357. * - an array of filter parameters
  358. * - a boolean indicating if the filter is a priority filter or not
  359. *
  360. * Priority filters are run before queued filters. These filters should be filters that
  361. * add/delete rows.
  362. *
  363. * If a closure is used, the view is appended as a parameter.
  364. */
  365. public $filters = array();
  366. /**
  367. * Contains the controller action to call when requesting subtables of the current report.
  368. *
  369. * By default, this is set to the controller action used to request the report.
  370. */
  371. public $subtable_controller_action = '';
  372. /**
  373. * Controls whether the 'prev'/'next' links are shown in the DataTable footer. These links
  374. * change the 'filter_offset' query parameter, thus allowing pagination.
  375. */
  376. public $show_pagination_control = true;
  377. /**
  378. * Controls whether offset information (ie, '5-10 of 20') is shown under the datatable.
  379. */
  380. public $show_offset_information = true;
  381. /**
  382. * Controls whether annotations are shown or not.
  383. */
  384. public $hide_annotations_view = true;
  385. /**
  386. * The filter_limit query parameter value to use in export links.
  387. *
  388. * Defaulted to the value of the `[General] API_datatable_default_limit` INI config option.
  389. */
  390. public $export_limit = '';
  391. /**
  392. * @ignore
  393. */
  394. public $report_id = '';
  395. /**
  396. * @ignore
  397. */
  398. public $controllerName;
  399. /**
  400. * @ignore
  401. */
  402. public $controllerAction;
  403. /**
  404. * Constructor.
  405. */
  406. public function __construct()
  407. {
  408. $this->export_limit = \Piwik\Config::getInstance()->General['API_datatable_default_limit'];
  409. $this->translations = array_merge(
  410. Metrics::getDefaultMetrics(),
  411. Metrics::getDefaultProcessedMetrics()
  412. );
  413. }
  414. /**
  415. * @ignore
  416. */
  417. public function setController($controllerName, $controllerAction)
  418. {
  419. $this->controllerName = $controllerName;
  420. $this->controllerAction = $controllerAction;
  421. $this->report_id = $controllerName . '.' . $controllerAction;
  422. $this->loadDocumentation();
  423. $this->setShouldShowPivotBySubtable();
  424. }
  425. /** Load documentation from the API */
  426. private function loadDocumentation()
  427. {
  428. $this->metrics_documentation = array();
  429. $report = API::getInstance()->getMetadata(0, $this->controllerName, $this->controllerAction);
  430. $report = $report[0];
  431. if (isset($report['metricsDocumentation'])) {
  432. $this->metrics_documentation = $report['metricsDocumentation'];
  433. }
  434. if (isset($report['documentation'])) {
  435. $this->documentation = $report['documentation'];
  436. }
  437. }
  438. /**
  439. * Marks display properties as client side properties. [Read this](#client-side-properties-desc)
  440. * to learn more.
  441. *
  442. * @param array $propertyNames List of property names, eg, `array('show_limit_control', 'show_goals')`.
  443. */
  444. public function addPropertiesThatShouldBeAvailableClientSide(array $propertyNames)
  445. {
  446. foreach ($propertyNames as $propertyName) {
  447. $this->clientSideProperties[] = $propertyName;
  448. }
  449. }
  450. /**
  451. * Marks display properties as overridable. [Read this](#overridable-properties-desc) to
  452. * learn more.
  453. *
  454. * @param array $propertyNames List of property names, eg, `array('show_limit_control', 'show_goals')`.
  455. */
  456. public function addPropertiesThatCanBeOverwrittenByQueryParams(array $propertyNames)
  457. {
  458. foreach ($propertyNames as $propertyName) {
  459. $this->overridableProperties[] = $propertyName;
  460. }
  461. }
  462. /**
  463. * Returns array of all property values in this config object. Property values are mapped
  464. * by name.
  465. *
  466. * @return array eg, `array('show_limit_control' => 0, 'show_goals' => 1, ...)`
  467. */
  468. public function getProperties()
  469. {
  470. return get_object_vars($this);
  471. }
  472. /**
  473. * @ignore
  474. */
  475. public function setDefaultColumnsToDisplay($columns, $hasNbVisits, $hasNbUniqVisitors)
  476. {
  477. if ($hasNbVisits || $hasNbUniqVisitors) {
  478. $columnsToDisplay = array('label');
  479. // if unique visitors data is available, show it, otherwise just visits
  480. if ($hasNbUniqVisitors) {
  481. $columnsToDisplay[] = 'nb_uniq_visitors';
  482. } else {
  483. $columnsToDisplay[] = 'nb_visits';
  484. }
  485. } else {
  486. $columnsToDisplay = $columns;
  487. }
  488. $this->columns_to_display = array_filter($columnsToDisplay);
  489. }
  490. /**
  491. * @ignore
  492. */
  493. public function getFiltersToRun()
  494. {
  495. $priorityFilters = array();
  496. $presentationFilters = array();
  497. foreach ($this->filters as $filterInfo) {
  498. if ($filterInfo instanceof \Closure) {
  499. $nameOrClosure = $filterInfo;
  500. $parameters = array();
  501. $priority = false;
  502. } else {
  503. @list($nameOrClosure, $parameters, $priority) = $filterInfo;
  504. }
  505. if ($priority) {
  506. $priorityFilters[] = array($nameOrClosure, $parameters);
  507. } else {
  508. $presentationFilters[] = array($nameOrClosure, $parameters);
  509. }
  510. }
  511. return array($priorityFilters, $presentationFilters);
  512. }
  513. /**
  514. * Adds a related report to the {@link $related_reports} property. If the report
  515. * references the one that is currently being displayed, it will not be added to the related
  516. * report list.
  517. *
  518. * @param string $relatedReport The plugin and method of the report, eg, `'UserSettings.getBrowser'`.
  519. * @param string $title The report's display name, eg, `'Browsers'`.
  520. * @param array $queryParams Any extra query parameters to set in releated report's URL, eg,
  521. * `array('idGoal' => 'ecommerceOrder')`.
  522. */
  523. public function addRelatedReport($relatedReport, $title, $queryParams = array())
  524. {
  525. list($module, $action) = explode('.', $relatedReport);
  526. // don't add the related report if it references this report
  527. if ($this->controllerName == $module
  528. && $this->controllerAction == $action) {
  529. if(empty($queryParams)) {
  530. return;
  531. }
  532. }
  533. $url = ApiRequest::getBaseReportUrl($module, $action, $queryParams);
  534. $this->related_reports[$url] = $title;
  535. }
  536. /**
  537. * Adds several related reports to the {@link $related_reports} property. If
  538. * any of the reports references the report that is currently being displayed, it will not
  539. * be added to the list. All other reports will still be added though.
  540. *
  541. * If you need to make sure the related report URL has some extra query parameters,
  542. * use {@link addRelatedReport()}.
  543. *
  544. * @param array $relatedReports Array mapping report IDs with their internationalized display
  545. * titles, eg,
  546. * ```
  547. * array(
  548. * 'UserSettings.getBrowser' => 'Browsers',
  549. * 'UserSettings.getConfiguration' => 'Configurations'
  550. * )
  551. * ```
  552. */
  553. public function addRelatedReports($relatedReports)
  554. {
  555. foreach ($relatedReports as $report => $title) {
  556. $this->addRelatedReport($report, $title);
  557. }
  558. }
  559. /**
  560. * Associates internationalized text with a metric. Overwrites existing mappings.
  561. *
  562. * See {@link $translations}.
  563. *
  564. * @param string $columnName The name of a column in the report data, eg, `'nb_visits'` or
  565. * `'goal_1_nb_conversions'`.
  566. * @param string $translation The internationalized text, eg, `'Visits'` or `"Conversions for 'My Goal'"`.
  567. */
  568. public function addTranslation($columnName, $translation)
  569. {
  570. $this->translations[$columnName] = $translation;
  571. }
  572. /**
  573. * Associates multiple translations with metrics.
  574. *
  575. * See {@link $translations} and {@link addTranslation()}.
  576. *
  577. * @param array $translations An array of column name => text mappings, eg,
  578. * ```
  579. * array(
  580. * 'nb_visits' => 'Visits',
  581. * 'goal_1_nb_conversions' => "Conversions for 'My Goal'"
  582. * )
  583. * ```
  584. */
  585. public function addTranslations($translations)
  586. {
  587. foreach ($translations as $key => $translation) {
  588. $this->addTranslation($key, $translation);
  589. }
  590. }
  591. private function setShouldShowPivotBySubtable()
  592. {
  593. $report = Report::factory($this->controllerName, $this->controllerAction);
  594. if (empty($report)) {
  595. $this->show_pivot_by_subtable = false;
  596. $this->pivot_by_dimension = false;
  597. } else {
  598. $this->show_pivot_by_subtable = PivotByDimension::isPivotingReportBySubtableSupported($report);
  599. $subtableDimension = $report->getSubtableDimension();
  600. if (!empty($subtableDimension)) {
  601. $this->pivot_by_dimension = $subtableDimension->getId();
  602. $this->pivot_dimension_name = $subtableDimension->getName();
  603. }
  604. }
  605. }
  606. public function disablePivotBySubtableIfTableHasNoSubtables(DataTable $table)
  607. {
  608. foreach ($table->getRows() as $row) {
  609. if ($row->getIdSubDataTable() !== null) {
  610. return;
  611. }
  612. }
  613. $this->show_pivot_by_subtable = false;
  614. }
  615. }