You can create a new report using the console command ./console generate:report. The generated report will guide you through the creation of a report.
Since: 2.5.0
Example #1
0
 public function renderReportWidget(Report $report)
 {
     Piwik::checkUserHasSomeViewAccess();
     $this->checkSitePermission();
     $report->checkIsEnabled();
     return $report->render();
 }
Example #2
0
 /**
  * Creates a new container widget based on the specified report in {@link construct()}.
  *
  * It will automatically use the report's categoryId, subcategoryId (if specified) and order in order to
  * create the container.
  *
  * @param string $containerId eg 'Products' or 'Contents' see {Piwik\Widget\WidgetContainerConfig::setId()}.
  *                            Other reports or widgets will be able to add more widgets to this container.
  *                            This is useful when you want to show for example multiple related widgets
  *                            together.
  * @return WidgetContainerConfig
  */
 public function createContainerWidget($containerId)
 {
     $widget = new WidgetContainerConfig();
     $widget->setCategoryId($this->report->getCategoryId());
     $widget->setId($containerId);
     if ($this->report->getSubcategoryId()) {
         $widget->setSubcategoryId($this->report->getSubcategoryId());
     }
     $orderThatListsReportsAtTheEndOfEachCategory = 100 + $this->report->getOrder();
     $widget->setOrder($orderThatListsReportsAtTheEndOfEachCategory);
     return $widget;
 }
Example #3
0
 private function getGenericFiltersHavingDefaultValues()
 {
     $filters = self::getGenericFiltersInformation();
     if ($this->report && $this->report->getDefaultSortColumn()) {
         foreach ($filters as $index => $filter) {
             if ($filter[0] === 'Sort') {
                 $filters[$index][1]['filter_sort_column'] = array('string', $this->report->getDefaultSortColumn());
                 $filters[$index][1]['filter_sort_order'] = array('string', $this->report->getDefaultSortOrder());
             }
         }
     }
     return $filters;
 }
Example #4
0
 public function getMetrics()
 {
     $metrics = parent::getMetrics();
     $metrics['avg_time_on_site'] = Piwik::translate('General_VisitDuration');
     $metrics['max_actions'] = Piwik::translate('General_ColumnMaxActions');
     return $metrics;
 }
Example #5
0
File: Get.php Project: piwik/piwik
 protected function init()
 {
     parent::init();
     $this->reportsToMerge = $this->getReportsToMerge();
     $this->module = 'API';
     $this->action = 'get';
     $this->categoryId = 'API';
     $this->name = Piwik::translate('General_MainMetrics');
     $this->documentation = '';
     $this->processedMetrics = array();
     foreach ($this->reportsToMerge as $report) {
         if (!is_array($report->processedMetrics)) {
             continue;
         }
         $this->processedMetrics = array_merge($this->processedMetrics, $report->processedMetrics);
     }
     $this->metrics = array();
     foreach ($this->reportsToMerge as $report) {
         if (!is_array($report->metrics)) {
             continue;
         }
         $this->metrics = array_merge($this->metrics, $report->metrics);
     }
     $this->order = 6;
 }
Example #6
0
 protected function init()
 {
     parent::init();
     $this->category = 'General_Visitors';
     $this->name = Piwik::translate('VisitFrequency_ColumnReturningVisits');
     $this->documentation = '';
     // TODO
     $this->metrics = array('nb_visits_returning', 'nb_actions_returning', 'avg_time_on_site_returning', 'bounce_rate_returning', 'nb_actions_per_visit_returning', 'nb_uniq_visitors_returning');
     $this->processedMetrics = false;
     $this->order = 40;
 }
Example #7
0
 protected function init()
 {
     parent::init();
     $this->category = 'General_Visitors';
     $this->name = Piwik::translate('VisitFrequency_ColumnReturningVisits');
     $this->documentation = '';
     // TODO
     $this->processedMetrics = array(new ReturningMetric(new AverageTimeOnSite()), new ReturningMetric(new ActionsPerVisit()), new ReturningMetric(new BounceRate()));
     $this->metrics = array('nb_visits_returning', 'nb_actions_returning', 'nb_uniq_visitors_returning', 'nb_users_returning', 'max_actions_returning');
     $this->order = 40;
 }
