Checks the given string for valid site IDs and returns them as an array.
public static getIdSitesFromIdSitesString ( string | array $ids, boolean | string $_restrictSitesToLogin = false ) : array | ||
$ids | string | array | Comma separated idSite list, eg, `'1,2,3,4'` or an array of IDs, eg, `array(1, 2, 3, 4)`. |
$_restrictSitesToLogin | boolean | string | Implementation detail. Used only when running as a scheduled task. |
리턴 | array | An array of valid, unique integers. |
/** * Return the number of visitors that visited every site in the given list for the * given date range. Includes the number of visitors that visited at least one site * and the number that visited every site. * * This data is calculated on demand, and for very large tables can take a long time * to run. * * See {@link Model\DistinctMetricsAggregator} for more information. * * @param string $idSite comma separated list of site IDs, ie, `"1,2,3"` * @param string $period * @param string $date * @return array Metrics **nb_total_visitors** and **nb_shared_visitors**. * @throws Exception if $idSite references zero sites or just one site. */ public function getCommonVisitors($idSite, $period, $date, $segment = false) { if (empty($idSite)) { throw new Exception("No sites to get common visitors for."); } $idSites = Site::getIdSitesFromIdSitesString($idSite); Piwik::checkUserHasViewAccess($idSites); $segment = new Segment($segment, $idSites); $period = PeriodFactory::build($period, $date); return $this->distinctMetricsAggregator->getCommonVisitorCount($idSites, $period->getDateStart(), $period->getDateEnd(), $segment); }
/** * 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. * 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 * @param string $period If specified (one of day, week, month, year, range) it will only invalidates archives for this period. * Note: because week, month, year, range reports aggregate day reports then you need to specifically invalidate day reports to see * other periods reports processed.. * @throws Exception * @return array * @hideExceptForSuperUser */ public function invalidateArchivedReports($idSites, $dates, $period = false) { $idSites = 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); $invalidator = new ArchiveInvalidator(); $output = $invalidator->markArchivesAsInvalidated($idSites, $dates, $period); Site::clearCache(); return $output; }
public static function checkUserHasAdminAccess($idSites) { if (!self::$superUser) { $websitesAccess = self::$idSitesAdmin; } else { $websitesAccess = API::getInstance()->getAllSitesId(); } $idSites = PiwikSite::getIdSitesFromIdSitesString($idSites); foreach ($idSites as $idsite) { if (!in_array($idsite, $websitesAccess)) { throw new NoAccessException("checkUserHasAdminAccess Fake exception // string not to be tested"); } } }
private function getSitesToInvalidateFor(InputInterface $input) { $sites = $input->getOption('sites'); $siteIds = Site::getIdSitesFromIdSitesString($sites); if (empty($siteIds)) { throw new \InvalidArgumentException("Invalid --sites value: '{$sites}'."); } $allSiteIds = SitesManagerAPI::getInstance()->getAllSitesId(); foreach ($siteIds as $idSite) { if (!in_array($idSite, $allSiteIds)) { throw new \InvalidArgumentException("Invalid --sites value: '{$sites}', there are no sites with IDs = {$idSite}"); } } return $siteIds; }
public function isSiteSearchEnabled($idSites, $idSite) { $idSites = Site::getIdSitesFromIdSitesString($idSites, true); if (!empty($idSite)) { $idSites[] = $idSite; } if (empty($idSites)) { return false; } foreach ($idSites as $idSite) { if (!Site::isSiteSearchEnabledFor($idSite)) { return false; } } return true; }
/** * 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. * 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 * @param string $period If specified (one of day, week, month, year, range) it will only invalidates archives for this period. * Note: because week, month, year, range reports aggregate day reports then you need to specifically invalidate day reports to see * other periods reports processed.. * @throws Exception * @return array * @hideExceptForSuperUser */ public function invalidateArchivedReports($idSites, $dates, $period = false) { $idSites = 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); list($dateObjects, $invalidDates) = $this->getDatesToInvalidateFromString($dates); $invalidator = new ArchiveInvalidator(); $output = $invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period); if ($invalidDates) { $output[] = 'Warning: some of the Dates to invalidate were invalid: ' . implode(", ", $invalidDates) . ". Piwik simply ignored those and proceeded with the others."; } Site::clearCache(); return $output; }
public function isSiteSearchEnabled() { $idSite = Common::getRequestVar('idSite', 0, 'int'); $idSites = Common::getRequestVar('idSites', '', 'string'); $idSites = Site::getIdSitesFromIdSitesString($idSites, true); if (!empty($idSite)) { $idSites[] = $idSite; } if (empty($idSites)) { return false; } foreach ($idSites as $idSite) { if (!Site::isSiteSearchEnabledFor($idSite)) { return false; } } return true; }
/** * 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) { $cacheId = self::getCacheId($idSite); $cache = $this->getGoalsInfoStaticCache(); if (!$cache->contains($cacheId)) { $idSite = Site::getIdSitesFromIdSitesString($idSite); if (empty($idSite)) { return array(); } Piwik::checkUserHasViewAccess($idSite); $goals = $this->getModel()->getActiveGoals($idSite); $cleanedGoals = array(); foreach ($goals as &$goal) { $cleanedGoals[$goal['idgoal']] = $this->formatGoal($goal); } $cache->save($cacheId, $cleanedGoals); } return $cache->fetch($cacheId); }
/** * 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 = Site::getIdSitesFromIdSitesString($idSite); if (empty($idSite)) { return array(); } Piwik::checkUserHasViewAccess($idSite); $goals = $this->getModel()->getActiveGoals($idSite); $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 = Site::getIdSitesFromIdSitesString($idSite); if (empty($idSite)) { return array(); } Piwik::checkUserHasViewAccess($idSite); $goals = Db::fetchAll("SELECT *\n\t\t\t\t\t\t\t\tFROM " . 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; }
/** * Invalidates report data, forcing it to be recomputed during the next archiving run. * * Note: This is done automatically when tracking or importing visits in the past. * * @param string $idSites Comma separated list of site IDs to invalidate reports for. * @param string $dates Comma separated list of dates of periods to invalidate reports for. * @param string|bool $period The type of period to invalidate: either 'day', 'week', 'month', 'year', 'range'. * The command will automatically cascade up, invalidating reports for parent periods as * well. So invalidating a day will invalidate the week it's in, the month it's in and the * year it's in, since those periods will need to be recomputed too. * @param string|bool $segment Optional. The segment to invalidate reports for. * @param bool $cascadeDown If true, child periods will be invalidated as well. So if it is requested to invalidate * a month, then all the weeks and days within that month will also be invalidated. But only * if this parameter is set. * @throws Exception * @return array * @hideExceptForSuperUser */ public function invalidateArchivedReports($idSites, $dates, $period = false, $segment = false, $cascadeDown = false) { $idSites = 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); if (!empty($segment)) { $segment = new Segment($segment, $idSites); } else { $segment = null; } list($dateObjects, $invalidDates) = $this->getDatesToInvalidateFromString($dates); $invalidationResult = $this->invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period, $segment, (bool) $cascadeDown); $output = $invalidationResult->makeOutputLogs(); if ($invalidDates) { $output[] = 'Warning: some of the Dates to invalidate were invalid: ' . implode(", ", $invalidDates) . ". Piwik simply ignored those and proceeded with the others."; } Site::clearCache(); // TODO: is this needed? it shouldn't be needed... return $invalidationResult->makeOutputLogs(); }
/** * 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) { $cacheId = self::getCacheId($idSite); $cache = $this->getGoalsInfoStaticCache(); if (!$cache->contains($cacheId)) { $idSite = Site::getIdSitesFromIdSitesString($idSite); if (empty($idSite)) { return array(); } Piwik::checkUserHasViewAccess($idSite); $goals = $this->getModel()->getActiveGoals($idSite); $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; } $cache->save($cacheId, $cleanedGoals); } return $cache->fetch($cacheId); }
/** * Updates the field ts_created for the specified websites. * * @param $idSites int Id Site to update ts_created * @param $minDate Date to set as creation date. To play it safe it will substract one more day. * * @ignore */ public function updateSiteCreatedTime($idSites, Date $minDate) { $idSites = Site::getIdSitesFromIdSitesString($idSites); Piwik::checkUserHasAdminAccess($idSites); $minDateSql = $minDate->subDay(1)->getDatetime(); $this->getModel()->updateSiteCreatedTime($idSites, $minDateSql); }
/** * @param int|array|string $idSites * @return array * @throws \Piwik\NoAccessException */ protected function getIdSites($idSites) { if ($idSites === 'all') { $idSites = $this->getSitesIdWithAtLeastViewAccess(); } $idSites = Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { throw new NoAccessException("The parameter 'idSite=' is missing from the request."); } return $idSites; }
/** * Initializes the various parameters to the script, based on input parameters. * */ private function initStateFromParameters() { $this->todayArchiveTimeToLive = Rules::getTodayArchiveTimeToLive(); $this->acceptInvalidSSLCertificate = $this->getParameterFromCli("accept-invalid-ssl-certificate"); $this->processPeriodsMaximumEverySeconds = $this->getDelayBetweenPeriodsArchives(); $this->shouldArchiveAllSites = (bool) $this->getParameterFromCli("force-all-websites"); $this->shouldStartProfiler = (bool) $this->getParameterFromCli("xhprof"); $restrictToIdSites = $this->getParameterFromCli("force-idsites", true); $skipIdSites = $this->getParameterFromCli("skip-idsites", true); $this->shouldArchiveSpecifiedSites = \Piwik\Site::getIdSitesFromIdSitesString($restrictToIdSites); $this->shouldSkipSpecifiedSites = \Piwik\Site::getIdSitesFromIdSitesString($skipIdSites); $this->lastSuccessRunTimestamp = Option::get(self::OPTION_ARCHIVING_FINISHED_TS); $this->shouldArchiveOnlySitesWithTrafficSince = $this->isShouldArchiveAllSitesWithTrafficSince(); $this->shouldArchiveOnlySpecificPeriods = $this->getPeriodsToProcess(); if ($this->shouldArchiveOnlySitesWithTrafficSince === false) { // force-all-periods is not set here if (empty($this->lastSuccessRunTimestamp)) { // First time we run the script $this->shouldArchiveOnlySitesWithTrafficSince = self::ARCHIVE_SITES_WITH_TRAFFIC_SINCE; } else { // there was a previous successful run $this->shouldArchiveOnlySitesWithTrafficSince = time() - $this->lastSuccessRunTimestamp; } } else { // force-all-periods is set here $this->archiveAndRespectTTL = false; if ($this->shouldArchiveOnlySitesWithTrafficSince === true) { // force-all-periods without value $this->shouldArchiveOnlySitesWithTrafficSince = self::ARCHIVE_SITES_WITH_TRAFFIC_SINCE; } } }
private function updateSiteCreatedTime($idSites, Date $minDate) { $idSites = Site::getIdSitesFromIdSitesString($idSites); $minDateSql = $minDate->subDay(1)->getDatetime(); $model = new SitesManagerModel(); $model->updateSiteCreatedTime($idSites, $minDateSql); }
/** * Creates a UserSynchronizer using INI configuration. * * @return UserSynchronizer */ public static function makeConfigured() { $result = new UserSynchronizer(); $result->setUserMapper(UserMapper::makeConfigured()); $result->setUsersManagerApi(UsersManagerAPI::getInstance()); $result->setUserModel(new UserModel()); if (Config::isAccessSynchronizationEnabled()) { $result->setUserAccessMapper(UserAccessMapper::makeConfigured()); Log::debug("UserSynchronizer::%s(): Using UserAccessMapper when synchronizing users.", __FUNCTION__); } else { Log::debug("UserSynchronizer::%s(): LDAP access synchronization not enabled.", __FUNCTION__); } $defaultSitesWithViewAccess = Config::getDefaultSitesToGiveViewAccessTo(); if (!empty($defaultSitesWithViewAccess)) { $siteIds = Access::doAsSuperUser(function () use($defaultSitesWithViewAccess) { return Site::getIdSitesFromIdSitesString($defaultSitesWithViewAccess); }); if (empty($siteIds)) { Log::warning("UserSynchronizer::%s(): new_user_default_sites_view_access INI config option has no " . "entries. Newly synchronized users will not have any access.", __FUNCTION__); } $result->setNewUserDefaultSitesWithViewAccess($siteIds); } Log::debug("UserSynchronizer::%s: configuring with defaultSitesWithViewAccess = %s", __FUNCTION__, $defaultSitesWithViewAccess); return $result; }
private function updateSiteCreatedTime($idSites, Date $minDate) { $idSites = Site::getIdSitesFromIdSitesString($idSites); $minDateSql = $minDate->subDay(1)->getDatetime(); #$model = new SitesManagerModel(); $model = Factory::getModel('Piwik\\Plugins\\SitesManager'); $model->updateSiteCreatedTime($idSites, $minDateSql); }
/** * 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 }
/** * Returns list of int site IDs from site list found in LDAP. * * @param string $sitesSpec eg, `"1,2,3"` or `"all"` * @return int[] */ protected function getSitesFromSitesList($sitesSpec) { return Access::doAsSuperUser(function () use($sitesSpec) { return Site::getIdSitesFromIdSitesString($sitesSpec); }); }
/** * Returns all annotations within a specific date range. The result is * an array that maps site IDs with arrays of annotations within the range. * * Note: The date range is inclusive. * * @see self::get for info on what attributes stored within annotations. * * @param Date|bool $startDate The start of the date range. * @param Date|bool $endDate The end of the date range. * @param array|bool|int|string $idSite IDs of the sites whose annotations to * search through. * @return array Array mapping site IDs with arrays of annotations, eg: * array( * '5' => array( * array(...), // annotation * array(...), // annotation * ... * ), * '6' => array( * array(...), // annotation * array(...), // annotation * ... * ), * ) */ public function search($startDate, $endDate, $idSite = false) { if ($idSite) { $idSites = Site::getIdSitesFromIdSitesString($idSite); } else { $idSites = array_keys($this->annotations); } // collect annotations that are within the right date range & belong to the right // site $result = array(); foreach ($idSites as $idSite) { if (!isset($this->annotations[$idSite])) { continue; } foreach ($this->annotations[$idSite] as $idNote => $annotation) { if ($startDate !== false) { $annotationDate = Date::factory($annotation['date']); if ($annotationDate->getTimestamp() < $startDate->getTimestamp() || $annotationDate->getTimestamp() > $endDate->getTimestamp()) { continue; } } $this->augmentAnnotationData($idSite, $idNote, $annotation); $result[$idSite][] = $annotation; } // sort by annotation date if (!empty($result[$idSite])) { uasort($result[$idSite], array($this, 'compareAnnotationDate')); } } return $result; }
/** * Returns a new Archive instance that will query archive data for the given set of * sites and periods, using an optional Segment. * * This method uses data that is found in query parameters, so the parameters to this * function can be string values. * * If you want to create an Archive instance with an array of Period instances, use * {@link Archive::factory()}. * * @param string|int|array $idSites A single ID (eg, `'1'`), multiple IDs (eg, `'1,2,3'` or `array(1, 2, 3)`), * or `'all'`. * @param string $period 'day', `'week'`, `'month'`, `'year'` or `'range'` * @param Date|string $strDate 'YYYY-MM-DD', magic keywords (ie, 'today'; {@link Date::factory()} * or date range (ie, 'YYYY-MM-DD,YYYY-MM-DD'). * @param bool|false|string $segment Segment definition or false if no segment should be used. {@link Piwik\Segment} * @param bool|false|string $_restrictSitesToLogin Used only when running as a scheduled task. * @param bool $skipAggregationOfSubTables Whether the archive, when it is processed, should also aggregate all sub-tables * @return Archive */ public static function build($idSites, $period, $strDate, $segment = false, $_restrictSitesToLogin = false, $skipAggregationOfSubTables = false) { $websiteIds = Site::getIdSitesFromIdSitesString($idSites, $_restrictSitesToLogin); if (Period::isMultiplePeriod($strDate, $period)) { $oPeriod = Factory::build($period, $strDate); $allPeriods = $oPeriod->getSubperiods(); } else { $timezone = count($websiteIds) == 1 ? Site::getTimezoneFor($websiteIds[0]) : false; $oPeriod = Factory::makePeriodFromQueryParams($timezone, $period, $strDate); $allPeriods = array($oPeriod); } $segment = new Segment($segment, $websiteIds); $idSiteIsAll = $idSites == self::REQUEST_ALL_WEBSITES_FLAG; $isMultipleDate = Period::isMultiplePeriod($strDate, $period); return Archive::factory($segment, $allPeriods, $websiteIds, $idSiteIsAll, $isMultipleDate, $skipAggregationOfSubTables); }
/** * Updates the field ts_created for the specified websites. * * @param $idSites int Id Site to update ts_created * @param $minDate Date to set as creation date. To play it safe it will substract one more day. * * @ignore */ public function updateSiteCreatedTime($idSites, Date $minDate) { $idSites = Site::getIdSitesFromIdSitesString($idSites); Piwik::checkUserHasAdminAccess($idSites); // Update piwik_site.ts_created $query = "UPDATE " . Common::prefixTable("site") . " SET ts_created = ?" . " WHERE idsite IN ( " . implode(",", $idSites) . " )\n\t\t\t\t\tAND ts_created > ?"; $minDateSql = $minDate->subDay(1)->getDatetime(); $bind = array($minDateSql, $minDateSql); Db::query($query, $bind); }
/** * 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::translate("UsersManager_ExceptionAdminAnonymous")); } // in case idSites is all we grant access to all the websites on which the current connected user has an 'admin' access if ($idSites === 'all') { $idSites = \Piwik\Plugins\SitesManager\API::getInstance()->getSitesIdWithAdminAccess(); } else { $idSites = Site::getIdSitesFromIdSitesString($idSites); } if (empty($idSites)) { throw new Exception('Specify at least one website ID in &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 = Db::get(); // 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(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 Access::getInstance()->reloadAccess(); Cache::deleteTrackerCache(); }
/** * 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. * 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 * @throws Exception * @return array */ public function invalidateArchivedReports($idSites, $dates) { $idSites = 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(',', trim($dates)); $dates = array_unique($dates); foreach ($dates as $theDate) { $theDate = trim($theDate); try { $date = Date::factory($theDate); } catch (Exception $e) { $invalidDates[] = $theDate; continue; } if ($date->toString() == $theDate) { $toInvalidate[] = $date; } else { $invalidDates[] = $theDate; } } // If using the feature "Delete logs older than N days"... $purgeDataSettings = PrivacyManager::getPurgeDataSettings(); $logsAreDeletedBeforeThisDate = $purgeDataSettings['delete_logs_schedule_lowest_interval']; $logsDeleteEnabled = $purgeDataSettings['delete_logs_enable']; $minimumDateWithLogs = false; if ($logsDeleteEnabled && $logsAreDeletedBeforeThisDate) { $minimumDateWithLogs = 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 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 Week */ $week = Period\Factory::build('week', $date); $weekAsString = $week->getDateStart()->toString('Y_m'); $datesByMonth[$weekAsString][] = $date->toString(); // Keep track of the minimum date for each website if ($minDate === false || $date->isEarlier($minDate)) { $minDate = $date; } } if (empty($minDate)) { throw new Exception("Check the 'dates' parameter is a valid date."); } // In each table, invalidate day/week/month/year containing this date $archiveTables = ArchiveTableCreator::getTablesArchivesInstalled(); foreach ($archiveTables as $table) { // Extract Y_m from table name $suffix = ArchiveTableCreator::getDateFromTableName($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 (" . implode(",", $idSites) . ")"; Db::query($query, $bind); } \Piwik\Plugins\SitesManager\API::getInstance()->updateSiteCreatedTime($idSites, $minDate); // Force to re-process data for these websites in the next cron core:archive command run $invalidatedIdSites = self::getWebsiteIdsToInvalidate(); $invalidatedIdSites = array_merge($invalidatedIdSites, $idSites); $invalidatedIdSites = array_unique($invalidatedIdSites); $invalidatedIdSites = array_values($invalidatedIdSites); Option::set(self::OPTION_INVALIDATED_IDSITES, serialize($invalidatedIdSites)); 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; }
/** * Get triggered alerts. * * @param int[] idSites * * @return array */ public function getTriggeredAlerts($idSites) { if (empty($idSites)) { return array(); } $idSites = Site::getIdSitesFromIdSitesString($idSites); Piwik::checkUserHasViewAccess($idSites); $login = Piwik::getCurrentUserLogin(); return $this->getModel()->getTriggeredAlerts($idSites, $login); }
private static function getSitesListOption(InputInterface $input, $optionName) { return Site::getIdSitesFromIdSitesString($input->getOption($optionName)); }
/** * Creates a UserSynchronizer using INI configuration. * * @return UserSynchronizer */ public static function makeConfigured() { $result = new UserSynchronizer(); $result->setUserMapper(UserMapper::makeConfigured()); $result->setUsersManagerApi(UsersManagerAPI::getInstance()); $result->setUserModel(new UserModel()); /** @var LoggerInterface $logger */ $logger = StaticContainer::get('Psr\\Log\\LoggerInterface'); if (Config::isAccessSynchronizationEnabled()) { $result->setUserAccessMapper(UserAccessMapper::makeConfigured()); $logger->debug("UserSynchronizer::{func}(): Using UserAccessMapper when synchronizing users.", array('func' => __FUNCTION__)); } else { $logger->debug("UserSynchronizer::{func}(): LDAP access synchronization not enabled.", array('func' => __FUNCTION__)); } $defaultSitesWithViewAccess = Config::getDefaultSitesToGiveViewAccessTo(); if (!empty($defaultSitesWithViewAccess)) { $siteIds = Access::doAsSuperUser(function () use($defaultSitesWithViewAccess) { return Site::getIdSitesFromIdSitesString($defaultSitesWithViewAccess); }); if (empty($siteIds)) { $logger->warning("UserSynchronizer::{func}(): new_user_default_sites_view_access INI config option has no " . "entries. Newly synchronized users will not have any access.", array('func' => __FUNCTION__)); } $result->setNewUserDefaultSitesWithViewAccess($siteIds); } $logger->debug("UserSynchronizer::{func}: configuring with defaultSitesWithViewAccess = {sites}", array('func' => __FUNCTION__, 'sites' => $defaultSitesWithViewAccess)); return $result; }