public static function checkUserHasAdminAccess($idSites) { if (!self::$superUser) { $websitesAccess = self::$idSitesAdmin; } else { $websitesAccess = Piwik_SitesManager_API::getInstance()->getAllSitesId(); } $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); foreach ($idSites as $idsite) { if (!in_array($idsite, $websitesAccess)) { throw new Exception("checkUserHasAdminAccess Fake exception // string not to be tested"); } } }
/** * Returns all Goals for a given website, or list of websites * * @param string|array $idSite Array or Comma separated list of website IDs to request the goals for * @return array Array of Goal attributes */ public function getGoals($idSite) { if (!is_array($idSite)) { $idSite = Piwik_Site::getIdSitesFromIdSitesString($idSite); } Piwik::checkUserHasViewAccess($idSite); $goals = Piwik_FetchAll("SELECT * \n\t\t\t\t\t\t\t\tFROM " . Piwik_Common::prefixTable('goal') . " \n\t\t\t\t\t\t\t\tWHERE idsite IN (" . implode(", ", $idSite) . ")\n\t\t\t\t\t\t\t\t\tAND deleted = 0"); $cleanedGoals = array(); foreach ($goals as &$goal) { if ($goal['match_attribute'] == 'manually') { unset($goal['pattern']); unset($goal['pattern_type']); unset($goal['case_sensitive']); } $cleanedGoals[$goal['idgoal']] = $goal; } return $cleanedGoals; }
/** * Returns all Goals for a given website, or list of websites * * @param string|array $idSite Array or Comma separated list of website IDs to request the goals for * @return array Array of Goal attributes */ public function getGoals($idSite) { //TODO calls to this function could be cached as static // would help UI at least, since some UI requests would call this 2-3 times.. $idSite = Piwik_Site::getIdSitesFromIdSitesString($idSite); if (empty($idSite)) { return array(); } Piwik::checkUserHasViewAccess($idSite); $goals = Piwik_FetchAll("SELECT * \n\t\t\t\t\t\t\t\tFROM " . Piwik_Common::prefixTable('goal') . " \n\t\t\t\t\t\t\t\tWHERE idsite IN (" . implode(", ", $idSite) . ")\n\t\t\t\t\t\t\t\t\tAND deleted = 0"); $cleanedGoals = array(); foreach ($goals as &$goal) { if ($goal['match_attribute'] == 'manually') { unset($goal['pattern']); unset($goal['pattern_type']); unset($goal['case_sensitive']); } $cleanedGoals[$goal['idgoal']] = $goal; } return $cleanedGoals; }
/** * Set an access level to a given user for a list of websites ID. * * If access = 'noaccess' the current access (if any) will be deleted. * If access = 'view' or 'admin' the current access level is deleted and updated with the new value. * * @param string $userLogin The user login * @param string $access Access to grant. Must have one of the following value : noaccess, view, admin * @param int|array $idSites The array of idSites on which to apply the access level for the user. * If the value is "all" then we apply the access level to all the websites ID for which the current authentificated user has an 'admin' access. * * @throws Exception if the user doesn't exist * @throws Exception if the access parameter doesn't have a correct value * @throws Exception if any of the given website ID doesn't exist * * @return bool true on success */ public function setUserAccess($userLogin, $access, $idSites) { $this->checkAccessType($access); $this->checkUserExists($userLogin); $this->checkUserIsNotSuperUser($userLogin); if ($userLogin == 'anonymous' && $access == 'admin') { throw new Exception(Piwik_TranslateException("UsersManager_ExceptionAdminAnonymous")); } // in case idSites is null we grant access to all the websites on which the current connected user // has an 'admin' access if ($idSites === 'all') { $idSites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAdminAccess(); } elseif (!is_array($idSites)) { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); } // it is possible to set user access on websites only for the websites admin // basically an admin can give the view or the admin access to any user for the websites he manages Piwik::checkUserHasAdminAccess($idSites); $this->deleteUserAccess($userLogin, $idSites); // delete UserAccess $db = Zend_Registry::get('db'); // if the access is noaccess then we don't save it as this is the default value // when no access are specified if ($access != 'noaccess') { foreach ($idSites as $idsite) { $db->insert(Piwik_Common::prefixTable("access"), array("idsite" => $idsite, "login" => $userLogin, "access" => $access)); } } // we reload the access list which doesn't yet take in consideration this new user access Zend_Registry::get('access')->reloadAccess(); Piwik_Common::deleteTrackerCache(); }
/** * This method checks that the user has VIEW or ADMIN access for the given list of websites. * If the user doesn't have VIEW or ADMIN access for at least one website of the list, we throw an exception. * * @param int|arrayOfIntegers|string List of ID sites to check (integer, array of integers, string comma separated list of integers) * @throws Exception If for any of the websites the user doesn't have an VIEW or ADMIN access */ public function checkUserHasViewAccess( $idSites ) { if($idSites === 'all') { $idSites = $this->getSitesIdWithAtLeastViewAccess(); } if(!is_array($idSites)) { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); } $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess(); foreach($idSites as $idsite) { if(!in_array($idsite, $idSitesAccessible)) { throw new Piwik_Access_NoAccessException("You can't access this resource as it requires a 'view' access for the website id = $idsite."); } } }
/** * 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 * @return array */ public function getReportMetadata($idSites = '', $period = false, $date = false) { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); if (!empty($idSites)) { Piwik::checkUserHasViewAccess($idSites); } $parameters = array('idSites' => $idSites, 'period' => $period, 'date' => $date); $availableReports = array(); Piwik_PostEvent('API.getReportMetadata', $availableReports, $parameters); foreach ($availableReports as &$availableReport) { if (!isset($availableReport['metrics'])) { $availableReport['metrics'] = $this->getDefaultMetrics(); } if (!isset($availableReport['processedMetrics'])) { $availableReport['processedMetrics'] = $this->getDefaultProcessedMetrics(); } if (!isset($availableReport['metricsDocumentation'])) { $availableReport['metricsDocumentation'] = $this->getDefaultMetricsDocumentation(); } } // Some plugins need to add custom metrics after all plugins hooked in Piwik_PostEvent('API.getReportMetadata.end', $availableReports, $parameters); // Oh this is not pretty! Until we have event listeners order parameter... Piwik_PostEvent('API.getReportMetadata.end.end', $availableReports, $parameters); // Sort results to ensure consistent order usort($availableReports, array($this, 'sort')); // Add the magic API.get report metadata aggregating all plugins API.get API calls automatically $this->addApiGetMetdata($availableReports); $knownMetrics = array_merge($this->getDefaultMetrics(), $this->getDefaultProcessedMetrics()); foreach ($availableReports as &$availableReport) { // Ensure all metrics have a translation $metrics = $availableReport['metrics']; $cleanedMetrics = array(); 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; // 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 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']); } return $availableReports; }
/** * @param int|array|string $idSites * @return array * @throws Piwik_Access_NoAccessException */ protected function getIdSites($idSites) { if ($idSites === 'all') { $idSites = $this->getSitesIdWithAtLeastViewAccess(); } if (!is_array($idSites)) { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); } if (empty($idSites)) { throw new Piwik_Access_NoAccessException("The parameter 'idSite=' is missing from the request."); } return $idSites; }
/** * Builds an Archive object or returns the same archive if previously built. * * @param string|int idSite integer, or comma separated list of integer * @param string|Piwik_Date $date 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory() * @param string $period 'week' 'day' etc. * @param string Segment definition - defaults to false for Backward Compatibility * * @return Piwik_Archive */ public static function build($idSite, $period, $strDate, $segment = false) { if ($idSite === 'all') { $sites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess(); } else { $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite); } if (!$segment instanceof Piwik_Segment) { $segment = new Piwik_Segment($segment, $idSite); } // idSite=1,3 or idSite=all if (count($sites) > 1 || $idSite === 'all') { $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate, $segment); } elseif (is_string($strDate) && self::isMultiplePeriod($strDate, $period)) { $oSite = new Piwik_Site($idSite); $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate, $segment); } else { $oSite = new Piwik_Site($idSite); if ($period == 'range') { $oPeriod = new Piwik_Period_Range('range', $strDate, $oSite->getTimezone(), Piwik_Date::factory('today', $oSite->getTimezone())); } else { if (is_string($strDate)) { if ($strDate == 'now' || $strDate == 'today') { $strDate = date('Y-m-d', Piwik_Date::factory('now', $oSite->getTimezone())->getTimestamp()); } elseif ($strDate == 'yesterday' || $strDate == 'yesterdaySameTime') { $strDate = date('Y-m-d', Piwik_Date::factory('now', $oSite->getTimezone())->subDay(1)->getTimestamp()); } $oDate = Piwik_Date::factory($strDate); } else { $oDate = $strDate; } $date = $oDate->toString(); $oPeriod = Piwik_Period::factory($period, $oDate); } $archive = new Piwik_Archive_Single(); $archive->setPeriod($oPeriod); $archive->setSite($oSite); $archive->setSegment($segment); } return $archive; }
/** * Builds an Archive object or returns the same archive if previously built. * * @param string|int idSite integer, or comma separated list of integer * @param string|Piwik_Date $date 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory() * @param string $period 'week' 'day' etc. * * @return Piwik_Archive */ static public function build($idSite, $period, $strDate ) { if($idSite === 'all') { $sites = Piwik_SitesManager_API::getSitesIdWithAtLeastViewAccess(); } else { $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite); } // idSite=1,3 or idSite=all if( count($sites) > 1 || $idSite === 'all' ) { $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate); } // if a period date string is detected: either 'last30', 'previous10' or 'YYYY-MM-DD,YYYY-MM-DD' elseif(is_string($strDate) && ( preg_match('/^(last|previous){1}([0-9]*)$/', $strDate, $regs) || preg_match('/^([0-9]{4}-[0-9]{1,2}-[0-9]{1,2}),([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$/', $strDate, $regs) ) ) { $oSite = new Piwik_Site($idSite); $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate); } // case we request a single archive else { if(is_string($strDate)) { $oDate = Piwik_Date::factory($strDate); } else { $oDate = $strDate; } $date = $oDate->toString(); if(isset(self::$alreadyBuilt[$idSite][$date][$period])) { return self::$alreadyBuilt[$idSite][$date][$period]; } $oPeriod = Piwik_Period::factory($period, $oDate); $archive = new Piwik_Archive_Single(); $archive->setPeriod($oPeriod); $archive->setSite(new Piwik_Site($idSite)); $archiveJustProcessed = $archive->prepareArchive(); //we don't cache the archives just processed, the datatable were freed from memory if(!$archiveJustProcessed) { self::$alreadyBuilt[$idSite][$date][$period] = $archive; } } return $archive; }
/** * Builds an Archive object or returns the same archive if previously built. * * @param string|int idSite integer, or comma separated list of integer * @param string|Piwik_Date $date 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory() * @param string $period 'week' 'day' etc. * * @return Piwik_Archive */ public static function build($idSite, $period, $strDate) { if ($idSite === 'all') { $sites = Piwik_SitesManager_API::getSitesIdWithAtLeastViewAccess(); } else { $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite); } // idSite=1,3 or idSite=all if (count($sites) > 1 || $idSite === 'all') { require_once 'Archive/Array/IndexedBySite.php'; $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate); } elseif (is_string($strDate) && (ereg('^(last|previous){1}([0-9]*)$', $strDate, $regs) || ereg('^([0-9]{4}-[0-9]{1,2}-[0-9]{1,2}),([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$', $strDate, $regs))) { $oSite = new Piwik_Site($idSite); require_once 'Archive/Array/IndexedByDate.php'; $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate); } else { if (is_string($strDate)) { $oDate = Piwik_Date::factory($strDate); } else { $oDate = $strDate; } $date = $oDate->toString(); if (isset(self::$alreadyBuilt[$idSite][$date][$period])) { return self::$alreadyBuilt[$idSite][$date][$period]; } $oPeriod = Piwik_Period::factory($period, $oDate); $archive = new Piwik_Archive_Single(); $archive->setPeriod($oPeriod); $archive->setSite(new Piwik_Site($idSite)); $archiveJustProcessed = $archive->prepareArchive(); //we don't cache the archives just processed, the datatable were freed from memory if (!$archiveJustProcessed) { self::$alreadyBuilt[$idSite][$date][$period] = $archive; } } return $archive; }
/** * Builds an Archive object or returns the same archive if previously built. * * @param int|string $idSite integer, or comma separated list of integer * @param string $period 'week' 'day' etc. * @param Piwik_Date|string $strDate 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory() * @param bool|string $segment Segment definition - defaults to false for Backward Compatibility * @param bool|string $_restrictSitesToLogin Used only when running as a scheduled task * @return Piwik_Archive */ public static function build($idSite, $period, $strDate, $segment = false, $_restrictSitesToLogin = false) { if ($idSite === 'all') { $sites = Piwik_SitesManager_API::getInstance()->getSitesIdWithAtLeastViewAccess($_restrictSitesToLogin); } else { $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite); } if (!$segment instanceof Piwik_Segment) { $segment = new Piwik_Segment($segment, $idSite); } // idSite=1,3 or idSite=all if (count($sites) > 1 || $idSite === 'all') { $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate, $segment, $_restrictSitesToLogin); } elseif (is_string($strDate) && self::isMultiplePeriod($strDate, $period)) { $oSite = new Piwik_Site($idSite); $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate, $segment); } else { $oSite = new Piwik_Site($idSite); $oPeriod = Piwik_Archive::makePeriodFromQueryParams($oSite, $period, $strDate); $archive = new Piwik_Archive_Single(); $archive->setPeriod($oPeriod); $archive->setSite($oSite); $archive->setSegment($segment); } return $archive; }
/** * When tracking data in the past (using Tracking API), this function * can be used to invalidate reports for the idSites and dates where new data * was added. * DEV: If you call this API, the UI should display the data correctly, but will process * in real time, which could be very slow after large data imports. * After calling this function via REST, you can manually force all data * to be reprocessed by visiting the script as the Super User: * http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0 * We recommend to use an hourly schedule of the script at misc/cron/archive.php * More information: http://piwik.org/setup-auto-archiving/ * * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates * @param string $dates Comma separated list of dates to invalidate for all these websites * @return array */ public function invalidateArchivedReports($idSites, $dates) { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { throw new Exception("Specify a value for &idSites= as a comma separated list of website IDs, for which your token_auth has 'admin' permission"); } Piwik::checkUserHasAdminAccess($idSites); // Ensure the specified dates are valid $toInvalidate = $invalidDates = array(); $dates = explode(',', $dates); $dates = array_unique($dates); foreach ($dates as $theDate) { try { $date = Piwik_Date::factory($theDate); } catch (Exception $e) { $invalidDates[] = $theDate; continue; } if ($date->toString() == $theDate) { $toInvalidate[] = $date; } else { $invalidDates[] = $theDate; } } // Lookup archive tables $tables = Piwik::getTablesInstalled(); $archiveTables = Piwik::getTablesArchivesInstalled(); // If using the feature "Delete logs older than N days"... $logsAreDeletedBeforeThisDate = Piwik_Config::getInstance()->Deletelogs['delete_logs_schedule_lowest_interval']; $logsDeleteEnabled = Piwik_Config::getInstance()->Deletelogs['delete_logs_enable']; $minimumDateWithLogs = false; if ($logsDeleteEnabled && $logsAreDeletedBeforeThisDate) { $minimumDateWithLogs = Piwik_Date::factory('today')->subDay($logsAreDeletedBeforeThisDate); } // Given the list of dates, process which tables they should be deleted from $minDate = false; $warningDates = $processedDates = array(); /* @var $date Piwik_Date */ foreach ($toInvalidate as $date) { // we should only delete reports for dates that are more recent than N days if ($minimumDateWithLogs && $date->isEarlier($minimumDateWithLogs)) { $warningDates[] = $date->toString(); } else { $processedDates[] = $date->toString(); } $month = $date->toString('Y_m'); // For a given date, we must invalidate in the monthly archive table $datesByMonth[$month][] = $date->toString(); // But also the year stored in January $year = $date->toString('Y_01'); $datesByMonth[$year][] = $date->toString(); // but also weeks overlapping several months stored in the month where the week is starting /* @var $week Piwik_Period_Week */ $week = Piwik_Period::factory('week', $date); $week = $week->getDateStart()->toString('Y_m'); $datesByMonth[$week][] = $date->toString(); // Keep track of the minimum date for each website if ($minDate === false || $date->isEarlier($minDate)) { $minDate = $date; } } // In each table, invalidate day/week/month/year containing this date $sqlIdSites = implode(",", $idSites); foreach ($archiveTables as $table) { // Extract Y_m from table name $suffix = str_replace(array('archive_numeric_', 'archive_blob_'), '', Piwik_Common::unprefixTable($table)); if (!isset($datesByMonth[$suffix])) { continue; } // Dates which are to be deleted from this table $datesToDeleteInTable = $datesByMonth[$suffix]; // Build one statement to delete all dates from the given table $sql = $bind = array(); $datesToDeleteInTable = array_unique($datesToDeleteInTable); foreach ($datesToDeleteInTable as $dateToDelete) { $sql[] = '(date1 <= ? AND ? <= date2)'; $bind[] = $dateToDelete; $bind[] = $dateToDelete; } $sql = implode(" OR ", $sql); $query = "DELETE FROM {$table} " . " WHERE ( {$sql} ) " . " AND idsite IN (" . $sqlIdSites . ")"; Piwik_Query($query, $bind); // var_dump($query);var_dump($bind); } // Update piwik_site.ts_created $query = "UPDATE " . Piwik_Common::prefixTable("site") . " SET ts_created = ?" . " WHERE idsite IN ( {$sqlIdSites} )\n\t\t\t\t\tAND ts_created > ?"; $minDateSql = $minDate->subDay(1)->getDatetime(); $bind = array($minDateSql, $minDateSql); Piwik_Query($query, $bind); // var_dump($query);var_dump($bind); // Force to re-process data for these websites in the next archive.php cron run $invalidatedIdSites = Piwik_CoreAdminHome_API::getWebsiteIdsToInvalidate(); $invalidatedIdSites = array_merge($invalidatedIdSites, $idSites); $invalidatedIdSites = array_unique($invalidatedIdSites); $invalidatedIdSites = array_values($invalidatedIdSites); Piwik_SetOption(self::OPTION_INVALIDATED_IDSITES, serialize($invalidatedIdSites)); Piwik_Site::clearCache(); $output = array(); // output logs if ($warningDates) { $output[] = 'Warning: the following Dates have not been invalidated, because they are earlier than your Log Deletion limit: ' . implode(", ", $warningDates) . "\n The last day with logs is " . $minimumDateWithLogs . ". " . "\n Please disable 'Delete old Logs' or set it to a higher deletion threshold (eg. 180 days or 365 years).'."; } $output[] = "Success. The following dates were invalidated successfully: " . implode(", ", $processedDates); return $output; }
/** * This method checks that the user has VIEW or ADMIN access for the given list of websites. * If the user doesn't have VIEW or ADMIN access for at least one website of the list, we throw an exception. * * @param int|arrayOfIntegers|string List of ID sites to check (integer, array of integers, string comma separated list of integers) * @throws Exception If for any of the websites the user doesn't have an VIEW or ADMIN access */ public function checkUserHasViewAccess( $idSites ) { if($this->isSuperUser()) { return; } if($idSites === 'all') { $idSites = $this->getSitesIdWithAtLeastViewAccess(); } if(!is_array($idSites)) { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); } $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess(); foreach($idSites as $idsite) { if(!in_array($idsite, $idSitesAccessible)) { throw new Piwik_Access_NoAccessException(Piwik_TranslateException('General_ExceptionPrivilegeAccessWebsite', array("'view'", $idsite))); } } }
/** * 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 * @return array */ public function getReportMetadata($idSites = '') { $idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites); $availableReports = array(); Piwik_PostEvent('API.getReportMetadata', $availableReports, $idSites); foreach ($availableReports as &$availableReport) { if (!isset($availableReport['metrics'])) { $availableReport['metrics'] = $this->getDefaultMetrics(); } if (!isset($availableReport['processedMetrics'])) { $availableReport['processedMetrics'] = $this->getDefaultProcessedMetrics(); } } // Some plugins need to add custom metrics after all plugins hooked in Piwik_PostEvent('API.getReportMetadata.end', $availableReports, $idSites); // Sort results to ensure consistent order usort($availableReports, array($this, 'sort')); $knownMetrics = array_merge($this->getDefaultMetrics(), $this->getDefaultProcessedMetrics()); foreach ($availableReports as &$availableReport) { // Ensure all metrics have a translation $metrics = $availableReport['metrics']; $cleanedMetrics = array(); 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; // Remove array elements that are false (to clean up API output) foreach ($availableReport as $attributeName => $attributeValue) { if (empty($attributeValue)) { unset($availableReport[$attributeName]); } } // 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']); } return $availableReports; }