'addWidgets', 'Menu.Reporting.addItems' => 'addMenu', 'Menu.Admin.addItems' => 'addAdminMenu', 'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics', 'API.getReportMetadata' => 'getReportMetadata', 'API.getSegmentDimensionMetadata' => 'getSegmentsMetadata', 'AssetManager.getStylesheetFiles' => 'getStylesheetFiles', 'AssetManager.getJavaScriptFiles' => 'getJsFiles', 'Tracker.newVisitorInformation' => 'enrichVisitWithLocation', 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks', 'ViewDataTable.configure' => 'configureViewDataTable', 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys', 'Tracker.setTrackerCacheGeneral' => 'setTrackerCacheGeneral', 'Insights.addReportToOverview' => 'addReportToInsightsOverview' ); return $hooks; } public function addReportToInsightsOverview(&$reports) { $reports['UserCountry_getCountry'] = array(); } public function setTrackerCacheGeneral(&$cache) { $cache['currentLocationProviderId'] = LocationProvider::getCurrentProviderId(); } public function getScheduledTasks(&$tasks) { // add the auto updater task if GeoIP admin is enabled if($this->isGeoLocationAdminEnabled()) { $tasks[] = new GeoIPAutoUpdater(); } } public function getStylesheetFiles(&$stylesheets) { $stylesheets[] = "plugins/UserCountry/stylesheets/userCountry.less"; } public function getJsFiles(&$jsFiles) { $jsFiles[] = "plugins/UserCountry/javascripts/userCountry.js"; } public function enrichVisitWithLocation(&$visitorInfo, \Piwik\Tracker\Request $request) { require_once PIWIK_INCLUDE_PATH . "/plugins/UserCountry/LocationProvider.php"; $privacyConfig = new PrivacyManagerConfig(); $ipAddress = IP::N2P($privacyConfig->useAnonymizedIpForVisitEnrichment ? $visitorInfo['location_ip'] : $request->getIp()); $userInfo = array( 'lang' => $visitorInfo['location_browser_lang'], 'ip' => $ipAddress ); $id = Common::getCurrentLocationProviderId(); $provider = LocationProvider::getProviderById($id); if ($provider === false) { $id = DefaultProvider::ID; $provider = LocationProvider::getProviderById($id); Common::printDebug("GEO: no current location provider sent, falling back to default '$id' one."); } $location = $provider->getLocation($userInfo); // if we can't find a location, use default provider if ($location === false) { $defaultId = DefaultProvider::ID; $provider = LocationProvider::getProviderById($defaultId); $location = $provider->getLocation($userInfo); Common::printDebug("GEO: couldn't find a location with Geo Module '$id', using Default '$defaultId' provider as fallback..."); $id = $defaultId; } Common::printDebug("GEO: Found IP $ipAddress location (provider '" . $id . "'): " . var_export($location, true)); if (empty($location['country_code'])) { // sanity check $location['country_code'] = \Piwik\Tracker\Visit::UNKNOWN_CODE; } // add optional location components $this->updateVisitInfoWithLocation($visitorInfo, $location); } /** * Sets visitor info array with location info. * * @param array $visitorInfo * @param array $location See LocationProvider::getLocation for more info. */ private function updateVisitInfoWithLocation(&$visitorInfo, $location) { static $logVisitToLowerLocationMapping = array( 'location_country' => LocationProvider::COUNTRY_CODE_KEY, ); static $logVisitToLocationMapping = array( 'location_region' => LocationProvider::REGION_CODE_KEY, 'location_city' => LocationProvider::CITY_NAME_KEY, 'location_latitude' => LocationProvider::LATITUDE_KEY, 'location_longitude' => LocationProvider::LONGITUDE_KEY, ); foreach ($logVisitToLowerLocationMapping as $column => $locationKey) { if (!empty($location[$locationKey])) { $visitorInfo[$column] = strtolower($location[$locationKey]); } } foreach ($logVisitToLocationMapping as $column => $locationKey) { if (!empty($location[$locationKey])) { $visitorInfo[$column] = $location[$locationKey]; } } // if the location has provider/organization info, set it if (!empty($location[LocationProvider::ISP_KEY])) { $providerValue = $location[LocationProvider::ISP_KEY]; // if the org is set and not the same as the isp, add it to the provider value if (!empty($location[LocationProvider::ORG_KEY]) && $location[LocationProvider::ORG_KEY] != $providerValue ) { $providerValue .= ' - ' . $location[LocationProvider::ORG_KEY]; } } else if (!empty($location[LocationProvider::ORG_KEY])) { $providerValue = $location[LocationProvider::ORG_KEY]; } if (isset($providerValue) && Manager::getInstance()->isPluginInstalled('Provider')) { $visitorInfo['location_provider'] = $providerValue; } } public function addWidgets() { $widgetContinentLabel = Piwik::translate('UserCountry_WidgetLocation') . ' (' . Piwik::translate('UserCountry_Continent') . ')'; $widgetCountryLabel = Piwik::translate('UserCountry_WidgetLocation') . ' (' . Piwik::translate('UserCountry_Country') . ')'; $widgetRegionLabel = Piwik::translate('UserCountry_WidgetLocation') . ' (' . Piwik::translate('UserCountry_Region') . ')'; $widgetCityLabel = Piwik::translate('UserCountry_WidgetLocation') . ' (' . Piwik::translate('UserCountry_City') . ')'; WidgetsList::add('General_Visitors', $widgetContinentLabel, 'UserCountry', 'getContinent'); WidgetsList::add('General_Visitors', $widgetCountryLabel, 'UserCountry', 'getCountry'); WidgetsList::add('General_Visitors', $widgetRegionLabel, 'UserCountry', 'getRegion'); WidgetsList::add('General_Visitors', $widgetCityLabel, 'UserCountry', 'getCity'); } public function addMenu() { MenuMain::getInstance()->add('General_Visitors', 'UserCountry_SubmenuLocations', array('module' => 'UserCountry', 'action' => 'index')); } /** * Event handler. Adds menu items to the MenuAdmin menu. */ public function addAdminMenu() { if($this->isGeoLocationAdminEnabled()) { MenuAdmin::getInstance()->add('General_Settings', 'UserCountry_Geolocation', array('module' => 'UserCountry', 'action' => 'adminIndex'), Piwik::hasUserSuperUserAccess(), $order = 8); } } public function getSegmentsMetadata(&$segments) { $segments[] = array( 'type' => 'dimension', 'category' => 'Visit Location', 'name' => Piwik::translate('UserCountry_Country'), 'segment' => 'countryCode', 'sqlSegment' => 'log_visit.location_country', 'acceptedValues' => 'de, us, fr, in, es, etc.', ); $segments[] = array( 'type' => 'dimension', 'category' => 'Visit Location', 'name' => Piwik::translate('UserCountry_Continent'), 'segment' => 'continentCode', 'sqlSegment' => 'log_visit.location_country', 'acceptedValues' => 'eur, asi, amc, amn, ams, afr, ant, oce', 'sqlFilter' => __NAMESPACE__ . '\UserCountry::getCountriesForContinent', ); $segments[] = array( 'type' => 'dimension', 'category' => 'Visit Location', 'name' => Piwik::translate('UserCountry_Region'), 'segment' => 'regionCode', 'sqlSegment' => 'log_visit.location_region', 'acceptedValues' => '01 02, OR, P8, etc.
eg. region=A1;country=fr', ); $segments[] = array( 'type' => 'dimension', 'category' => 'Visit Location', 'name' => Piwik::translate('UserCountry_City'), 'segment' => 'city', 'sqlSegment' => 'log_visit.location_city', 'acceptedValues' => 'Sydney, Sao Paolo, Rome, etc.', ); $segments[] = array( 'type' => 'dimension', 'category' => 'Visit Location', 'name' => Piwik::translate('UserCountry_Latitude'), 'segment' => 'latitude', 'sqlSegment' => 'log_visit.location_latitude', 'acceptedValues' => '-33.578, 40.830, etc.
You can select visitors within a lat/long range using &segment=lat>X;lat<Y;long>M;long<N.', ); $segments[] = array( 'type' => 'dimension', 'category' => 'Visit Location', 'name' => Piwik::translate('UserCountry_Longitude'), 'segment' => 'longitude', 'sqlSegment' => 'log_visit.location_longitude', 'acceptedValues' => '-70.664, 14.326, etc.', ); } public function getReportMetadata(&$reports) { $metrics = array( 'nb_visits' => Piwik::translate('General_ColumnNbVisits'), 'nb_uniq_visitors' => Piwik::translate('General_ColumnNbUniqVisitors'), 'nb_actions' => Piwik::translate('General_ColumnNbActions'), ); $reports[] = array( 'category' => Piwik::translate('General_Visitors'), 'name' => Piwik::translate('UserCountry_Country'), 'module' => 'UserCountry', 'action' => 'getCountry', 'dimension' => Piwik::translate('UserCountry_Country'), 'metrics' => $metrics, 'order' => 5, ); $reports[] = array( 'category' => Piwik::translate('General_Visitors'), 'name' => Piwik::translate('UserCountry_Continent'), 'module' => 'UserCountry', 'action' => 'getContinent', 'dimension' => Piwik::translate('UserCountry_Continent'), 'metrics' => $metrics, 'order' => 6, ); $reports[] = array( 'category' => Piwik::translate('General_Visitors'), 'name' => Piwik::translate('UserCountry_Region'), 'module' => 'UserCountry', 'action' => 'getRegion', 'dimension' => Piwik::translate('UserCountry_Region'), 'metrics' => $metrics, 'order' => 7, ); $reports[] = array( 'category' => Piwik::translate('General_Visitors'), 'name' => Piwik::translate('UserCountry_City'), 'module' => 'UserCountry', 'action' => 'getCity', 'dimension' => Piwik::translate('UserCountry_City'), 'metrics' => $metrics, 'order' => 8, ); } public function getReportsWithGoalMetrics(&$dimensions) { $dimensions = array_merge($dimensions, array( array('category' => Piwik::translate('General_Visit'), 'name' => Piwik::translate('UserCountry_Country'), 'module' => 'UserCountry', 'action' => 'getCountry', ), array('category' => Piwik::translate('General_Visit'), 'name' => Piwik::translate('UserCountry_Continent'), 'module' => 'UserCountry', 'action' => 'getContinent', ), array('category' => Piwik::translate('General_Visit'), 'name' => Piwik::translate('UserCountry_Region'), 'module' => 'UserCountry', 'action' => 'getRegion'), array('category' => Piwik::translate('General_Visit'), 'name' => Piwik::translate('UserCountry_City'), 'module' => 'UserCountry', 'action' => 'getCity'), )); } /** * Returns a list of country codes for a given continent code. * * @param string $continent The continent code. * @return array */ public static function getCountriesForContinent($continent) { $result = array(); $continent = strtolower($continent); foreach (Common::getCountriesList() as $countryCode => $continentCode) { if ($continent == $continentCode) { $result[] = $countryCode; } } return array('SQL' => "'" . implode("', '", $result) . "', ?", 'bind' => '-'); // HACK: SegmentExpression requires a $bind, even if there's nothing to bind } public function configureViewDataTable(ViewDataTable $view) { switch ($view->requestConfig->apiMethodToRequestDataTable) { case 'UserCountry.getCountry': $this->configureViewForGetCountry($view); break; case 'UserCountry.getContinent': $this->configureViewForGetContinent($view); break; case 'UserCountry.getRegion': $this->configureViewForGetRegion($view); break; case 'UserCountry.getCity': $this->configureViewForGetCity($view); break; } } private function configureViewForGetCountry(ViewDataTable $view) { $view->config->show_goals = true; $view->config->show_exclude_low_population = false; $view->config->addTranslation('label', Piwik::translate('UserCountry_Country')); $view->config->documentation = Piwik::translate('UserCountry_getCountryDocumentation'); $view->requestConfig->filter_limit = 5; if (LocationProvider::getCurrentProviderId() == DefaultProvider::ID) { // if we're using the default location provider, add a note explaining how it works $footerMessage = Piwik::translate("General_Note") . ': ' . Piwik::translate('UserCountry_DefaultLocationProviderExplanation', array('', '')); $view->config->show_footer_message = $footerMessage; } } private function configureViewForGetContinent(ViewDataTable $view) { $view->config->show_exclude_low_population = false; $view->config->show_goals = true; $view->config->show_search = false; $view->config->show_offset_information = false; $view->config->show_pagination_control = false; $view->config->show_limit_control = false; $view->config->documentation = Piwik::translate('UserCountry_getContinentDocumentation'); $view->config->addTranslation('label', Piwik::translate('UserCountry_Continent')); } private function configureViewForGetRegion(ViewDataTable $view) { $view->config->show_exclude_low_population = false; $view->config->show_goals = true; $view->config->documentation = Piwik::translate('UserCountry_getRegionDocumentation') . '
' . $this->getGeoIPReportDocSuffix(); $view->config->addTranslation('label', Piwik::translate('UserCountry_Region')); $view->requestConfig->filter_limit = 5; $this->checkIfNoDataForGeoIpReport($view); } private function configureViewForGetCity(ViewDataTable $view) { $view->config->show_exclude_low_population = false; $view->config->show_goals = true; $view->config->documentation = Piwik::translate('UserCountry_getCityDocumentation') . '
' . $this->getGeoIPReportDocSuffix(); $view->config->addTranslation('label', Piwik::translate('UserCountry_City')); $view->requestConfig->filter_limit = 5; $this->checkIfNoDataForGeoIpReport($view); } private function getGeoIPReportDocSuffix() { return Piwik::translate('UserCountry_GeoIPDocumentationSuffix', array('', '', '', '') ); } /** * Checks if a datatable for a view is empty and if so, displays a message in the footer * telling users to configure GeoIP. */ private function checkIfNoDataForGeoIpReport(ViewDataTable $view) { $self = $this; $view->config->filters[] = function ($dataTable) use ($self, $view) { // if there's only one row whose label is 'Unknown', display a message saying there's no data if ($dataTable->getRowsCount() == 1 && $dataTable->getFirstRow()->getColumn('label') == Piwik::translate('General_Unknown') ) { $footerMessage = Piwik::translate('UserCountry_NoDataForGeoIPReport1'); // if GeoIP is working, don't display this part of the message if (!$self->isGeoIPWorking()) { $params = array('module' => 'UserCountry', 'action' => 'adminIndex'); $footerMessage .= ' ' . Piwik::translate('UserCountry_NoDataForGeoIPReport2', array('', '', '', '')); } else { $footerMessage .= ' ' . Piwik::translate('UserCountry_ToGeolocateOldVisits', array('', '')); } $view->config->show_footer_message = $footerMessage; } }; } /** * Returns true if a GeoIP provider is installed & working, false if otherwise. * * @return bool */ public function isGeoIPWorking() { $provider = LocationProvider::getCurrentProvider(); return $provider instanceof GeoIp && $provider->isAvailable() === true && $provider->isWorking() === true; } public function getClientSideTranslationKeys(&$translationKeys) { $translationKeys[] = "UserCountry_FatalErrorDuringDownload"; $translationKeys[] = "UserCountry_SetupAutomaticUpdatesOfGeoIP"; $translationKeys[] = "General_Done"; } public static function isGeoLocationAdminEnabled() { return (bool) Config::getInstance()->General['enable_geolocation_admin']; } }