Example #8
0
 public function renderReportWidget($reportModule = null, $reportAction = null)
 {
     Piwik::checkUserHasSomeViewAccess();
     $this->checkSitePermission();
     $report = Report::factory($reportModule, $reportAction);
     if (empty($report)) {
         throw new Exception(Piwik::translate('General_ExceptionReportNotFound'));
     }
     $report->checkIsEnabled();
     return $report->render();
 }
Example #9
0
 public function get($idSite, $period, $date, $segment = false, $columns = false)
 {
     Piwik::checkUserHasViewAccess($idSite);
     $archive = Archive::build($idSite, $period, $date, $segment);
     $requestedColumns = Piwik::getArrayFromApiParameter($columns);
     $report = Report::factory("VisitsSummary", "get");
     $columns = $report->getMetricsRequiredForReport($this->getCoreColumns($period), $requestedColumns);
     $dataTable = $archive->getDataTableFromNumeric($columns);
     if (!empty($requestedColumns)) {
         $columnsToShow = $requestedColumns ?: $report->getAllMetrics();
         $dataTable->queueFilter('ColumnDelete', array($columnsToRemove = array(), $columnsToShow));
     }
     return $dataTable;
 }
 /**
  * See {@link AddSegmentBySegmentValue}.
  *
  * @param DataTable $table
  * @return int The number of deleted rows.
  */
 public function filter($table)
 {
     if (empty($this->report) || !$table->getRowsCount()) {
         return;
     }
     $dimension = $this->report->getDimension();
     if (empty($dimension)) {
         return;
     }
     $segments = $dimension->getSegments();
     if (empty($segments)) {
         return;
     }
     /** @var \Piwik\Plugin\Segment $segment */
     $segment = reset($segments);
     $segmentName = $segment->getSegment();
     foreach ($table->getRows() as $row) {
         $value = $row->getMetadata('segmentValue');
         $filter = $row->getMetadata('segment');
         if ($value !== false && $filter === false) {
             $row->setMetadata('segment', sprintf('%s==%s', $segmentName, urlencode($value)));
         }
     }
 }
Example #11
0
 public function indexSiteSearch()
 {
     $view = new View('@Actions/indexSiteSearch');
     $keyword = Report::factory($this->pluginName, 'getSiteSearchKeywords');
     $noResult = Report::factory($this->pluginName, 'getSiteSearchNoResultKeywords');
     $pageUrls = Report::factory($this->pluginName, 'getPageUrlsFollowingSiteSearch');
     $view->keywords = $keyword->render();
     $view->noResultKeywords = $noResult->render();
     $view->pagesUrlsFollowingSiteSearch = $pageUrls->render();
     $categoryTrackingEnabled = Actions::isCustomVariablesPluginsEnabled();
     if ($categoryTrackingEnabled) {
         $categories = Report::factory($this->pluginName, 'getSiteSearchCategories');
         $view->categories = $categories->render();
     }
     return $view->render();
 }
Example #12
0
 public function software()
 {
     $view = new View('@DevicesDetection/software');
     $view->osReport = $this->renderReport('getOsVersions');
     $view->browserReport = $this->renderReport('getBrowsers');
     $view->browserEngineReport = $this->renderReport('getBrowserEngines');
     $isResolutionEnabled = PluginManager::getInstance()->isPluginActivated('Resolution');
     if ($isResolutionEnabled) {
         $view->configurations = $this->renderReport(Report::factory('Resolution', 'getConfiguration'));
     }
     $isDevicePluginsEnabled = PluginManager::getInstance()->isPluginActivated('DevicePlugins');
     if ($isDevicePluginsEnabled) {
         $view->browserPlugins = $this->renderReport(Report::factory('DevicePlugins', 'getPlugin'));
     }
     return $view->render();
 }
