Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Giehl <stefan@matomo.org>2020-02-27 10:23:17 +0300
committerGitHub <noreply@github.com>2020-02-27 10:23:17 +0300
commitf28c7fa6cb6c63c8f459206448c7dcb93568099e (patch)
tree55168edfd9c5e2802bf6c0ace2fed4584cb79f52 /plugins/GeoIp2
parentbcbdf42154ff40af6ce8276234ffbae19e0370d4 (diff)
Removes GeoIp Legacy support (#15521)
* Removes GeoIp Legacy support * move rendering provider configuration to the provider, to allow other providers to define an own one * move related translations to GeoIp2 plugin * Adds some UI tests * Apply review feedback
Diffstat (limited to 'plugins/GeoIp2')
-rw-r--r--plugins/GeoIp2/Columns/Region.php20
-rw-r--r--plugins/GeoIp2/Controller.php200
-rw-r--r--plugins/GeoIp2/GeoIP2AutoUpdater.php25
-rw-r--r--plugins/GeoIp2/GeoIp2.php23
-rw-r--r--plugins/GeoIp2/LocationProvider/GeoIp2/Php.php73
-rw-r--r--plugins/GeoIp2/LocationProvider/GeoIp2/ServerModule.php18
-rw-r--r--plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.controller.js149
-rw-r--r--plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.directive.js37
-rw-r--r--plugins/GeoIp2/lang/en.json48
-rw-r--r--plugins/GeoIp2/templates/_updaterManage.twig60
-rw-r--r--plugins/GeoIp2/templates/_updaterNextRunTime.twig9
-rw-r--r--plugins/GeoIp2/templates/configuration.twig48
-rw-r--r--plugins/GeoIp2/templates/setupguide.twig9
-rw-r--r--plugins/GeoIp2/tests/Unit/GeoIp2Test.php135
14 files changed, 807 insertions, 47 deletions
diff --git a/plugins/GeoIp2/Columns/Region.php b/plugins/GeoIp2/Columns/Region.php
deleted file mode 100644
index 934251b60e..0000000000
--- a/plugins/GeoIp2/Columns/Region.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-/**
- * Matomo - free/libre analytics platform
- *
- * @link https://matomo.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\GeoIp2\Columns;
-
-class Region extends \Piwik\Plugins\UserCountry\Columns\Region
-{
- protected $columnType = 'char(3) DEFAULT NULL';
- protected $segmentName = '';
-
- public function uninstall()
- {
- // do not remove region column when plugin is deactivated
- }
-} \ No newline at end of file
diff --git a/plugins/GeoIp2/Controller.php b/plugins/GeoIp2/Controller.php
new file mode 100644
index 0000000000..2de183067e
--- /dev/null
+++ b/plugins/GeoIp2/Controller.php
@@ -0,0 +1,200 @@
+<?php
+namespace Piwik\Plugins\GeoIp2;
+
+use Piwik\Common;
+use Piwik\DataTable\Renderer\Json;
+use Piwik\Http;
+use Piwik\Piwik;
+use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2;
+use Piwik\Plugins\UserCountry\UserCountry;
+use Piwik\View;
+
+class Controller extends \Piwik\Plugin\ControllerAdmin
+{
+ /**
+ * Starts or continues download of DBIP-City.mmdb.
+ *
+ * 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 downloadFreeDBIPLiteDB()
+ {
+ $this->dieIfGeolocationAdminIsDisabled();
+ Piwik::checkUserHasSuperUserAccess();
+
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ $this->checkTokenInUrl();
+ Json::sendHeaderJSON();
+ $outputPath = GeoIp2::getPathForGeoIpDatabase('DBIP-City.mmdb') . '.gz';
+ try {
+ $result = Http::downloadChunk(
+ $url = GeoIp2::getDbIpLiteUrl(),
+ $outputPath,
+ $continue = Common::getRequestVar('continue', true, 'int')
+ );
+
+ // if the file is done
+ if ($result['current_size'] >= $result['expected_file_size']) {
+ try {
+ GeoIP2AutoUpdater::unzipDownloadedFile($outputPath, 'loc', $url, $unlink = true);
+ } catch (\Exception $e) {
+ // remove downloaded file on error
+ unlink($outputPath);
+ throw $e;
+ }
+
+ // setup the auto updater
+ GeoIP2AutoUpdater::setUpdaterOptions(array(
+ 'loc' => GeoIp2::getDbIpLiteUrl(),
+ 'period' => GeoIP2AutoUpdater::SCHEDULE_PERIOD_MONTHLY,
+ ));
+
+ $result['settings'] = GeoIP2AutoUpdater::getConfiguredUrls();
+ }
+
+ return json_encode($result);
+ } catch (\Exception $ex) {
+ return json_encode(array('error' => $ex->getMessage()));
+ }
+ }
+ }
+
+ /**
+ * 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()
+ {
+ $this->dieIfGeolocationAdminIsDisabled();
+ Piwik::checkUserHasSuperUserAccess();
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ Json::sendHeaderJSON();
+ try {
+ $this->checkTokenInUrl();
+
+ GeoIP2AutoUpdater::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->getNextMissingDbUrlInfoGeoIp2();
+
+ if ($info !== false) {
+ return json_encode($info);
+ } else {
+ $view = new View("@GeoIp2/_updaterNextRunTime");
+ $view->nextRunTime = GeoIP2AutoUpdater::getNextRunTime();
+ $nextRunTimeHtml = $view->render();
+ return json_encode(array('nextRunTime' => $nextRunTimeHtml));
+ }
+ } catch (\Exception $ex) {
+ return 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()
+ {
+ $this->dieIfGeolocationAdminIsDisabled();
+ Piwik::checkUserHasSuperUserAccess();
+
+ if ($_SERVER["REQUEST_METHOD"] == "POST") {
+ try {
+ $this->checkTokenInUrl();
+
+ 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 = GeoIP2AutoUpdater::getConfiguredUrl($key);
+ $filename = GeoIP2AutoUpdater::getZippedFilenameToDownloadTo($url, $key, GeoIP2AutoUpdater::getGeoIPUrlExtension($url));
+ $outputPath = GeoIp2::getPathForGeoIpDatabase($filename);
+
+ // download part of the file
+ $result = Http::downloadChunk(
+ $url, $outputPath, Common::getRequestVar('continue', true, 'int'));
+
+ // if the file is done
+ if ($result['current_size'] >= $result['expected_file_size']) {
+ GeoIP2AutoUpdater::unzipDownloadedFile($outputPath, $key, $url, $unlink = true);
+
+ $info = $this->getNextMissingDbUrlInfoGeoIp2();
+ if ($info !== false) {
+ return json_encode($info);
+ }
+ }
+
+ return json_encode($result);
+ } catch (\Exception $ex) {
+ return json_encode(array('error' => $ex->getMessage()));
+ }
+ }
+ }
+
+ /**
+ * Gets information for the first missing GeoIP2 database (if any).
+ *
+ * @return array|bool
+ */
+ private function getNextMissingDbUrlInfoGeoIp2()
+ {
+ $missingDbs = GeoIP2AutoUpdater::getMissingDatabases();
+ if (!empty($missingDbs)) {
+ $missingDbKey = $missingDbs[0];
+ $url = GeoIP2AutoUpdater::getConfiguredUrl($missingDbKey);
+
+ $link = '<a href="' . $url . '">' . $url . '</a>';
+
+ return [
+ 'to_download' => $missingDbKey,
+ 'to_download_label' => Piwik::translate('GeoIp2_DownloadingDb', $link) . '...',
+ ];
+ }
+ return false;
+ }
+
+ private function dieIfGeolocationAdminIsDisabled()
+ {
+ if (!UserCountry::isGeoLocationAdminEnabled()) {
+ throw new \Exception('Geo location setting page has been disabled.');
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/GeoIp2/GeoIP2AutoUpdater.php b/plugins/GeoIp2/GeoIP2AutoUpdater.php
index 423256be83..dcc79dfdbd 100644
--- a/plugins/GeoIp2/GeoIP2AutoUpdater.php
+++ b/plugins/GeoIp2/GeoIP2AutoUpdater.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\GeoIp2;
require_once PIWIK_INCLUDE_PATH . "/core/ScheduledTask.php"; // for the tracker which doesn't include this file
use Exception;
+use GeoIp2\Database\Reader;
use Piwik\Common;
use Piwik\Container\StaticContainer;
use Piwik\Date;
@@ -21,7 +22,6 @@ use Piwik\Option;
use Piwik\Piwik;
use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2 AS LocationProviderGeoIp2;
use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2\Php;
-use Piwik\Plugins\UserCountry\GeoIPAutoUpdater;
use Piwik\Scheduler\Scheduler;
use Piwik\Scheduler\Task;
use Piwik\Scheduler\Timetable;
@@ -200,7 +200,7 @@ class GeoIP2AutoUpdater extends Task
$content = $unzip->listContent();
if (empty($content)) {
- throw new Exception(Piwik::translate('UserCountry_CannotListContent',
+ throw new Exception(Piwik::translate('GeoIp2_CannotListContent',
array("'$path'", $unzip->errorInfo())));
}
@@ -253,7 +253,7 @@ class GeoIP2AutoUpdater extends Task
$success = $unzip->extract($outputPath);
if ($success !== true) {
- throw new Exception(Piwik::translate('UserCountry_CannotUnzipDatFile',
+ throw new Exception(Piwik::translate('General_CannotUnzipFile',
array("'$path'", $unzip->errorInfo())));
}
@@ -263,7 +263,7 @@ class GeoIP2AutoUpdater extends Task
}
} else {
$ext = end(explode(basename($path), '.', 2));
- throw new Exception(Piwik::translate('UserCountry_UnsupportedArchiveType', "'$ext'"));
+ throw new Exception(Piwik::translate('GeoIp2_UnsupportedArchiveType', "'$ext'"));
}
try {
@@ -286,11 +286,11 @@ class GeoIP2AutoUpdater extends Task
Log::info("GeoIP2AutoUpdater: Encountered exception when testing newly downloaded" .
" GeoIP 2 database: %s", $e->getMessage());
- throw new Exception(Piwik::translate('UserCountry_ThisUrlIsNotAValidGeoIPDB'));
+ throw new Exception(Piwik::translate('GeoIp2_ThisUrlIsNotAValidGeoIPDB'));
}
if (empty($location)) {
- throw new Exception(Piwik::translate('UserCountry_ThisUrlIsNotAValidGeoIPDB'));
+ throw new Exception(Piwik::translate('GeoIp2_ThisUrlIsNotAValidGeoIPDB'));
}
// delete the existing GeoIP database (if any) and rename the downloaded file
@@ -403,7 +403,7 @@ class GeoIP2AutoUpdater extends Task
&& $period != self::SCHEDULE_PERIOD_WEEKLY
) {
throw new Exception(Piwik::translate(
- 'UserCountry_InvalidGeoIPUpdatePeriod',
+ 'GeoIp2_InvalidGeoIPUpdatePeriod',
array("'$period'", "'" . self::SCHEDULE_PERIOD_MONTHLY . "', '" . self::SCHEDULE_PERIOD_WEEKLY . "'")
));
}
@@ -415,9 +415,6 @@ class GeoIP2AutoUpdater extends Task
$scheduler->rescheduleTaskAndRunTomorrow(new GeoIP2AutoUpdater());
}
-
- // clear option for GeoIP as not needed if GeoIP2 is set up
- GeoIPAutoUpdater::clearOptions();
}
/**
@@ -559,7 +556,7 @@ class GeoIP2AutoUpdater extends Task
&& $ext != 'gz'
&& $ext != 'mmdb.gz'
) {
- throw new \Exception(Piwik::translate('UserCountry_UnsupportedArchiveType', "'$ext'"));
+ throw new \Exception(Piwik::translate('GeoIp2_UnsupportedArchiveType', "'$ext'"));
}
}
@@ -592,9 +589,13 @@ class GeoIP2AutoUpdater extends Task
// test the provider. on error, we rename the broken DB.
try {
+ // check database directly, as location provider ignores invalid database errors
+ $pathToDb = LocationProviderGeoIp2::getPathToGeoIpDatabase($customNames);
+ $reader = new Reader($pathToDb);
+
$location = $provider->getLocation(array('ip' => LocationProviderGeoIp2::TEST_IP));
} catch (\Exception $e) {
- if($logErrors) {
+ if ($logErrors) {
Log::error("GeoIP2AutoUpdater: Encountered exception when performing redundant tests on GeoIP2 "
. "%s database: %s", $type, $e->getMessage());
}
diff --git a/plugins/GeoIp2/GeoIp2.php b/plugins/GeoIp2/GeoIp2.php
index 62562d8924..ffba09bd98 100644
--- a/plugins/GeoIp2/GeoIp2.php
+++ b/plugins/GeoIp2/GeoIp2.php
@@ -15,6 +15,14 @@ use Piwik\Plugins\UserCountry\LocationProvider;
*/
class GeoIp2 extends \Piwik\Plugin
{
+ public function registerEvents()
+ {
+ return array(
+ 'AssetManager.getJavaScriptFiles' => 'getJsFiles',
+ 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
+ );
+ }
+
public function isTrackerPlugin()
{
return true;
@@ -27,4 +35,19 @@ class GeoIp2 extends \Piwik\Plugin
LocationProvider::setCurrentProvider(LocationProvider\DefaultProvider::ID);
}
}
+
+ public function getJsFiles(&$jsFiles)
+ {
+ $jsFiles[] = "plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.controller.js";
+ $jsFiles[] = "plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.directive.js";
+ }
+
+ public function getClientSideTranslationKeys(&$translationKeys)
+ {
+ $translationKeys[] = "GeoIp2_FatalErrorDuringDownload";
+ $translationKeys[] = "GeoIp2_SetupAutomaticUpdatesOfGeoIP";
+ $translationKeys[] = "General_Done";
+ $translationKeys[] = "General_Save";
+ $translationKeys[] = "General_Continue";
+ }
}
diff --git a/plugins/GeoIp2/LocationProvider/GeoIp2/Php.php b/plugins/GeoIp2/LocationProvider/GeoIp2/Php.php
index 310a273dc4..6a92f4f409 100644
--- a/plugins/GeoIp2/LocationProvider/GeoIp2/Php.php
+++ b/plugins/GeoIp2/LocationProvider/GeoIp2/Php.php
@@ -11,11 +11,17 @@ namespace Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2;
use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;
use MaxMind\Db\Reader\InvalidDatabaseException;
+use Piwik\Date;
use Piwik\Common;
use Piwik\Log;
use Piwik\Piwik;
+use Piwik\Plugin\Manager;
+use Piwik\Plugins\GeoIp2\GeoIP2AutoUpdater;
use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2;
use Piwik\Plugins\Marketplace\Api\Exception;
+use Piwik\Plugins\UserCountry\LocationProvider;
+use Piwik\SettingsPiwik;
+use Piwik\View;
/**
* A LocationProvider that uses the PHP implementation of GeoIP 2.
@@ -391,16 +397,16 @@ class Php extends GeoIp2
}
if (isset($availableInfo[self::ISP_KEY]) && $availableInfo[self::ISP_KEY]) {
- $availableDatabaseTypes[] = Piwik::translate('UserCountry_ISPDatabase');
+ $availableDatabaseTypes[] = Piwik::translate('GeoIp2_ISPDatabase');
}
if (!empty($availableDatabaseTypes)) {
$extraMessage = '<strong>' . Piwik::translate('General_Note') . '</strong>:&nbsp;'
- . Piwik::translate('UserCountry_GeoIPImplHasAccessTo') . ':&nbsp;<strong>'
+ . Piwik::translate('GeoIp2_GeoIPImplHasAccessTo') . ':&nbsp;<strong>'
. implode(', ', $availableDatabaseTypes) . '</strong>.';
} else {
$extraMessage = '<strong>' . Piwik::translate('General_Note') . '</strong>:&nbsp;'
- . Piwik::translate('UserCountry_GeoIPNoDatabaseFound');
+ . Piwik::translate('GeoIp2_GeoIPNoDatabaseFound');
}
return array('id' => self::ID,
@@ -411,6 +417,67 @@ class Php extends GeoIp2
'order' => 2);
}
+ public function renderConfiguration()
+ {
+ $view = new View('@GeoIp2/configuration.twig');
+ $today = Date::today();
+
+ $urls = GeoIP2AutoUpdater::getConfiguredUrls();
+ $view->geoIPLocUrl = $urls['loc'];
+ $view->geoIPIspUrl = $urls['isp'];
+ $view->geoIPUpdatePeriod = GeoIP2AutoUpdater::getSchedulePeriod();
+
+ $view->hasGeoIp2Provider = Manager::getInstance()->isPluginActivated('GeoIp2');
+
+ $geoIPDatabasesInstalled = $view->hasGeoIp2Provider ? GeoIp2::isDatabaseInstalled() : false;
+
+ $view->geoIPDatabasesInstalled = $geoIPDatabasesInstalled;
+ $view->updatePeriodOptions = [
+ 'month' => Piwik::translate('Intl_PeriodMonth'),
+ 'week' => Piwik::translate('Intl_PeriodWeek')
+ ];
+
+
+ // if using a server module, they are working and there are no databases
+ // in misc, then the databases are located outside of Matomo, so we cannot update them
+ $view->showGeoIPUpdateSection = true;
+ $currentProviderId = LocationProvider::getCurrentProviderId();
+ if (!$geoIPDatabasesInstalled
+ && in_array($currentProviderId, [GeoIp2\ServerModule::ID])
+ && LocationProvider::getCurrentProvider()->isWorking()
+ && LocationProvider::getCurrentProvider()->isAvailable()
+ ) {
+ $view->showGeoIPUpdateSection = false;
+ }
+
+ $view->isInternetEnabled = SettingsPiwik::isInternetEnabled();
+
+
+ $view->dbipLiteUrl = GeoIp2::getDbIpLiteUrl();
+ $view->dbipLiteFilename = "dbip-city-lite-{$today->toString('Y-m')}.mmdb";
+ $view->dbipLiteDesiredFilename = "DBIP-City.mmdb";
+ $view->nextRunTime = GeoIP2AutoUpdater::getNextRunTime();
+
+ $lastRunTime = GeoIP2AutoUpdater::getLastRunTime();
+
+ if ($lastRunTime !== false) {
+ $view->lastTimeUpdaterRun = '<strong>' . $lastRunTime->toString() . '</strong>';
+ }
+ return $view->render();
+ }
+
+ public function renderSetUpGuide()
+ {
+ $today = Date::today();
+ $view = new View('@GeoIp2/setupguide.twig');
+
+ $view->dbipLiteUrl = GeoIp2::getDbIpLiteUrl();
+ $view->dbipLiteFilename = "dbip-city-lite-{$today->toString('Y-m')}.mmdb";
+ $view->dbipLiteDesiredFilename = "DBIP-City.mmdb";
+
+ return $view->render();
+ }
+
/**
* Returns a GeoIP2 reader instance. Creates it if necessary.
*
diff --git a/plugins/GeoIp2/LocationProvider/GeoIp2/ServerModule.php b/plugins/GeoIp2/LocationProvider/GeoIp2/ServerModule.php
index 5e2facc549..5ebe6114ce 100644
--- a/plugins/GeoIp2/LocationProvider/GeoIp2/ServerModule.php
+++ b/plugins/GeoIp2/LocationProvider/GeoIp2/ServerModule.php
@@ -159,11 +159,11 @@ class ServerModule extends GeoIp2
// if not available return message w/ extra info
if (!function_exists('apache_get_modules')) {
- return Piwik::translate('General_Note') . ':&nbsp;' . Piwik::translate('UserCountry_AssumingNonApache');
+ return Piwik::translate('General_Note') . ':&nbsp;' . Piwik::translate('GeoIp2_AssumingNonApache');
}
$message = "<strong>" . Piwik::translate('General_Note') . ':&nbsp;'
- . Piwik::translate('UserCountry_FoundApacheModules')
+ . Piwik::translate('GeoIp2_FoundApacheModules')
. "</strong>:<br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
foreach (apache_get_modules() as $name) {
$message .= "<li>$name</li>\n";
@@ -187,7 +187,7 @@ class ServerModule extends GeoIp2
|| array_key_exists($settings[self::CITY_NAME_KEY], $_SERVER);
if (!$available) {
- return Piwik::translate("UserCountry_CannotFindGeoIPServerVar", $settings[self::COUNTRY_CODE_KEY] . ' $_SERVER');
+ return Piwik::translate('GeoIp2_CannotFindGeoIPServerVar', $settings[self::COUNTRY_CODE_KEY] . ' $_SERVER');
}
return true;
@@ -209,24 +209,24 @@ class ServerModule extends GeoIp2
if (function_exists('apache_note')) {
$serverDesc = 'Apache';
} else {
- $serverDesc = Piwik::translate('UserCountry_HttpServerModule');
+ $serverDesc = Piwik::translate('GeoIp2_HttpServerModule');
}
$title = sprintf(self::TITLE, $serverDesc);
$desc = Piwik::translate('GeoIp2_LocationProviderDesc_ServerModule', array('<strong>', '</strong>'))
. '<br/><br/>'
- . Piwik::translate('UserCountry_GeoIpLocationProviderDesc_ServerBasedAnonWarn')
+ . Piwik::translate('GeoIp2_GeoIPLocationProviderDesc_ServerBasedAnonWarn')
. '<br/><br/>'
. Piwik::translate('GeoIp2_LocationProviderDesc_ServerModule2',
array('<strong>', '</strong>', '<strong>', '</strong>'));
$installDocs =
'<a rel="noreferrer" target="_blank" href="https://maxmind.github.io/mod_maxminddb/">'
- . Piwik::translate('UserCountry_HowToInstallApacheModule')
+ . Piwik::translate('GeoIp2_HowToInstallApacheModule')
. '</a><br/>'
. '<a rel="noreferrer" target="_blank" href="https://github.com/leev/ngx_http_geoip2_module/blob/master/README.md#installing">'
- . Piwik::translate('UserCountry_HowToInstallNginxModule')
+ . Piwik::translate('GeoIp2_HowToInstallNginxModule')
. '</a>';
$geoipServerVars = array();
@@ -237,9 +237,9 @@ class ServerModule extends GeoIp2
}
if (empty($geoipServerVars)) {
- $extraMessage = '<strong>' . Piwik::translate('UserCountry_GeoIPNoServerVars', '$_SERVER') . '</strong>';
+ $extraMessage = '<strong>' . Piwik::translate('GeoIp2_GeoIPNoServerVars', '$_SERVER') . '</strong>';
} else {
- $extraMessage = '<strong>' . Piwik::translate('UserCountry_GeoIPServerVarsFound', '$_SERVER')
+ $extraMessage = '<strong>' . Piwik::translate('GeoIp2_GeoIPServerVarsFound', '$_SERVER')
. ":</strong><br/><br/>\n<ul style=\"list-style:disc;margin-left:24px\">\n";
foreach ($geoipServerVars as $key) {
$extraMessage .= '<li>' . $key . "</li>\n";
diff --git a/plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.controller.js b/plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.controller.js
new file mode 100644
index 0000000000..d52224d75a
--- /dev/null
+++ b/plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.controller.js
@@ -0,0 +1,149 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('Geoip2UpdaterController', Geoip2UpdaterController);
+
+ Geoip2UpdaterController.$inject = ['piwikApi', '$window'];
+
+ function Geoip2UpdaterController(piwikApi, $window) {
+ // remember to keep controller very simple. Create a service/factory (model) if needed
+ var self = this;
+
+ this.buttonUpdateSaveText = _pk_translate('General_Save');
+ this.progressUpdateLabel = '';
+
+ // geoip database wizard
+ var downloadNextChunk = function (action, thisId, progressBarId, cont, extraData, callback) {
+ var data = {};
+ for (var k in extraData) {
+ data[k] = extraData[k];
+ }
+
+ piwikApi.withTokenInUrl();
+ piwikApi.post({
+ module: 'GeoIp2',
+ action: action,
+ 'continue': cont ? 1 : 0
+ }, data).then(function (response) {
+ if (!response || response.error) {
+ callback(response);
+ } else {
+ // update progress bar
+ var newProgressVal = Math.floor((response.current_size / response.expected_file_size) * 100);
+ self[progressBarId] = Math.min(newProgressVal, 100);
+
+ // if incomplete, download next chunk, otherwise, show updater manager
+ if (newProgressVal < 100) {
+ downloadNextChunk(action, thisId, progressBarId, true, extraData, callback);
+ } else {
+ callback(response);
+ }
+ }
+ }, function () {
+ callback({error: _pk_translate('GeoIp2_FatalErrorDuringDownload')});
+ });
+ };
+
+ this.startDownloadFreeGeoIp = function () {
+ this.showFreeDownload = true;
+ this.showPiwikNotManagingInfo = false;
+
+ this.progressFreeDownload = 0;
+
+ // start download of free dbs
+ downloadNextChunk(
+ 'downloadFreeDBIPLiteDB',
+ 'geoipdb-screen2-download',
+ 'progressFreeDownload',
+ false,
+ {},
+ function (response) {
+ if (response.error) {
+ $('#geoipdb-update-info').html(response.error);
+ self.geoipDatabaseInstalled = true;
+ } else {
+ $window.location.reload();
+ }
+ }
+ );
+ };
+
+ this.startAutomaticUpdateGeoIp = function () {
+ this.buttonUpdateSaveText = _pk_translate('General_Continue');
+ this.showGeoIpUpdateInfo();
+ };
+
+ this.showGeoIpUpdateInfo = function () {
+ this.geoipDatabaseInstalled = true;
+
+ // todo we need to replace this the proper way eventually
+ $('#geoip-db-mangement .card-title').text(_pk_translate('GeoIp2_SetupAutomaticUpdatesOfGeoIP'));
+ }
+
+ this.saveGeoIpLinks = function () {
+ var currentDownloading = null;
+ var updateGeoIPSuccess = function (response) {
+ if (response && response.error) {
+ self.isUpdatingGeoIpDatabase = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(response.error, {
+ placeat: '#geoipdb-update-info-error',
+ context: 'error',
+ style: {display: 'inline-block'},
+ id: 'userCountryGeoIpUpdate'
+ });
+
+ } else if (response && response.to_download) {
+ var continuing = currentDownloading == response.to_download;
+ currentDownloading = response.to_download;
+
+ // show progress bar w/ message
+ self.progressUpdateDownload = 0;
+ self.progressUpdateLabel = response.to_download_label;
+ self.isUpdatingGeoIpDatabase = true;
+
+ // start/continue download
+ downloadNextChunk(
+ 'downloadMissingGeoIpDb', 'geoipdb-update-info', 'progressUpdateDownload',
+ continuing, {key: response.to_download}, updateGeoIPSuccess);
+
+ } else {
+ self.progressUpdateLabel = '';
+ self.isUpdatingGeoIpDatabase = false;
+
+ var UI = require('piwik/UI');
+ var notification = new UI.Notification();
+ notification.show(_pk_translate('General_Done'), {
+ placeat: '#done-updating-updater',
+ context: 'success',
+ noclear: true,
+ type: 'toast',
+ style: {display: 'inline-block'},
+ id: 'userCountryGeoIpUpdate'
+ });
+
+ $('#geoip-updater-next-run-time').html(response.nextRunTime).parent().effect('highlight', {color: '#FFFFCB'}, 2000);
+ }
+ };
+
+ piwikApi.withTokenInUrl();
+ piwikApi.post({
+ period: this.updatePeriod,
+ module: 'GeoIp2',
+ action: 'updateGeoIPLinks'
+ }, {
+ loc_db: this.locationDbUrl,
+ isp_db: this.ispDbUrl,
+ org_db: this.orgDbUrl
+ }).then(updateGeoIPSuccess);
+ };
+
+
+ }
+})(); \ No newline at end of file
diff --git a/plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.directive.js b/plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.directive.js
new file mode 100644
index 0000000000..9b21145feb
--- /dev/null
+++ b/plugins/GeoIp2/angularjs/geoip2-updater/geoip2-updater.directive.js
@@ -0,0 +1,37 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Usage:
+ * <div piwik-geoip2-updater>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikGeoip2Updater', piwikGeoip2Updater);
+
+ piwikGeoip2Updater.$inject = ['piwik'];
+
+ function piwikGeoip2Updater(piwik){
+
+ return {
+ restrict: 'A',
+ transclude: true,
+ controller: 'Geoip2UpdaterController',
+ controllerAs: 'locationUpdater',
+ template: '<div ng-transclude></div>',
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, controller) {
+ controller.geoipDatabaseInstalled = '0' !== attrs.geoipDatabaseInstalled;
+ controller.showFreeDownload = false;
+ controller.showPiwikNotManagingInfo = true;
+ controller.progressFreeDownload = 0;
+ controller.progressUpdateDownload = 0;
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/GeoIp2/lang/en.json b/plugins/GeoIp2/lang/en.json
index d0559328eb..158028266e 100644
--- a/plugins/GeoIp2/lang/en.json
+++ b/plugins/GeoIp2/lang/en.json
@@ -1,15 +1,57 @@
{
"GeoIp2": {
+ "AssumingNonApache": "Cannot find apache_get_modules function, assuming non-Apache webserver.",
"CannotFindGeoIPDatabaseInArchive": "No valid DBIP / GeoIP database could be found in tar archive %1$s!",
+ "CannotFindGeoIPServerVar": "The %s variable is not set. Your server may not be configured correctly.",
+ "CannotListContent": "Couldn't list content for %1$s: %2$s",
+ "CannotSetupGeoIPAutoUpdating": "It seems like you're storing your geolocation databases outside of Matomo (we can tell since there are no databases in the misc subdirectory, but your GeoIP is working). Matomo cannot automatically update your geolocation databases if they are located outside of the misc directory.",
"CannotUnzipGeoIPFile": "Could not unzip GeoIP file in %1$s: %2$s",
+ "DownloadingDb": "Downloading %s",
+ "DownloadNewDatabasesEvery": "Update databases every",
+ "FatalErrorDuringDownload": "A fatal error occurred while downloading this file. There might be something wrong with your internet connection, with the geolocation database you downloaded or Matomo. Try downloading and installing it manually.",
+ "FoundApacheModules": "Matomo found the following Apache modules",
+ "GeoIPImplHasAccessTo": "This GeoIP implementation has access to the following types of databases",
+ "GeoIPDatabases": "GeoIP Databases",
+ "GeoIPLocationProviderDesc_ServerBasedAnonWarn": "Note: IP anonymization has no effect on the locations reported by this provider. Before using it with IP anonymization, make sure this does not violate any privacy laws you may be subject to.",
+ "GeoIPLocationProviderNotRecommended": "Geolocation works, but you are not using one of the recommended providers.",
+ "GeoIPNoDatabaseFound": "This GeoIP implementation was not able to find any database.",
+ "GeoIPNoServerVars": "Matomo cannot find any GeoIP %s variables.",
+ "GeoIPServerVarsFound": "Matomo detects the following GeoIP %s variables",
+ "GeoIPUpdaterInstructions": "Enter the download links for your databases below. If you've purchased databases from %3$sdbip%4$s or %1$sMaxMind%2$s, you can find these links in your dbip or MaxMind account. Please contact your geolocation database provider if you have trouble accessing them.",
+ "GeoIPUpdaterIntro": "Matomo is currently managing updates for the following databases",
+ "GeoIPVariablesConfigurationHere": "You can configure used server variables %1$shere%2$s.",
+ "GeoLiteCityLink": "If you're using the dbip city lite database, use this link: %1$s%2$s%3$s",
+ "HowToSetupGeoIP": "How to setup accurate geolocation with dbip",
+ "HowToSetupGeoIP_Step1": "%1$sDownload%2$s the DBIP City Lite database from %3$sdbip%4$s.",
+ "HowToSetupGeoIP_Step2": "Extract this file and copy the result, %1$s into the %2$smisc%3$s Matomo subdirectory and rename it to %4$s (you can do this either by FTP or SSH).",
+ "HowToSetupGeoIP_Step3": "Reload this screen. The %1$sDBIP / GeoIP (PHP)%2$s provider will now be %3$sInstalled%4$s. Select it.",
+ "HowToSetupGeoIP_Step4": "And you're done! You've just setup Matomo to use DBIP which means you'll be able to see the regions and cities of your visitors along with very accurate country information.",
+ "HowToSetupGeoIPIntro": "You do not appear to have accurate Geolocation setup. This is a useful feature and without it you will not see accurate and complete location information for your visitors. Here's how you can quickly start using it:",
+ "HowToInstallApacheModule": "How do I install the GeoIP module for Apache?",
+ "HowToInstallNginxModule": "How do I install the GeoIP module for Nginx?",
+ "HttpServerModule": "HTTP Server Module",
+ "InvalidGeoIPUpdatePeriod": "Invalid period for the GeoIP updater: %1$s. Valid values are %2$s.",
+ "IPurchasedGeoIPDBs": "I purchased more accurate databases from %3$sdbip%4$s or %1$sMaxMind%2$s and want to setup automatic updates.",
+ "ISPDatabase": "ISP Database",
+ "IWantToDownloadFreeGeoIP": "I want to download the free DBIP database...",
"PluginDescription": "Provides DBIP / GeoIP2 location providers.",
+ "LocationDatabase": "Location Database",
+ "LocationDatabaseHint": "A location database is either a country, region or city database.",
"LocationProviderDesc_Php": "This location provider is the simplest to install as it does not require server configuration (ideal for shared hosting!). It uses a DBIP or GeoIP 2 database and MaxMind's PHP API to accurately determine the location of your visitors.",
"LocationProviderDesc_Php_WithExtension": "This location provider is speeded up by the installed %1$smaxminddb%2$s extension.",
"LocationProviderDesc_ServerModule": "This location provider uses the GeoIP 2 module that has been installed in your HTTP server. This provider is fast and accurate, but %1$scan only be used with normal browser tracking.%2$s",
"LocationProviderDesc_ServerModule2": "If you have to import log files or do something else that requires setting IP addresses, use the %3$sPHP GeoIP 2 implementation%4$s and install %1$smaxminddb extension%2$s.",
- "ServerBasedVariablesConfiguration": "Configuration for server variables used by GeoIP 2 server modules",
- "GeoIPVariablesConfigurationHere": "You can configure used server variables %1$shere%2$s.",
+ "NotManagingGeoIPDBs": "Matomo is currently not managing any DBIP or MaxMind databases.",
+ "UnsupportedArchiveType": "Encountered unsupported archive type %1$s.",
+ "UpdaterHasNotBeenRun": "The updater has never been run.",
+ "UpdaterIsNotScheduledToRun": "It is not scheduled to run in the future.",
+ "UpdaterScheduledForNextRun": "It is scheduled to run during the next cron core:archive command execution.",
+ "UpdaterWasLastRun": "The updater was last run on %s.",
+ "UpdaterWillRunNext": "It is next scheduled to run on %s.",
"ShowCustomServerVariablesConfig": "I use the Geoip2 server module (Nginx, Apache...) and want to configure server variables",
- "ServerVariableFor": "Server variable for %s"
+ "ServerBasedVariablesConfiguration": "Configuration for server variables used by GeoIP 2 server modules",
+ "ServerVariableFor": "Server variable for %s",
+ "SetupAutomaticUpdatesOfGeoIP": "Setup automatic updates of geolocation databases",
+ "ThisUrlIsNotAValidGeoIPDB": "The downloaded file is not a valid geolocation database. Please re-check the URL or download the file manually."
}
} \ No newline at end of file
diff --git a/plugins/GeoIp2/templates/_updaterManage.twig b/plugins/GeoIp2/templates/_updaterManage.twig
new file mode 100644
index 0000000000..f110de26da
--- /dev/null
+++ b/plugins/GeoIp2/templates/_updaterManage.twig
@@ -0,0 +1,60 @@
+<div ng-show="locationUpdater.geoipDatabaseInstalled" id="geoipdb-update-info">
+ <p>
+ {{ 'GeoIp2_GeoIPUpdaterInstructions'|translate('<a href="http://www.maxmind.com/?rId=piwik">','</a>','<a rel="noreferrer noopener" href="https://db-ip.com/?refid=mtm">','</a>')|raw }}
+ <br/><br/>
+ {% if dbipLiteUrl|default is not empty %}{{ 'GeoIp2_GeoLiteCityLink'|translate('<a rel="noreferrer noopener" href="'~dbipLiteUrl|e('html_attr')~'">',dbipLiteUrl|e('html'),'</a>')|raw }}{% endif %}
+
+ <span ng-show="locationUpdater.geoipDatabaseInstalled">
+ <br/><br/>{{ 'GeoIp2_GeoIPUpdaterIntro'|translate }}:
+ </span>
+ </p>
+
+ <div piwik-field uicontrol="text" name="geoip-location-db"
+ ng-model="locationUpdater.locationDbUrl"
+ introduction="{{ 'GeoIp2_LocationDatabase'|translate|e('html_attr') }}"
+ data-title="{{ 'Actions_ColumnDownloadURL'|translate|e('html_attr') }}"
+ value="{{ geoIPLocUrl }}"
+ inline-help="{{ 'GeoIp2_LocationDatabaseHint'|translate|e('html_attr') }}">
+ </div>
+
+ <div piwik-field uicontrol="text" name="geoip-isp-db"
+ ng-model="locationUpdater.ispDbUrl"
+ introduction="{{ 'GeoIp2_ISPDatabase'|translate|e('html_attr') }}"
+ data-title="{{ 'Actions_ColumnDownloadURL'|translate|e('html_attr') }}"
+ value="{{ geoIPIspUrl }}">
+ </div>
+
+ <div id="locationProviderUpdatePeriodInlineHelp" class="inline-help-node">
+ {% if lastTimeUpdaterRun is defined and lastTimeUpdaterRun is not empty %}
+ {{ 'GeoIp2_UpdaterWasLastRun'|translate(lastTimeUpdaterRun)|raw }}
+ {% else %}
+ {{ 'GeoIp2_UpdaterHasNotBeenRun'|translate }}
+ {% endif %}
+ <br/><br/>
+ <div id="geoip-updater-next-run-time">
+ {% include "@GeoIp2/_updaterNextRunTime.twig" %}
+ </div>
+ </div>
+
+ <div piwik-field uicontrol="radio" name="geoip-update-period"
+ ng-model="locationUpdater.updatePeriod"
+ introduction="{{ 'GeoIp2_DownloadNewDatabasesEvery'|translate|e('html_attr') }}"
+ value="{{ geoIPUpdatePeriod }}"
+ options="{{ updatePeriodOptions|json_encode }}"
+ inline-help="#locationProviderUpdatePeriodInlineHelp">
+ </div>
+
+ <input type="button"
+ class="btn"
+ ng-click="locationUpdater.saveGeoIpLinks()"
+ ng-value="locationUpdater.buttonUpdateSaveText"/>
+
+ <div>
+ <div id="done-updating-updater"></div>
+ <div id="geoipdb-update-info-error"></div>
+ <div piwik-progressbar
+ progress="locationUpdater.progressUpdateDownload"
+ label="locationUpdater.progressUpdateLabel"
+ ng-show="locationUpdater.isUpdatingGeoIpDatabase"></div>
+ </div>
+</div>
diff --git a/plugins/GeoIp2/templates/_updaterNextRunTime.twig b/plugins/GeoIp2/templates/_updaterNextRunTime.twig
new file mode 100644
index 0000000000..ab5b273be6
--- /dev/null
+++ b/plugins/GeoIp2/templates/_updaterNextRunTime.twig
@@ -0,0 +1,9 @@
+{% if nextRunTime|default is not empty %}
+ {% if date(nextRunTime.getTimestamp()) <= date() %}
+ {{ 'GeoIp2_UpdaterScheduledForNextRun'|translate }}
+ {% else %}
+ {{ 'GeoIp2_UpdaterWillRunNext'|translate('<strong>' ~ nextRunTime.toString() ~ '</strong>')|raw }}
+ {% endif %}
+{% else %}
+ {{ 'GeoIp2_UpdaterIsNotScheduledToRun'|translate }}
+{% endif %} \ No newline at end of file
diff --git a/plugins/GeoIp2/templates/configuration.twig b/plugins/GeoIp2/templates/configuration.twig
new file mode 100644
index 0000000000..729e0f112d
--- /dev/null
+++ b/plugins/GeoIp2/templates/configuration.twig
@@ -0,0 +1,48 @@
+<div piwik-content-block
+ content-title="{% if not geoIPDatabasesInstalled %}{{ 'GeoIp2_GeoIPDatabases'|translate|e('html_attr') }}{% else %}{{ 'GeoIp2_SetupAutomaticUpdatesOfGeoIP'|translate|e('html_attr') }}{% endif %}"
+ id="geoip-db-mangement">
+
+ <div piwik-geoip2-updater
+ geoip-database-installed="{% if geoIPDatabasesInstalled %}1{% else %}0{% endif %}">
+
+ {% if showGeoIPUpdateSection %}
+ {% if not geoIPDatabasesInstalled %}
+ <div ng-show="!locationUpdater.geoipDatabaseInstalled">
+ <div ng-show="locationUpdater.showPiwikNotManagingInfo">
+ <h3>{{ 'GeoIp2_NotManagingGeoIPDBs'|translate|e('html_attr') }}</h3>
+ <div id="manage-geoip-dbs">
+ <div class="row" id="geoipdb-screen1">
+ <div class="geoipdb-column-1 col s6">
+ <p>{{ 'GeoIp2_IWantToDownloadFreeGeoIP'|translate|raw }}</p>
+ </div>
+ <div class="geoipdb-column-2 col s6">
+ <p>{{ 'GeoIp2_IPurchasedGeoIPDBs'|translate('<a rel="noreferrer noopener" href="http://www.maxmind.com/en/geolocation_landing?rId=piwik">','</a>','<a rel="noreferrer noopener" href="https://db-ip.com/db/?refid=mtm">','</a>')|raw }}</p>
+ </div>
+ <div class="geoipdb-column-1 col s6">
+ <input type="button" class="btn"
+ ng-click="locationUpdater.startDownloadFreeGeoIp()"
+ value="{{ 'General_GetStarted'|translate }}..."/>
+ </div>
+ <div class="geoipdb-column-2 col s6">
+ <input type="button" class="btn"
+ ng-click="locationUpdater.startAutomaticUpdateGeoIp()"
+ value="{{ 'General_GetStarted'|translate }}..." id="start-automatic-update-geoip"/>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="geoipdb-screen2-download" ng-show="locationUpdater.showFreeDownload">
+ <div piwik-progressbar
+ label="{{ ('GeoIp2_DownloadingDb'|translate('<a href="'~dbipLiteUrl~'">'~dbipLiteFilename~'</a>') ~ '...')|json_encode }}"
+ progress="locationUpdater.progressFreeDownload">
+ </div>
+ </div>
+ </div>
+ {% endif %}
+
+ {% include "@GeoIp2/_updaterManage.twig" %}
+ {% else %}
+ <p class="form-description">{{ 'GeoIp2_CannotSetupGeoIPAutoUpdating'|translate }}</p>
+ {% endif %}
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/GeoIp2/templates/setupguide.twig b/plugins/GeoIp2/templates/setupguide.twig
new file mode 100644
index 0000000000..737ec17fb1
--- /dev/null
+++ b/plugins/GeoIp2/templates/setupguide.twig
@@ -0,0 +1,9 @@
+<h3 style="margin-top:0;">{{ 'GeoIp2_HowToSetupGeoIP'|translate }}</h3>
+<p>{{ 'GeoIp2_HowToSetupGeoIPIntro'|translate }}</p>
+<ul style="list-style:disc !important;margin-left:2em;">
+ <li style="list-style-type: disc !important;">{{ 'GeoIp2_HowToSetupGeoIP_Step1'|translate('<a rel="noreferrer noopener" href="'~dbipLiteUrl~'">','</a>','<a rel="noreferrer noopener" target="_blank" href="http://db-ip.com/?refid=mtm">','</a>')|raw }}</li>
+ <li style="list-style-type: disc !important;">{{ 'GeoIp2_HowToSetupGeoIP_Step2'|translate("'"~dbipLiteFilename~"'",'<strong>','</strong>','<strong>'~dbipLiteDesiredFilename~'</strong>')|raw }}</li>
+ <li style="list-style-type: disc !important;">{{ 'GeoIp2_HowToSetupGeoIP_Step3'|translate('<strong>','</strong>','<span style="color:green"><strong>','</strong></span>')|raw }}</li>
+ <li style="list-style-type: disc !important;">{{ 'GeoIp2_HowToSetupGeoIP_Step4'|translate }}</li>
+</ul>
+<p>&nbsp;</p> \ No newline at end of file
diff --git a/plugins/GeoIp2/tests/Unit/GeoIp2Test.php b/plugins/GeoIp2/tests/Unit/GeoIp2Test.php
new file mode 100644
index 0000000000..6b67150ef0
--- /dev/null
+++ b/plugins/GeoIp2/tests/Unit/GeoIp2Test.php
@@ -0,0 +1,135 @@
+<?php
+/**
+ * Matomo - free/libre analytics platform
+ *
+ * @link https://matomo.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Plugins\UserCountry\tests\Unit;
+
+use Piwik\Container\StaticContainer;
+use Piwik\Plugins\GeoIp2\GeoIP2AutoUpdater;
+use Piwik\Plugins\GeoIp2\LocationProvider\GeoIp2;
+use Piwik\Plugins\UserCountry\LocationProvider;
+use Exception;
+
+class GeoIp2Test extends \PHPUnit_Framework_TestCase
+{
+ /**
+ * Test that redundant checks work.
+ *
+ * @group Plugins
+ */
+ public function testGeoIpUpdaterRedundantChecks()
+ {
+ LocationProvider::$providers = null;
+
+ // create empty ISP file
+ $this->createEmptyISPFile();
+
+ // run redundant checks
+ $updater = new Piwik_GeoIp2_GeoIP2AutoUpdater_publictest();
+ $updater->performRedundantDbChecks();
+
+ // check that files are renamed correctly
+ $this->checkBrokenGeoIPState();
+
+ // create empty file again & run checks again
+ $this->createEmptyISPFile();
+ $updater->performRedundantDbChecks();
+
+ // check that w/ broken files already there, redundant checks still work correctly
+ $this->checkBrokenGeoIPState();
+ }
+
+ /**
+ * @group Plugins
+ *
+ * @dataProvider getInvalidGeoIpUrlsToTest
+ */
+ public function testGeoIpDownloadInvalidUrl($url)
+ {
+ // unset translations, otherwise Exception message will be translated
+ StaticContainer::get('Piwik\Translation\Translator')->reset();
+
+ $updater = new Piwik_GeoIp2_GeoIP2AutoUpdater_publictest();
+ try {
+ $updater->downloadFile('loc', $url);
+ $this->fail("Downloading invalid url succeeded!");
+ } catch (Exception $ex) {
+ $this->assertEquals("GeoIp2_UnsupportedArchiveType", $ex->getMessage());
+ }
+ }
+
+ public function getInvalidGeoIpUrlsToTest()
+ {
+ return array(array("http://localhost/tests/resources/geoip.tar"),
+ array("http://localhost/tests/resources/geoip.tar.bz2"),
+ array("http://localhost/tests/resources/geoip.dat"));
+ }
+
+ protected $backUpNames;
+
+ public function setUp()
+ {
+ $this->backUpNames = GeoIp2::$dbNames;
+
+ GeoIp2::$dbNames = [
+ 'loc' => ['DBIP-City.mmdb'],
+ 'isp' => ['DBIP-ISP.mmdb']
+ ];
+ }
+
+ public function tearDown()
+ {
+ GeoIp2::$dbNames = $this->backUpNames;
+
+ $geoIpDirPath = PIWIK_INCLUDE_PATH . '/tests/lib/geoip-files';
+ $filesToRemove = array('DBIP-ISP.mmdb.broken', 'DBIP-ISP.mmdb');
+
+ foreach ($filesToRemove as $name) {
+ $path = $geoIpDirPath . '/' . $name;
+ if (file_exists($path)) {
+ @unlink($path);
+ }
+ }
+ }
+
+ private function createEmptyISPFile()
+ {
+ $geoIpDir = PIWIK_INCLUDE_PATH . '/tests/lib/geoip-files';
+
+ $fd = fopen($geoIpDir . '/DBIP-ISP.mmdb', 'w');
+ fclose($fd);
+ }
+
+ private function checkBrokenGeoIPState()
+ {
+ $geoIpDir = PIWIK_INCLUDE_PATH . '/tests/lib/geoip-files';
+
+ $this->assertFalse(file_exists($geoIpDir . '/DBIP-City.mmdb.broken'));
+
+ $this->assertFalse(file_exists($geoIpDir . '/DBIP-ISP.mmdb'));
+ $this->assertTrue(file_exists($geoIpDir . '/DBIP-ISP.mmdb.broken'));
+ }
+}
+
+class Piwik_GeoIp2_GeoIP2AutoUpdater_publictest extends GeoIP2AutoUpdater
+{
+ public function __construct()
+ {
+ // empty
+ }
+
+ // during tests do not call the Log::error or they will be displayed in the output
+ public function performRedundantDbChecks($logErrors = false)
+ {
+ parent::performRedundantDbChecks($logErrors);
+ }
+
+ public function downloadFile($type, $url)
+ {
+ parent::downloadFile($type, $url);
+ }
+}