urlSparklineCountries = $this->getUrlSparkline('getLastDistinctCountriesGraph'); $view->numberDistinctCountries = $this->getNumberOfDistinctCountries(true); $view->dataTableCountry = $this->getCountry(true); $view->dataTableContinent = $this->getContinent(true); $view->dataTableRegion = $this->getRegion(true); $view->dataTableCity = $this->getCity(true); echo $view->render(); } function adminIndex() { Piwik::checkUserIsSuperUser(); $view = new Piwik_View('@UserCountry/adminIndex'); $allProviderInfo = Piwik_UserCountry_LocationProvider::getAllProviderInfo( $newline = '
', $includeExtra = true); $view->locationProviders = $allProviderInfo; $view->currentProviderId = Piwik_UserCountry_LocationProvider::getCurrentProviderId(); $view->thisIP = Piwik_IP::getIpFromHeader(); $geoIPDatabasesInstalled = Piwik_UserCountry_LocationProvider_GeoIp::isDatabaseInstalled(); $view->geoIPDatabasesInstalled = $geoIPDatabasesInstalled; // check if there is a working provider (that isn't the default one) $isThereWorkingProvider = false; foreach ($allProviderInfo as $id => $provider) { if ($id != Piwik_UserCountry_LocationProvider_Default::ID && $provider['status'] == Piwik_UserCountry_LocationProvider::INSTALLED ) { $isThereWorkingProvider = true; break; } } $view->isThereWorkingProvider = $isThereWorkingProvider; // if using either the Apache or PECL module, they are working and there are no databases // in misc, then the databases are located outside of Piwik, so we cannot update them $view->showGeoIPUpdateSection = true; $currentProviderId = Piwik_UserCountry_LocationProvider::getCurrentProviderId(); if (!$geoIPDatabasesInstalled && ($currentProviderId == Piwik_UserCountry_LocationProvider_GeoIp_ServerBased::ID || $currentProviderId == Piwik_UserCountry_LocationProvider_GeoIp_Pecl::ID) && $allProviderInfo[$currentProviderId]['status'] == Piwik_UserCountry_LocationProvider::INSTALLED ) { $view->showGeoIPUpdateSection = false; } $this->setUpdaterManageVars($view); $this->setBasicVariablesView($view); Piwik_Controller_Admin::setBasicVariablesAdminView($view); echo $view->render(); } /** * Starts or continues download of GeoLiteCity.dat. * * To avoid a server/PHP timeout & to show progress of the download to the user, we * use the HTTP Range header to download one chunk of the file at a time. After each * chunk, it is the browser's responsibility to call the method again to continue the download. * * Input: * 'continue' query param - if set to 1, will assume we are currently downloading & use * Range: HTTP header to get another chunk of the file. * * Output (in JSON): * 'current_size' - Current size of the partially downloaded file on disk. * 'expected_file_size' - The expected finished file size as returned by the HTTP server. * 'next_screen' - When the download finishes, this is the next screen that should be shown. * 'error' - When an error occurs, the message is returned in this property. */ public function downloadFreeGeoIPDB() { Piwik::checkUserIsSuperUser(); if ($_SERVER["REQUEST_METHOD"] == "POST") { $this->checkTokenInUrl(); Piwik_DataTable_Renderer_Json::sendHeaderJSON(); $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase('GeoIPCity.dat') . '.gz'; try { $result = Piwik_Http::downloadChunk( $url = Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL, $outputPath, $continue = Common::getRequestVar('continue', true, 'int') ); // if the file is done if ($result['current_size'] >= $result['expected_file_size']) { Piwik_UserCountry_GeoIPAutoUpdater::unzipDownloadedFile($outputPath, $unlink = true); // setup the auto updater Piwik_UserCountry_GeoIPAutoUpdater::setUpdaterOptions(array( 'loc_db' => Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL, 'period' => Piwik_UserCountry_GeoIPAutoUpdater::SCHEDULE_PERIOD_MONTHLY, )); // make sure to echo out the geoip updater management screen $result['next_screen'] = $this->getGeoIpUpdaterManageScreen(); } echo Common::json_encode($result); } catch (Exception $ex) { echo Common::json_encode(array('error' => $ex->getMessage())); } } } /** * Renders and returns the HTML that manages the GeoIP auto-updater. * * @return string */ private function getGeoIpUpdaterManageScreen() { $view = new Piwik_View('@UserCountry/getGeoIpUpdaterManageScreen'); $view->geoIPDatabasesInstalled = true; $this->setUpdaterManageVars($view); return $view->render(); } /** * Sets some variables needed by the _updaterManage.twig template. * * @param Piwik_View $view */ private function setUpdaterManageVars($view) { $urls = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrls(); $view->geoIPLocUrl = $urls['loc']; $view->geoIPIspUrl = $urls['isp']; $view->geoIPOrgUrl = $urls['org']; $view->geoIPUpdatePeriod = Piwik_UserCountry_GeoIPAutoUpdater::getSchedulePeriod(); $view->geoLiteUrl = Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL; $lastRunTime = Piwik_UserCountry_GeoIPAutoUpdater::getLastRunTime(); if ($lastRunTime !== false) { $view->lastTimeUpdaterRun = '' . $lastRunTime->toString() . ''; } } /** * Sets the URLs used to download new versions of the installed GeoIP databases. * * Input (query params): * 'loc_db' - URL for a GeoIP location database. * 'isp_db' - URL for a GeoIP ISP database (optional). * 'org_db' - URL for a GeoIP Org database (optional). * 'period' - 'weekly' or 'monthly'. Determines how often update is run. * * Output (json): * 'error' - if an error occurs its message is set as the resulting JSON object's * 'error' property. */ public function updateGeoIPLinks() { Piwik::checkUserIsSuperUser(); if ($_SERVER["REQUEST_METHOD"] == "POST") { Piwik_DataTable_Renderer_Json::sendHeaderJSON(); try { $this->checkTokenInUrl(); Piwik_UserCountry_GeoIPAutoUpdater::setUpdaterOptionsFromUrl(); // if there is a updater URL for a database, but its missing from the misc dir, tell // the browser so it can download it next $info = $this->getNextMissingDbUrlInfo(); if ($info !== false) { echo Common::json_encode($info); return; } else { echo 1; } } catch (Exception $ex) { echo Common::json_encode(array('error' => $ex->getMessage())); } } } /** * Starts or continues a download for a missing GeoIP database. A database is missing if * it has an update URL configured, but the actual database is not available in the misc * directory. * * Input: * 'url' - The URL to download the database from. * 'continue' - 1 if we're continuing a download, 0 if we're starting one. * * Output: * 'error' - If an error occurs this describes the error. * 'to_download' - The URL of a missing database that should be downloaded next (if any). * 'to_download_label' - The label to use w/ the progress bar that describes what we're * downloading. * 'current_size' - Size of the current file on disk. * 'expected_file_size' - Size of the completely downloaded file. */ public function downloadMissingGeoIpDb() { Piwik::checkUserIsSuperUser(); if ($_SERVER["REQUEST_METHOD"] == "POST") { try { $this->checkTokenInUrl(); Piwik_DataTable_Renderer_Json::sendHeaderJSON(); // based on the database type (provided by the 'key' query param) determine the // url & output file name $key = Common::getRequestVar('key', null, 'string'); $url = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrl($key); $ext = Piwik_UserCountry_GeoIPAutoUpdater::getGeoIPUrlExtension($url); $filename = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$key][0] . '.' . $ext; if (substr($filename, 0, 15) == 'GeoLiteCity.dat') { $filename = 'GeoIPCity.dat' . substr($filename, 15); } $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($filename); // download part of the file $result = Piwik_Http::downloadChunk( $url, $outputPath, Common::getRequestVar('continue', true, 'int')); // if the file is done if ($result['current_size'] >= $result['expected_file_size']) { Piwik_UserCountry_GeoIPAutoUpdater::unzipDownloadedFile($outputPath, $unlink = true); $info = $this->getNextMissingDbUrlInfo(); if ($info !== false) { echo Common::json_encode($info); return; } } echo Common::json_encode($result); } catch (Exception $ex) { echo Common::json_encode(array('error' => $ex->getMessage())); } } } /** * Sets the current LocationProvider type. * * Input: * Requires the 'id' query parameter to be set to the desired LocationProvider's ID. * * Output: * Nothing. */ public function setCurrentLocationProvider() { Piwik::checkUserIsSuperUser(); if ($_SERVER["REQUEST_METHOD"] == "POST") { $this->checkTokenInUrl(); $providerId = Common::getRequestVar('id'); $provider = Piwik_UserCountry_LocationProvider::setCurrentProvider($providerId); if ($provider === false) { throw new Exception("Invalid provider ID: '$providerId'."); } echo 1; } } /** * Echo's a pretty formatted location using a specific LocationProvider. * * Input: * The 'id' query parameter must be set to the ID of the LocationProvider to use. * * Output: * The pretty formatted location that was obtained. Will be HTML. */ public function getLocationUsingProvider() { $providerId = Common::getRequestVar('id'); $provider = $provider = Piwik_UserCountry_LocationProvider::getProviderById($providerId); if ($provider === false) { throw new Exception("Invalid provider ID: '$providerId'."); } $location = $provider->getLocation(array('ip' => Piwik_IP::getIpFromHeader(), 'lang' => Common::getBrowserLanguage(), 'disable_fallbacks' => true)); $location = Piwik_UserCountry_LocationProvider::prettyFormatLocation( $location, $newline = '
', $includeExtra = true); echo $location; } function getCountry($fetch = false) { $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getCountry"); $view->setLimit(5); $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Country')); $view->setReportDocumentation(Piwik_Translate('UserCountry_getCountryDocumentation')); return $this->renderView($view, $fetch); } function getContinent($fetch = false) { $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getContinent", 'table'); $view->disableSearchBox(); $view->disableOffsetInformationAndPaginationControls(); $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Continent')); $view->setReportDocumentation(Piwik_Translate('UserCountry_getContinentDocumentation')); return $this->renderView($view, $fetch); } /** * Echo's or returns an HTML view of the visits by region report. * * @param bool $fetch If true, returns the HTML as a string, otherwise it is echo'd. * @return string */ public function getRegion($fetch = false) { $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getRegion"); $view->setLimit(5); $view->setColumnTranslation('label', Piwik_Translate('UserCountry_Region')); $view->setReportDocumentation(Piwik_Translate('UserCountry_getRegionDocumentation') . '
' . $this->getGeoIPReportDocSuffix()); $this->checkIfNoDataForGeoIpReport($view); return $this->renderView($view, $fetch); } /** * Echo's or returns an HTML view of the visits by city report. * * @param bool $fetch If true, returns the HTML as a string, otherwise it is echo'd. * @return string */ public function getCity($fetch = false) { $view = $this->getStandardDataTableUserCountry(__FUNCTION__, "UserCountry.getCity"); $view->setLimit(5); $view->setColumnTranslation('label', Piwik_Translate('UserCountry_City')); $view->setReportDocumentation(Piwik_Translate('UserCountry_getCityDocumentation') . '
' . $this->getGeoIPReportDocSuffix()); $this->checkIfNoDataForGeoIpReport($view); return $this->renderView($view, $fetch); } private function getGeoIPReportDocSuffix() { return Piwik_Translate('UserCountry_GeoIPDocumentationSuffix', array( '', '', '', '' )); } protected function getStandardDataTableUserCountry($currentControllerAction, $APItoCall, $defaultDatatableType = null) { $view = Piwik_ViewDataTable::factory($defaultDatatableType); $view->init($this->pluginName, $currentControllerAction, $APItoCall); $view->disableExcludeLowPopulation(); $this->setPeriodVariablesView($view); $this->setMetricsVariablesView($view); $view->enableShowGoals(); return $view; } function getNumberOfDistinctCountries($fetch = false) { return $this->getNumericValue('UserCountry.getNumberOfDistinctCountries'); } function getLastDistinctCountriesGraph($fetch = false) { $view = $this->getLastUnitGraph('UserCountry', __FUNCTION__, "UserCountry.getNumberOfDistinctCountries"); $view->setColumnsToDisplay('UserCountry_distinctCountries'); return $this->renderView($view, $fetch); } /** * 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($view) { // only display on HTML tables since the datatable for HTML graphs aren't accessible if (!($view instanceof Piwik_ViewDataTable_HtmlTable)) { return; } // if there's only one row whose label is 'Unknown', display a message saying there's no data $view->main(); $dataTable = $view->getDataTable(); 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 (!$this->isGeoIPWorking()) { $params = array('module' => 'UserCountry', 'action' => 'adminIndex'); $footerMessage .= ' ' . Piwik_Translate('UserCountry_NoDataForGeoIPReport2', array( '', '', '', '' )); } else { $footerMessage .= ' ' . Piwik_Translate('UserCountry_ToGeolocateOldVisits', array( '', '' )); } // HACK! Can't use setFooterMessage because the view gets built in the main function, // so instead we set the property by hand. $realView = $view->getView(); $properties = $realView->properties; $properties['show_footer_message'] = $footerMessage; $realView->properties = $properties; } } /** * Gets information for the first missing GeoIP database (if any). * * @return bool */ private function getNextMissingDbUrlInfo() { $missingDbs = Piwik_UserCountry_GeoIPAutoUpdater::getMissingDatabases(); if (!empty($missingDbs)) { $missingDbKey = $missingDbs[0]; $missingDbName = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$missingDbKey][0]; $url = Piwik_UserCountry_GeoIPAutoUpdater::getConfiguredUrl($missingDbKey); $link = '' . $missingDbName . ''; return array( 'to_download' => $missingDbKey, 'to_download_label' => Piwik_Translate('UserCountry_DownloadingDb', $link) . '...', ); } return false; } /** * Returns true if a GeoIP provider is installed & working, false if otherwise. * * @return bool */ private function isGeoIPWorking() { $provider = Piwik_UserCountry_LocationProvider::getCurrentProvider(); return $provider instanceof Piwik_UserCountry_LocationProvider_GeoIp && $provider->isAvailable() === true && $provider->isWorking() === true; } }