Example #13
0
 /**
  * Returns the list of metrics (pages, downloads, outlinks)
  *
  * @param int $idSite
  * @param string $period
  * @param string $date
  * @param bool|string $segment
  * @param bool|array $columns
  * @return DataTable
  */
 public function get($idSite, $period, $date, $segment = false, $columns = false)
 {
     Piwik::checkUserHasViewAccess($idSite);
     $report = Report::factory("Actions", "get");
     $archive = Archive::build($idSite, $period, $date, $segment);
     $requestedColumns = Piwik::getArrayFromApiParameter($columns);
     $columns = $report->getMetricsRequiredForReport($allColumns = null, $requestedColumns);
     $inDbColumnNames = array_map(function ($value) {
         return 'Actions_' . $value;
     }, $columns);
     $dataTable = $archive->getDataTableFromNumeric($inDbColumnNames);
     $dataTable->deleteColumns(array_diff($requestedColumns, $columns));
     $newNameMapping = array_combine($inDbColumnNames, $columns);
     $dataTable->filter('ReplaceColumnNames', array($newNameMapping));
     $columnsToShow = $requestedColumns ?: $report->getAllMetrics();
     $dataTable->queueFilter('ColumnDelete', array($columnsToRemove = array(), $columnsToShow));
     return $dataTable;
 }
Example #14
0
 /**
  * @return Report[]
  */
 private function getReportsToMerge()
 {
     $result = array();
     foreach (Report::getAllReportClasses() as $reportClass) {
         if ($reportClass == 'Piwik\\Plugins\\API\\Reports\\Get') {
             continue;
         }
         /** @var Report $report */
         $report = new $reportClass();
         if ($report->getModule() == 'API' || $report->getAction() != 'get') {
             continue;
         }
         $metrics = $report->getMetrics();
         if (!empty($report->parameters) || empty($metrics)) {
             continue;
         }
         $result[] = $report;
     }
     return $result;
 }
Example #15
0
 protected function makeController($module, $action, &$parameters)
 {
     $controllerClassName = $this->getClassNameController($module);
     // TRY TO FIND ACTION IN CONTROLLER
     if (class_exists($controllerClassName)) {
         $class = $this->getClassNameController($module);
         /** @var $controller Controller */
         $controller = new $class();
         $controllerAction = $action;
         if ($controllerAction === false) {
             $controllerAction = $controller->getDefaultAction();
         }
         if (is_callable(array($controller, $controllerAction))) {
             return array($controller, $controllerAction);
         }
         if ($action === false) {
             $this->triggerControllerActionNotFoundError($module, $controllerAction);
         }
     }
     // TRY TO FIND ACTION IN WIDGET
     $widget = Widgets::factory($module, $action);
     if (!empty($widget)) {
         $parameters['widgetModule'] = $module;
         $parameters['widgetMethod'] = $action;
         return array(new CoreHomeController(), 'renderWidget');
     }
     // TRY TO FIND ACTION IN REPORT
     $report = Report::factory($module, $action);
     if (!empty($report)) {
         $parameters['reportModule'] = $module;
         $parameters['reportAction'] = $action;
         return array(new CoreHomeController(), 'renderReportWidget');
     }
     if (!empty($action) && 'menu' === substr($action, 0, 4)) {
         $reportAction = lcfirst(substr($action, 4));
         // menuGetPageUrls => getPageUrls
         $report = Report::factory($module, $reportAction);
         if (!empty($report)) {
             $parameters['reportModule'] = $module;
             $parameters['reportAction'] = $reportAction;
             return array(new CoreHomeController(), 'renderReportMenu');
         }
     }
     $this->triggerControllerActionNotFoundError($module, $action);
 }
Example #16
0
 /**
  * Constructor. Initializes display and request properties to their default values.
  * Posts the {@hook ViewDataTable.configure} event which plugins can use to configure the
  * way reports are displayed.
  */
 public function __construct($controllerAction, $apiMethodToRequestDataTable, $overrideParams = array())
 {
     list($controllerName, $controllerAction) = explode('.', $controllerAction);
     $this->requestConfig = static::getDefaultRequestConfig();
     $this->config = static::getDefaultConfig();
     $this->config->subtable_controller_action = $controllerAction;
     $this->config->setController($controllerName, $controllerAction);
     $this->request = new ViewDataTableRequest($this->requestConfig);
     $this->requestConfig->idSubtable = Common::getRequestVar('idSubtable', false, 'int');
     $this->config->self_url = Request::getBaseReportUrl($controllerName, $controllerAction);
     $this->requestConfig->apiMethodToRequestDataTable = $apiMethodToRequestDataTable;
     $report = Report::factory($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest());
     if (!empty($report)) {
         /** @var Report $report */
         $subtable = $report->getActionToLoadSubTables();
         if (!empty($subtable)) {
             $this->config->subtable_controller_action = $subtable;
         }
         $this->config->show_goals = $report->hasGoalMetrics();
         $relatedReports = $report->getRelatedReports();
         if (!empty($relatedReports)) {
             foreach ($relatedReports as $relatedReport) {
                 $widgetTitle = $relatedReport->getWidgetTitle();
                 if ($widgetTitle && Common::getRequestVar('widget', 0, 'int')) {
                     $relatedReportName = $widgetTitle;
                 } else {
                     $relatedReportName = $relatedReport->getName();
                 }
                 $this->config->addRelatedReport($relatedReport->getModule() . '.' . $relatedReport->getAction(), $relatedReportName);
             }
         }
         $metrics = $report->getMetrics();
         if (!empty($metrics)) {
             $this->config->addTranslations($metrics);
         }
         $processedMetrics = $report->getProcessedMetrics();
         if (!empty($processedMetrics)) {
             $this->config->addTranslations($processedMetrics);
         }
         $report->configureView($this);
     }
     /**
      * Triggered during {@link ViewDataTable} construction. Subscribers should customize
      * the view based on the report that is being displayed.
      *
      * Plugins that define their own reports must subscribe to this event in order to
      * specify how the Piwik UI should display the report.
      *
      * **Example**
      *
      *     // event handler
      *     public function configureViewDataTable(ViewDataTable $view)
      *     {
      *         switch ($view->requestConfig->apiMethodToRequestDataTable) {
      *             case 'VisitTime.getVisitInformationPerServerTime':
      *                 $view->config->enable_sort = true;
      *                 $view->requestConfig->filter_limit = 10;
      *                 break;
      *         }
      *     }
      *
      * @param ViewDataTable $view The instance to configure.
      */
     Piwik::postEvent('ViewDataTable.configure', array($this));
     $this->assignRelatedReportsTitle();
     $this->config->show_footer_icons = false == $this->requestConfig->idSubtable;
     // the exclude low population threshold value is sometimes obtained by requesting data.
     // to avoid issuing unecessary requests when display properties are determined by metadata,
     // we allow it to be a closure.
     if (isset($this->requestConfig->filter_excludelowpop_value) && $this->requestConfig->filter_excludelowpop_value instanceof \Closure) {
         $function = $this->requestConfig->filter_excludelowpop_value;
         $this->requestConfig->filter_excludelowpop_value = $function();
     }
     $this->overrideViewPropertiesWithParams($overrideParams);
     $this->overrideViewPropertiesWithQueryParams();
 }
Example #17
0
 public function test_getForDimension_ShouldReturnNullIfReportPluginNotLoaded()
 {
     PluginManager::getInstance()->loadPlugins(array());
     $report = Report::getForDimension(new Keyword());
     $this->assertNull($report);
 }
Example #18
0
 /**
  * Convenience method that creates and renders a ViewDataTable for a API method.
  *
  * @param string|\Piwik\Plugin\Report $apiAction The name of the API action (eg, `'getResolution'`) or
  *                                      an instance of an report.
  * @param bool $controllerAction The name of the Controller action name  that is rendering the report. Defaults
  *                               to the `$apiAction`.
  * @param bool $fetch If `true`, the rendered string is returned, if `false` it is `echo`'d.
  * @throws \Exception if `$pluginName` is not an existing plugin or if `$apiAction` is not an
  *                    existing method of the plugin's API.
  * @return string|void See `$fetch`.
  * @api
  */
 protected function renderReport($apiAction, $controllerAction = false)
 {
     if (empty($controllerAction) && is_string($apiAction)) {
         $report = Report::factory($this->pluginName, $apiAction);
         if (!empty($report)) {
             $apiAction = $report;
         }
     }
     if ($apiAction instanceof Report) {
         $this->checkSitePermission();
         $apiAction->checkIsEnabled();
         return $apiAction->render();
     }
     $pluginName = $this->pluginName;
     /** @var Proxy $apiProxy */
     $apiProxy = Proxy::getInstance();
     if (!$apiProxy->isExistingApiAction($pluginName, $apiAction)) {
         throw new \Exception("Invalid action name '{$apiAction}' for '{$pluginName}' plugin.");
     }
     $apiAction = $apiProxy->buildApiActionName($pluginName, $apiAction);
     if ($controllerAction !== false) {
         $controllerAction = $pluginName . '.' . $controllerAction;
     }
     $view = ViewDataTableFactory::build(null, $apiAction, $controllerAction);
     $rendered = $view->render();
     return $rendered;
 }
Example #19
0
 public function getProcessedMetrics()
 {
     $metrics = parent::getProcessedMetrics();
     $metrics['avg_time_on_site'] = Piwik::translate('General_VisitDuration');
     return $metrics;
 }
 private function isAllMetricsReport()
 {
     return $this->report->getModule() == 'API' && $this->report->getAction() == 'get';
 }
Example #21
0
 private static function getAllReportsWithGoalMetrics()
 {
     $reportsWithGoals = array();
     foreach (Report::getAllReports() as $report) {
         if ($report->hasGoalMetrics()) {
             $reportsWithGoals[] = array('category' => $report->getCategory(), 'name' => $report->getName(), 'module' => $report->getModule(), 'action' => $report->getAction());
         }
     }
     /**
      * Triggered when gathering all reports that contain Goal metrics. The list of reports
      * will be displayed on the left column of the bottom of every _Goals_ page.
      *
      * If plugins define reports that contain goal metrics (such as **conversions** or **revenue**),
      * they can use this event to make sure their reports can be viewed on Goals pages.
      *
      * **Example**
      *
      *     public function getReportsWithGoalMetrics(&$reports)
      *     {
      *         $reports[] = array(
      *             'category' => Piwik::translate('MyPlugin_myReportCategory'),
      *             'name' => Piwik::translate('MyPlugin_myReportDimension'),
      *             'module' => 'MyPlugin',
      *             'action' => 'getMyReport'
      *         );
      *     }
      *
      * @param array &$reportsWithGoals The list of arrays describing reports that have Goal metrics.
      *                                 Each element of this array must be an array with the following
      *                                 properties:
      *
      *                                 - **category**: The report category. This should be a translated string.
      *                                 - **name**: The report's translated name.
      *                                 - **module**: The plugin the report is in, eg, `'UserCountry'`.
      *                                 - **action**: The API method of the report, eg, `'getCountry'`.
      * @ignore
      * @deprecated since 2.5.0
      */
     Piwik::postEvent('Goals.getReportsWithGoalMetrics', array(&$reportsWithGoals));
     return $reportsWithGoals;
 }
Example #22
0
 private function addVisualizationInfoFromMetricMetadata()
 {
     $dataTable = $this->dataTable instanceof DataTable\Map ? $this->dataTable->getFirstRow() : $this->dataTable;
     $metrics = Report::getMetricsForTable($dataTable, $this->report);
     // TODO: instead of iterating & calling translate everywhere, maybe we can get all translated names in one place.
     //       may be difficult, though, since translated metrics are specific to the report.
     foreach ($metrics as $metric) {
         $name = $metric->getName();
         if (empty($this->config->translations[$name])) {
             $this->config->translations[$name] = $metric->getTranslatedName();
         }
         if (empty($this->config->metrics_documentation[$name])) {
             $this->config->metrics_documentation[$name] = $metric->getDocumentation();
         }
     }
 }
Example #23
0
 public function test_getAllReports_ShouldFindAllAvailableReports()
 {
     $this->loadExampleReportPlugin();
     $this->loadMorePlugins();
     $reports = Report::getAllReports();
     $this->assertGreaterThan(20, count($reports));
     foreach ($reports as $report) {
         $this->assertInstanceOf('Piwik\\Plugin\\Report', $report);
     }
 }
Example #24
0
 /**
  * Returns if the default viewDataTable ID to use is fixed.
  *
  * @param Report $report
  * @return bool
  */
 private static function isDefaultViewTypeForReportFixed($report)
 {
     if (!empty($report) && $report->isEnabled()) {
         return $report->alwaysUseDefaultViewDataTable();
     }
     return false;
 }
 public function getMetrics()
 {
     $metrics = parent::getMetrics();
     if ($this->scopeOfDimension === CustomDimensions::SCOPE_ACTION) {
         $metrics['nb_visits'] = Piwik::translate('CustomDimensions_ColumnUniqueActions');
     }
     if (array_key_exists('nb_hits', $metrics)) {
         $metrics['nb_hits'] = Piwik::translate('General_ColumnNbActions');
     }
     return $metrics;
 }
Example #26
0
 /**
  * @param DataTable $dataTable
  * @param Report $report
  * @return Metric[]
  */
 private function getMetricsToFormat(DataTable $dataTable, Report $report = null)
 {
     return Report::getMetricsForTable($dataTable, $report, $baseType = 'Piwik\\Plugin\\Metric');
 }
Example #27
0
 public function configureViewDataTable(ViewDataTable $view)
 {
     if ($view->requestConfig->getApiModuleToRequest() != 'Events') {
         return;
     }
     // eg. 'Events.getCategory'
     $apiMethod = $view->requestConfig->getApiMethodToRequest();
     $secondaryDimension = $this->getSecondaryDimensionFromRequest();
     $view->config->subtable_controller_action = API::getInstance()->getActionToLoadSubtables($apiMethod, $secondaryDimension);
     if (Common::getRequestVar('pivotBy', false) === false) {
         $view->config->columns_to_display = array('label', 'nb_events', 'sum_event_value');
     }
     $view->config->show_flatten_table = true;
     $view->config->show_table_all_columns = false;
     $view->requestConfig->filter_sort_column = 'nb_events';
     $labelTranslation = $this->getColumnTranslation($apiMethod);
     $view->config->addTranslation('label', $labelTranslation);
     $view->config->addTranslations($this->getMetricTranslations());
     $this->addRelatedReports($view, $secondaryDimension);
     $this->addTooltipEventValue($view);
     $subtableReport = Report::factory('Events', $view->config->subtable_controller_action);
     $view->config->pivot_by_dimension = $subtableReport->getDimension()->getId();
     $view->config->pivot_by_column = 'nb_events';
 }
Example #28
0
 /**
  * Triggers the Menu.Reporting.addItems hook and returns the menu.
  *
  * @return Array
  */
 public function getMenu()
 {
     if (!$this->menu) {
         /**
          * @ignore
          * @deprecated
          */
         Piwik::postEvent('Menu.Reporting.addItems', array());
         foreach (Report::getAllReports() as $report) {
             if ($report->isEnabled()) {
                 $report->configureReportingMenu($this);
             }
         }
         foreach ($this->getAllMenus() as $menu) {
             $menu->configureReportingMenu($this);
         }
     }
     return parent::getMenu();
 }
Example #29
0
 /**
  * Returns true if pivoting by subtable is supported for a report. Will return true if the report
  * has a subtable dimension and if the subtable dimension is different than the report's dimension.
  *
  * @param Report $report
  * @return bool
  */
 public static function isPivotingReportBySubtableSupported(Report $report)
 {
     return self::areDimensionsNotEqualAndNotNull($report->getSubtableDimension(), $report->getDimension());
 }
 /**
  * Triggers a hook to ask plugins for available Reports.
  * Returns metadata information about each report (category, name, dimension, metrics, etc.)
  *
  * @param string $idSites Comma separated list of website Ids
  * @param bool|string $period
  * @param bool|Date $date
  * @param bool $hideMetricsDoc
  * @param bool $showSubtableReports
  * @return array
  */
 public function getReportMetadata($idSites, $period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false)
 {
     $idSites = Site::getIdSitesFromIdSitesString($idSites);
     if (!empty($idSites)) {
         Piwik::checkUserHasViewAccess($idSites);
     }
     // as they cache key contains a lot of information there would be an even better cache result by caching parts of
     // this huge method separately but that makes it also more complicated. leaving it like this for now.
     $key = $this->buildReportMetadataCacheKey($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports);
     $key = CacheId::pluginAware($key);
     $cache = PiwikCache::getTransientCache();
     if ($cache->contains($key)) {
         return $cache->fetch($key);
     }
     $parameters = array('idSites' => $idSites, 'period' => $period, 'date' => $date);
     $availableReports = array();
     foreach (Report::getAllReports() as $report) {
         $report->configureReportMetadata($availableReports, $parameters);
     }
     /**
      * Triggered when gathering metadata for all available reports.
      *
      * Plugins that define new reports should use this event to make them available in via
      * the metadata API. By doing so, the report will become available in scheduled reports
      * as well as in the Piwik Mobile App. In fact, any third party app that uses the metadata
      * API will automatically have access to the new report.
      *
      * @param string &$availableReports The list of available reports. Append to this list
      *                                  to make a report available.
      *
      *                                  Every element of this array must contain the following
      *                                  information:
      *
      *                                  - **category**: A translated string describing the report's category.
      *                                  - **name**: The translated display title of the report.
      *                                  - **module**: The plugin of the report.
      *                                  - **action**: The API method that serves the report.
      *
      *                                  The following information is optional:
      *
      *                                  - **dimension**: The report's [dimension](/guides/all-about-analytics-data#dimensions) if any.
      *                                  - **metrics**: An array mapping metric names with their display names.
      *                                  - **metricsDocumentation**: An array mapping metric names with their
      *                                                              translated documentation.
      *                                  - **processedMetrics**: The array of metrics in the report that are
      *                                                          calculated using existing metrics. Can be set to
      *                                                          `false` if the report contains no processed
      *                                                          metrics.
      *                                  - **order**: The order of the report in the list of reports
      *                                               with the same category.
      *
      * @param array $parameters Contains the values of the sites and period we are
      *                          getting reports for. Some reports depend on this data.
      *                          For example, Goals reports depend on the site IDs being
      *                          requested. Contains the following information:
      *
      *                          - **idSites**: The array of site IDs we are getting reports for.
      *                          - **period**: The period type, eg, `'day'`, `'week'`, `'month'`,
      *                                        `'year'`, `'range'`.
      *                          - **date**: A string date within the period or a date range, eg,
      *                                      `'2013-01-01'` or `'2012-01-01,2013-01-01'`.
      *
      * TODO: put dimensions section in all about analytics data
      * @deprecated since 2.5.0 Use Report Classes instead.
      * @ignore
      */
     Piwik::postEvent('API.getReportMetadata', array(&$availableReports, $parameters));
     // TODO we can remove this one once we remove API.getReportMetadata event (except hideMetricsDoc)
     foreach ($availableReports as &$availableReport) {
         // can be removed once we remove hook API.getReportMetadata
         if (!isset($availableReport['metrics'])) {
             $availableReport['metrics'] = Metrics::getDefaultMetrics();
         }
         // can be removed once we remove hook API.getReportMetadata
         if (!isset($availableReport['processedMetrics'])) {
             $availableReport['processedMetrics'] = Metrics::getDefaultProcessedMetrics();
         }
         if ($hideMetricsDoc) {
             unset($availableReport['metricsDocumentation']);
         } else {
             if (!isset($availableReport['metricsDocumentation'])) {
                 // set metric documentation to default if it's not set
                 // can be removed once we remove hook API.getReportMetadata
                 $availableReport['metricsDocumentation'] = Metrics::getDefaultMetricsDocumentation();
             }
         }
     }
     /**
      * Triggered after all available reports are collected.
      *
      * This event can be used to modify the report metadata of reports in other plugins. You
      * could, for example, add custom metrics to every report or remove reports from the list
      * of available reports.
      *
      * @param array &$availableReports List of all report metadata. Read the {@hook API.getReportMetadata}
      *                                 docs to see what this array contains.
      * @param array $parameters Contains the values of the sites and period we are
      *                          getting reports for. Some report depend on this data.
      *                          For example, Goals reports depend on the site IDs being
      *                          request. Contains the following information:
      *
      *                          - **idSites**: The array of site IDs we are getting reports for.
      *                          - **period**: The period type, eg, `'day'`, `'week'`, `'month'`,
      *                                        `'year'`, `'range'`.
      *                          - **date**: A string date within the period or a date range, eg,
      *                                      `'2013-01-01'` or `'2012-01-01,2013-01-01'`.
      */
     Piwik::postEvent('API.getReportMetadata.end', array(&$availableReports, $parameters));
     // Sort results to ensure consistent order
     usort($availableReports, array('self', 'sortReports'));
     $knownMetrics = array_merge(Metrics::getDefaultMetrics(), Metrics::getDefaultProcessedMetrics());
     $columnsToKeep = $this->getColumnsToKeep();
     $columnsToRemove = $this->getColumnsToRemove();
     foreach ($availableReports as &$availableReport) {
         // Ensure all metrics have a translation
         $metrics = $availableReport['metrics'];
         $cleanedMetrics = array();
         // TODO we can remove this once we remove the getReportMetadata event, leaving it here for backwards compatibility
         foreach ($metrics as $metricId => $metricTranslation) {
             // When simply the column name was given, ie 'metric' => array( 'nb_visits' )
             // $metricTranslation is in this case nb_visits. We look for a known translation.
             if (is_numeric($metricId) && isset($knownMetrics[$metricTranslation])) {
                 $metricId = $metricTranslation;
                 $metricTranslation = $knownMetrics[$metricTranslation];
             }
             $cleanedMetrics[$metricId] = $metricTranslation;
         }
         $availableReport['metrics'] = $cleanedMetrics;
         // if hide/show columns specified, hide/show metrics & docs
         $availableReport['metrics'] = $this->hideShowMetricsWithParams($availableReport['metrics'], $columnsToRemove, $columnsToKeep);
         if (isset($availableReport['processedMetrics'])) {
             $availableReport['processedMetrics'] = $this->hideShowMetricsWithParams($availableReport['processedMetrics'], $columnsToRemove, $columnsToKeep);
         }
         if (isset($availableReport['metricsDocumentation'])) {
             $availableReport['metricsDocumentation'] = $this->hideShowMetricsWithParams($availableReport['metricsDocumentation'], $columnsToRemove, $columnsToKeep);
         }
         // Remove array elements that are false (to clean up API output)
         foreach ($availableReport as $attributeName => $attributeValue) {
             if (empty($attributeValue)) {
                 unset($availableReport[$attributeName]);
             }
         }
         // when there are per goal metrics, don't display conversion_rate since it can differ from per goal sum
         // TODO we should remove this once we remove the getReportMetadata event, leaving it here for backwards compatibility
         if (isset($availableReport['metricsGoal'])) {
             unset($availableReport['processedMetrics']['conversion_rate']);
             unset($availableReport['metricsGoal']['conversion_rate']);
         }
         // Processing a uniqueId for each report,
         // can be used by UIs as a key to match a given report
         $uniqueId = $availableReport['module'] . '_' . $availableReport['action'];
         if (!empty($availableReport['parameters'])) {
             foreach ($availableReport['parameters'] as $key => $value) {
                 $uniqueId .= '_' . $key . '--' . $value;
             }
         }
         $availableReport['uniqueId'] = $uniqueId;
         // Order is used to order reports internally, but not meant to be used outside
         unset($availableReport['order']);
     }
     // remove subtable reports
     if (!$showSubtableReports) {
         foreach ($availableReports as $idx => $report) {
             if (isset($report['isSubtableReport']) && $report['isSubtableReport']) {
                 unset($availableReports[$idx]);
             }
         }
     }
     $actualReports = array_values($availableReports);
     $cache->save($key, $actualReports);
     return $actualReports;
     // make sure array has contiguous key values
 }