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:
authorbenakamoorthi <benaka.moorthi@gmail.com>2012-11-27 02:14:37 +0400
committerbenakamoorthi <benaka.moorthi@gmail.com>2012-11-27 02:14:37 +0400
commitdc98a971829c9ed278f79e759ba41b51881ba2fc (patch)
tree8acbffb943baa97c66ea954099e86fcdf243351a /plugins/UserCountry
parent4caab2461a50d6d4f81435cf52816a43525780ff (diff)
Fixes #3456, added updating scheduled task for GeoIP databases w/ manager UI and easy-install for GeoLiteCity.
Notes: * Added new PEAR lib Archive_Tar and new Unzip implmentations for .tar.gz, .tar.bz2 & .gz files. * Modified Http class to allow use of Range HTTP header. * Added ability to download file in chunks to Http class. * Moved GeoIP admin page styles to separate CSS file. * Allowed monthly scheduled tasks to specify day of week to run on. git-svn-id: http://dev.piwik.org/svn/trunk@7550 59fd770c-687e-43c8-a1e3-f5a4ff64c105
Diffstat (limited to 'plugins/UserCountry')
-rw-r--r--plugins/UserCountry/Controller.php229
-rwxr-xr-xplugins/UserCountry/GeoIPAutoUpdater.php397
-rwxr-xr-xplugins/UserCountry/LocationProvider/GeoIp.php29
-rw-r--r--plugins/UserCountry/UserCountry.php39
-rwxr-xr-xplugins/UserCountry/templates/admin.js140
-rwxr-xr-xplugins/UserCountry/templates/adminIndex.tpl42
-rwxr-xr-xplugins/UserCountry/templates/styles.css69
-rwxr-xr-xplugins/UserCountry/templates/updaterSetup.tpl53
8 files changed, 980 insertions, 18 deletions
diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php
index e7ed5c458a..1df06697c8 100644
--- a/plugins/UserCountry/Controller.php
+++ b/plugins/UserCountry/Controller.php
@@ -14,7 +14,7 @@
*
* @package Piwik_UserCountry
*/
-class Piwik_UserCountry_Controller extends Piwik_Controller
+class Piwik_UserCountry_Controller extends Piwik_Controller_Admin
{
function index()
{
@@ -41,6 +41,7 @@ class Piwik_UserCountry_Controller extends Piwik_Controller
$view->locationProviders = $allProviderInfo;
$view->currentProviderId = Piwik_UserCountry_LocationProvider::getCurrentProviderId();
$view->thisIP = Piwik_IP::getIpFromHeader();
+ $view->geoIPDatabasesInstalled = Piwik_UserCountry_LocationProvider_GeoIp::isDatabaseInstalled();
// check if there is a working provider (that isn't the default one)
$isThereWorkingProvider = false;
@@ -54,6 +55,20 @@ class Piwik_UserCountry_Controller extends Piwik_Controller
}
}
$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 (!$view->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);
$view->menu = Piwik_GetAdminMenu();
@@ -62,6 +77,197 @@ class Piwik_UserCountry_Controller extends Piwik_Controller
}
/**
+ * 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();
+
+ $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase('GeoIPCity.dat').'.gz';
+ try
+ {
+ $result = Piwik_Http::downloadChunk(
+ $url = Piwik_UserCountry_LocationProvider_GeoIp::GEO_LITE_URL,
+ $outputPath,
+ $continue = Piwik_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 json_encode($result);
+ }
+ catch (Exception $ex)
+ {
+ echo json_encode(array('error' => $ex->getMessage()));
+ }
+ }
+ }
+
+ /**
+ * Renders and returns the HTML that manages the GeoIP auto-updater.
+ *
+ * @return string
+ */
+ private function getGeoIpUpdaterManageScreen()
+ {
+ $view = Piwik_View::factory('updaterSetup');
+ $view->geoIPDatabasesInstalled = true;
+ $this->setUpdaterManageVars($view);
+ return $view->render();
+ }
+
+ /**
+ * Sets some variables needed by the updaterSetup.tpl 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();
+ }
+
+ /**
+ * 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")
+ {
+ 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 json_encode($info);
+ return;
+ }
+ }
+ catch (Exception $ex)
+ {
+ echo 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();
+
+ // based on the database type (provided by the 'key' query param) determine the
+ // url & output file name
+ $key = Piwik_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, Piwik_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 json_encode($info);
+ return;
+ }
+ }
+
+ echo json_encode($result);
+ }
+ catch (Exception $ex)
+ {
+ echo json_encode(array('error' => $ex->getMessage()));
+ }
+ }
+ }
+
+ /**
* Sets the current LocationProvider type.
*
* Input:
@@ -243,4 +449,25 @@ class Piwik_UserCountry_Controller extends Piwik_Controller
$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];
+
+ return array(
+ 'to_download' => $missingDbKey,
+ 'to_download_label' => Piwik_Translate('UserCountry_DownloadingDb', $missingDbName).'...',
+ );
+ }
+ return false;
+ }
}
diff --git a/plugins/UserCountry/GeoIPAutoUpdater.php b/plugins/UserCountry/GeoIPAutoUpdater.php
new file mode 100755
index 0000000000..391a226937
--- /dev/null
+++ b/plugins/UserCountry/GeoIPAutoUpdater.php
@@ -0,0 +1,397 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ * @version $Id$
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_UserCountry
+ */
+
+/**
+ * Used to automatically update installed GeoIP databases, and manages the updater's
+ * scheduled task.
+ */
+class Piwik_UserCountry_GeoIPAutoUpdater
+{
+ const SCHEDULE_PERIOD_MONTHLY = 'month';
+ const SCHEDULE_PERIOD_WEEKLY = 'week';
+
+ const SCHEDULE_PERIOD_OPTION_NAME = 'geoip.updater_period';
+ const LOC_URL_OPTION_NAME = 'geoip.loc_db_url';
+ const ISP_URL_OPTION_NAME = 'geoip.isp_db_url';
+ const ORG_URL_OPTION_NAME = 'geoip.org_db_url';
+
+ private static $urlOptions = array(
+ 'loc' => self::LOC_URL_OPTION_NAME,
+ 'isp' => self::ISP_URL_OPTION_NAME,
+ 'org' => self::ORG_URL_OPTION_NAME,
+ );
+
+ /**
+ * Attempts to download new location, ISP & organization GeoIP databases and
+ * replace the existing ones w/ them.
+ */
+ public function update()
+ {
+ try
+ {
+ $locUrl = Piwik_GetOption(self::LOC_URL_OPTION_NAME);
+ if ($locUrl !== false)
+ {
+ $this->downloadFile('loc', $locUrl);
+ }
+
+ $ispUrl = Piwik_GetOption(self::ISP_URL_OPTION_NAME);
+ if ($ispUrl !== false)
+ {
+ $this->downloadFile('isp', $ispUrl);
+ }
+
+ $orgUrl = Piwik_GetOption(self::ORG_URL_OPTION_NAME);
+ if ($orgUrl !== false)
+ {
+ $this->downloadFile('org', $orgUrl);
+ }
+ }
+ catch (Exception $ex)
+ {
+ // message will already be prefixed w/ 'Piwik_UserCountry_GeoIPAutoUpdater: '
+ Piwik::log($ex->getMessage());
+ throw $ex;
+ }
+ }
+
+ /**
+ * Downloads a GeoIP database archive, extracts the .dat file and overwrites the existing
+ * old database.
+ *
+ * If something happens that causes the download to fail, no exception is thrown, but
+ * an error is logged.
+ *
+ * @param string $url URL to the database to download. The type of database is determined
+ * from this URL.
+ */
+ private function downloadFile( $dbType, $url )
+ {
+ $ext = Piwik_UserCountry_GeoIPAutoUpdater::getGeoIPUrlExtension($url);
+ $zippedFilename = Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$dbType][0].'.'.$ext;
+
+ $zippedOutputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($zippedFilename);
+
+ // download zipped file to misc dir
+ try
+ {
+ $success = Piwik_Http::sendHttpRequest($url, $timeout = 3600, $userAgent = null, $zippedOutputPath);
+ }
+ catch (Exception $ex)
+ {
+ throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to download '$url' to "
+ . "'$zippedOutputPath': " . $ex->getMessage());
+ }
+
+ if ($success !== true)
+ {
+ throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to download '$url' to "
+ . "'$zippedOutputPath'! (Unknown error)");
+ }
+
+ Piwik::log("Piwik_UserCountry_GeoIPAutoUpdater: successfully downloaded '$url'");
+
+ try
+ {
+ self::unzipDownloadedFile($zippedOutputPath, $unlink = true);
+ }
+ catch (Exception $ex)
+ {
+ throw new Exception("Piwik_UserCountry_GeoIPAutoUpdater: failed to unzip '$zippedOutputPath' after "
+ . "downloading " . "'$url': ".$ex->getMessage());
+ }
+
+ Piwik::log("Piwik_UserCountry_GeoIPAutoUpdater: successfully updated GeoIP database '$url'");
+ }
+
+ /**
+ * Unzips a downloaded GeoIP database. Only unzips .gz & .tar.gz files.
+ *
+ * @param string $path Path to zipped file.
+ * @param bool $unlink Whether to unlink archive or not.
+ */
+ public static function unzipDownloadedFile( $path, $unlink = false )
+ {
+ $parts = explode('.', basename($path));
+ $outputPath = Piwik_UserCountry_LocationProvider_GeoIp::getPathForGeoIpDatabase($parts[0].'.dat');
+
+ // extract file
+ if (substr($path, -7, 7) == '.tar.gz')
+ {
+ // find the .dat file in the tar archive
+ $unzip = Piwik_Unzip::factory('tar.gz', $path);
+ $content = $unzip->listContent();
+
+ if (empty($content))
+ {
+ throw new Exception(Piwik_Translate('UserCountry_CannotListContent',
+ array("'$path'", $unzip->errorInfo())));
+ }
+
+ $datFile = null;
+ foreach ($content as $info)
+ {
+ $archivedPath = $info['filename'];
+ if (basename($archivedPath) === basename($outputPath))
+ {
+ $datFile = $archivedPath;
+ }
+ }
+
+ if ($datFile === null)
+ {
+ throw new Exception(Piwik_Translate('UserCountry_CannotFindGeoIPDatabaseInArchive',
+ array(basename($outputPath), "'$path'")));
+ }
+
+ // extract JUST the .dat file
+ $unzipped = $unzip->extractInString($datFile);
+
+ if (empty($unzipped))
+ {
+ throw new Exception(Piwik_Translate('UserCountry_CannotUnzipDatFile',
+ array("'$path'", $unzip->errorInfo())));
+ }
+
+ // write unzipped to file
+ $fd = fopen($outputPath, 'wb');
+ fwrite($fd, $unzipped);
+ fclose($fd);
+ }
+ else if (substr($path, -3, 3) == '.gz')
+ {
+ $unzip = Piwik_Unzip::factory('gz', $path);
+ $success = $unzip->extract($outputPath);
+
+ if ($success !== true)
+ {
+ throw new Exception(Piwik_Translate('UserCountry_CannotUnzipDatFile',
+ array("'$path'", $unzip->errorInfo())));
+ }
+ }
+ else
+ {
+ $ext = end(explode(basename($path), '.', 2));
+ throw new Exception(Piwik_Translate('UserCountry_UnsupportedArchiveType', "'$ext'"));
+ }
+
+ // delete original archive
+ if ($unlink)
+ {
+ unlink($path);
+ }
+ }
+
+ /**
+ * Creates a ScheduledTask instance based on set option values.
+ *
+ * @return Piwik_ScheduledTask
+ */
+ public static function makeScheduledTask()
+ {
+ $instance = new Piwik_UserCountry_GeoIPAutoUpdater();
+
+ $schedulePeriodStr = self::getSchedulePeriod();
+
+ // created the scheduledtime instance, also, since GeoIP updates are done on tuesdays,
+ // get new DBs on Wednesday
+ switch ($schedulePeriodStr)
+ {
+ case self::SCHEDULE_PERIOD_WEEKLY:
+ $schedulePeriod = new Piwik_ScheduledTime_Weekly();
+ $schedulePeriod->setDay(3);
+ break;
+ case self::SCHEDULE_PERIOD_MONTHLY:
+ default:
+ $schedulePeriod = new Piwik_ScheduledTime_Monthly();
+ $schedulePeriod->setDayOfWeek(3, 0);
+ break;
+ }
+
+ return new Piwik_ScheduledTask($instance, 'update', $schedulePeriod, Piwik_ScheduledTask::LOWEST_PRIORITY);
+ }
+
+ /**
+ * Sets the options used by this class based on query parameter values.
+ *
+ * See setUpdaterOptions for query params used.
+ */
+ public static function setUpdaterOptionsFromUrl()
+ {
+ self::setUpdaterOptions(array(
+ 'loc' => Piwik_Common::getRequestVar('loc_db', false, 'string'),
+ 'isp' => Piwik_Common::getRequestVar('isp_db', false, 'string'),
+ 'org' => Piwik_Common::getRequestVar('org_db', false, 'string'),
+ 'period' => Piwik_Common::getRequestVar('period', false, 'string'),
+ ));
+ }
+
+ /**
+ * Sets the options used by this class based on the elements in $options.
+ *
+ * The following elements of $options are used:
+ * 'loc' - URL for location database.
+ * 'isp' - URL for ISP database.
+ * 'org' - URL for Organization database.
+ * 'period' - 'weekly' or 'monthly'. When to run the updates.
+ *
+ * @param array $options
+ */
+ public static function setUpdaterOptions( $options )
+ {
+ // set url options
+ foreach (self::$urlOptions as $optionKey => $optionName)
+ {
+ if (empty($options[$optionKey]))
+ {
+ continue;
+ }
+
+ Piwik_SetOption($optionName, $url = $options[$optionKey]);
+ }
+
+ // set period option
+ if (!empty($options['period']))
+ {
+ $period = $options['period'];
+ if ($period != self::SCHEDULE_PERIOD_MONTHLY
+ && $period != self::SCHEDULE_PERIOD_WEEKLY)
+ {
+ throw new Exception(Piwik_Translate(
+ 'UserCountry_InvalidGeoIPUpdatePeriod',
+ array("'$period'", "'".self::SCHEDULE_PERIOD_MONTHLY."', '".self::SCHEDULE_PERIOD_WEEKLY."'")
+ ));
+ }
+
+ Piwik_SetOption(self::SCHEDULE_PERIOD_OPTION_NAME, $period);
+ }
+ }
+
+ /**
+ * Returns true if the auto-updater is setup to update at least one type of
+ * database. False if otherwise.
+ *
+ * @return bool
+ */
+ public static function isUpdaterSetup()
+ {
+ if (Piwik_GetOption(self::LOC_URL_OPTION_NAME) !== false
+ || Piwik_GetOption(self::ISP_URL_OPTION_NAME) !== false
+ || Piwik_GetOption(self::ORG_URL_OPTION_NAME) !== false)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Retrieves the URLs used to update various GeoIP database files.
+ *
+ * @return array
+ */
+ public static function getConfiguredUrls()
+ {
+ $result = array();
+ foreach (self::$urlOptions as $key => $optionName)
+ {
+ $result[$key] = Piwik_GetOption($optionName);
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the confiured URL (if any) for a type of database.
+ *
+ * @param string $key 'loc', 'isp' or 'org'
+ * @return string|false
+ */
+ public static function getConfiguredUrl( $key )
+ {
+ return Piwik_GetOption(self::$urlOptions[$key]);
+ }
+
+ /**
+ * Performs a GeoIP database update.
+ */
+ public static function performUpdate()
+ {
+ $instance = new Piwik_UserCountry_GeoIPAutoUpdater();
+ $instance->update();
+ }
+
+ /**
+ * Returns the configured update period, either 'week' or 'month'. Defaults to
+ * 'month'.
+ *
+ * @return string
+ */
+ public static function getSchedulePeriod()
+ {
+ $period = Piwik_GetOption(self::SCHEDULE_PERIOD_OPTION_NAME);
+ if ($period === false)
+ {
+ $period = self::SCHEDULE_PERIOD_MONTHLY;
+ }
+ return $period;
+ }
+
+ /**
+ * Returns an array of strings for GeoIP databases that have update URLs configured, but
+ * are not present in the misc directory. Each string is a key describing the type of
+ * database (ie, 'loc', 'isp' or 'org').
+ *
+ * @return array
+ */
+ public static function getMissingDatabases()
+ {
+ $result = array();
+ foreach (self::getConfiguredUrls() as $key => $url)
+ {
+ if ($url !== false)
+ {
+ // if a database of the type does not exist, but there's a url to update, then
+ // a database is missing
+ $path = Piwik_UserCountry_LocationProvider_GeoIp::getPathToGeoIpDatabase(
+ Piwik_UserCountry_LocationProvider_GeoIp::$dbNames[$key]);
+ if ($path === false)
+ {
+ $result[] = $key;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns the extension of a URL used to update a GeoIP database, if it can be found.
+ */
+ public static function getGeoIPUrlExtension( $url )
+ {
+ // check for &suffix= query param that is special to MaxMind URLs
+ if (preg_match('/suffix=([^&]+)/', $url, $matches))
+ {
+ return $matches[1];
+ }
+
+ // use basename of url
+ $filenameParts = explode('.', basename($url), 2);
+ if (count($filenameParts) > 1)
+ {
+ return end($filenameParts);
+ }
+ else
+ {
+ return reset($filenameParts);
+ }
+ }
+}
diff --git a/plugins/UserCountry/LocationProvider/GeoIp.php b/plugins/UserCountry/LocationProvider/GeoIp.php
index 9c2d49a476..4cb0157169 100755
--- a/plugins/UserCountry/LocationProvider/GeoIp.php
+++ b/plugins/UserCountry/LocationProvider/GeoIp.php
@@ -17,6 +17,9 @@
*/
abstract class Piwik_UserCountry_LocationProvider_GeoIp extends Piwik_UserCountry_LocationProvider
{
+ /* For testing, use: 'http://piwik-team.s3.amazonaws.com/GeoLiteCity.dat.gz' */
+ const GEO_LITE_URL = 'http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz';
+
public static $geoIPDatabaseDir = 'misc';
/**
@@ -176,10 +179,9 @@ abstract class Piwik_UserCountry_LocationProvider_GeoIp extends Piwik_UserCountr
*/
public static function getPathToGeoIpDatabase( $possibleFileNames )
{
- $dir = PIWIK_INCLUDE_PATH.'/'.self::$geoIPDatabaseDir;
foreach ($possibleFileNames as $filename)
{
- $path = $dir.'/'.$filename;
+ $path = self::getPathForGeoIpDatabase($filename);
if (file_exists($path))
{
return $path;
@@ -189,6 +191,17 @@ abstract class Piwik_UserCountry_LocationProvider_GeoIp extends Piwik_UserCountr
}
/**
+ * Returns full path for a GeoIP database managed by Piwik.
+ *
+ * @param string $filename Name of the .dat file.
+ * @return string
+ */
+ public static function getPathForGeoIpDatabase( $filename )
+ {
+ return PIWIK_INCLUDE_PATH.'/'.self::$geoIPDatabaseDir.'/'.$filename;
+ }
+
+ /**
* Returns test IP used by isWorking and expected result.
*
* @return array eg. array('1.2.3.4', array(self::COUNTRY_CODE_KEY => ...))
@@ -206,6 +219,18 @@ abstract class Piwik_UserCountry_LocationProvider_GeoIp extends Piwik_UserCountr
}
return $result;
}
+
+ /**
+ * Returns true if there is a GeoIP database in the 'misc' directory.
+ *
+ * @return bool
+ */
+ public static function isDatabaseInstalled()
+ {
+ return self::getPathToGeoIpDatabase(self::$dbNames['loc'])
+ || self::getPathToGeoIpDatabase(self::$dbNames['isp'])
+ || self::getPathToGeoIpDatabase(self::$dbNames['org']);
+ }
}
diff --git a/plugins/UserCountry/UserCountry.php b/plugins/UserCountry/UserCountry.php
index 91e6feaf05..cefbb66d87 100644
--- a/plugins/UserCountry/UserCountry.php
+++ b/plugins/UserCountry/UserCountry.php
@@ -11,6 +11,11 @@
*/
/**
+ * @see plugins/UserCountry/GeoIPAutoUpdater.php
+ */
+require_once PIWIK_INCLUDE_PATH . '/plugins/UserCountry/GeoIPAutoUpdater.php';
+
+/**
*
* @package Piwik_UserCountry
*/
@@ -48,8 +53,10 @@ class Piwik_UserCountry extends Piwik_Plugin
'Goals.getReportsWithGoalMetrics' => 'getReportsWithGoalMetrics',
'API.getReportMetadata' => 'getReportMetadata',
'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
'AssetManager.getJsFiles' => 'getJsFiles',
'Tracker.getVisitorLocation' => 'getVisitorLocation',
+ 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
);
return $hooks;
}
@@ -57,6 +64,27 @@ class Piwik_UserCountry extends Piwik_Plugin
/**
* @param Piwik_Event_Notification $notification notification object
*/
+ function getScheduledTasks($notification)
+ {
+ $tasks = &$notification->getNotificationObject();
+
+ // add the auto updater task
+ $tasks[] = Piwik_UserCountry_GeoIPAutoUpdater::makeScheduledTask();
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles( $notification )
+ {
+ $cssFiles = &$notification->getNotificationObject();
+
+ $cssFiles[] = "plugins/UserCountry/templates/styles.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
public function getJsFiles( $notification )
{
$jsFiles = &$notification->getNotificationObject();
@@ -123,13 +151,10 @@ class Piwik_UserCountry extends Piwik_Plugin
*/
function addAdminMenu()
{
- if (Piwik::isUserIsSuperUser())
- {
- Piwik_AddAdminMenu('UserCountry_Geolocation',
- array('module' => 'UserCountry', 'action' => 'adminIndex'),
- Piwik::isUserHasSomeAdminAccess(),
- $order = 8);
- }
+ Piwik_AddAdminMenu('UserCountry_Geolocation',
+ array('module' => 'UserCountry', 'action' => 'adminIndex'),
+ Piwik::isUserIsSuperUser(),
+ $order = 8);
}
/**
diff --git a/plugins/UserCountry/templates/admin.js b/plugins/UserCountry/templates/admin.js
index 8d10ca6e5c..1077039cba 100755
--- a/plugins/UserCountry/templates/admin.js
+++ b/plugins/UserCountry/templates/admin.js
@@ -6,8 +6,10 @@
*/
$(document).ready(function() {
+ $('#geoip-download-progress,#geoip-updater-progressbar').progressbar({value: 1});
+
// handle switch current location provider
- $('.current-location-provider').change(function() {
+ $('.location-provider').change(function() {
if (!$(this).is(':checked')) return; // only handle radio buttons that get checked
var parent = $(this).parent(),
@@ -60,4 +62,140 @@ $(document).ready(function() {
return false;
});
+
+ // geoip database wizard
+ var downloadNextChunk = function(action, thisId, progressBarId, cont, extraData, callback)
+ {
+ var data = {
+ module: 'UserCountry',
+ action: action,
+ token_auth: piwik.token_auth,
+ 'continue': cont ? 1 : 0,
+ };
+ for (var k in extraData)
+ {
+ data[k] = extraData[k];
+ }
+
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(data, 'post');
+ ajaxRequest.setCallback(function(response) {
+ if (!response || response.error)
+ {
+ callback(response);
+ }
+ else
+ {
+ // update progress bar
+ var newProgressVal = Math.ceil((response.current_size / response.expected_file_size) * 100);
+ newProgressVal = Math.min(newProgressVal, 100);
+ $('#'+progressBarId).progressbar('option', 'value', newProgressVal);
+
+ // if incomplete, download next chunk, otherwise, show updater manager
+ if (newProgressVal < 100)
+ {
+ downloadNextChunk(action, thisId, progressBarId, true, extraData, callback);
+ }
+ else
+ {
+ callback(response);
+ }
+ }
+ });
+ ajaxRequest.send(false);
+ };
+
+ $('#start-download-free-geoip').click(function() {
+ $('#geoipdb-screen1').hide("slide", {direction: "left"}, 800, function() {
+ $('#geoipdb-screen2-download').fadeIn(1000);
+
+ // start download of free dbs
+ downloadNextChunk(
+ 'downloadFreeGeoIPDB',
+ 'geoipdb-screen2-download',
+ 'geoip-download-progress',
+ false,
+ {},
+ function(response) {
+ if (response.error)
+ {
+ // on error, show error & stop downloading
+ $('#'+thisId).fadeOut(1000, function() {
+ $('#manage-geoip-dbs').html(response.error);
+ });
+ }
+ else
+ {
+ $('#geoipdb-screen2-download').fadeOut(1000, function() {
+ $('#manage-geoip-dbs').html(response.next_screen);
+ });
+ }
+ }
+ );
+ });
+ });
+
+ $('body').on('click', '#start-automatic-update-geoip', function() {
+ $('#geoipdb-screen1').hide("slide", {direction: "left"}, 800, function () {
+ $('#geoipdb-update-info').fadeIn(1000);
+ });
+ });
+
+ $('body').on('click', '#update-geoip-links', function() {
+ $('#geoipdb-update-info-error').hide();
+
+ var currentDownloading = null,
+ updateGeoIPSuccess = function(response)
+ {
+ if (response && response.error)
+ {
+ $('#geoip-progressbar-container').hide();
+ $('#geoipdb-update-info-error').html(response.error).show();
+ }
+ else if (response && response.to_download)
+ {
+ var continuing = currentDownloading == response.to_download;
+ currentDownloading = response.to_download;
+
+ // show progress bar w/ message
+ $('#geoip-updater-progressbar').progressbar('option', 'value', 1);
+ $('#geoip-updater-progressbar-label').html(response.to_download_label);
+ $('#geoip-progressbar-container').show();
+
+ // start/continue download
+ downloadNextChunk(
+ 'downloadMissingGeoIpDb', 'geoipdb-update-info', 'geoip-updater-progressbar',
+ continuing, {key: response.to_download}, updateGeoIPSuccess);
+ }
+ else
+ {
+ $('#geoipdb-update-info-error').hide();
+ $('#geoip-updater-progressbar-label').html('');
+ $('#geoip-progressbar-container').hide();
+
+ // fade in/out Done message
+ $('#done-updating-updater').fadeIn(1000, function() {
+ setTimeout(function() {
+ $('#done-updating-updater').fadeOut(1000);
+ }, 3000);
+ });
+ }
+ };
+
+ // setup the auto-updater
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams({
+ period: $('#geoip-update-period-cell>input:checked').val()
+ }, 'get');
+ ajaxRequest.addParams({
+ module: 'UserCountry',
+ action: 'updateGeoIPLinks',
+ token_auth: piwik.token_auth,
+ loc_db: $('#geoip-location-db').val(),
+ isp_db: $('#geoip-isp-db').val(),
+ org_db: $('#geoip-org-db').val()
+ }, 'post');
+ ajaxRequest.setCallback(updateGeoIPSuccess);
+ ajaxRequest.send(false);
+ });
});
diff --git a/plugins/UserCountry/templates/adminIndex.tpl b/plugins/UserCountry/templates/adminIndex.tpl
index a4ddcfa9bc..ecf51a265d 100755
--- a/plugins/UserCountry/templates/adminIndex.tpl
+++ b/plugins/UserCountry/templates/adminIndex.tpl
@@ -22,7 +22,7 @@
<p>&nbsp;</p>
{/if}
-<table class="adminTable">
+<table class="adminTable locationProviderTable">
<tr>
<th>{'UserCountry_LocationProvider'|translate}</th>
<th>{'General_Description'|translate}</th>
@@ -32,19 +32,19 @@
<tr>
<td width="140">
<p>
- <input class="current-location-provider" name="current-location-provider" value="{$id}" type="radio" {if $currentProviderId eq $id}checked="checked"{/if} id="provider_input_{$id}" style="cursor:pointer" {if $provider.status neq 1}disabled="disabled"{/if}/>
- <label for="provider_input_{$id}" style="font-size:1.2em">{$provider.title|translate}</label><br/>
+ <input class="location-provider" name="location-provider" value="{$id}" type="radio" {if $currentProviderId eq $id}checked="checked"{/if} id="provider_input_{$id}" {if $provider.status neq 1}disabled="disabled"{/if}/>
+ <label for="provider_input_{$id}">{$provider.title|translate}</label><br/>
<span class='loadingPiwik' style='display:none'><img src='./themes/default/images/loading-blue.gif' /></span>
<span class="ajaxSuccess" style='display:none'>{'General_Done'|translate}</span>
</p>
- <p style="margin-left:.5em">
+ <p class="loc-provider-status">
<strong><em>
{if $provider.status eq 0}
- {'General_NotInstalled'|translate}
+ <span class="is-not-installed">{'General_NotInstalled'|translate}</span>
{elseif $provider.status eq 1}
- <span style="color:green">{'General_Installed'|translate}</span>
+ <span class="is-installed">{'General_Installed'|translate}</span>
{elseif $provider.status eq 2}
- <span style="color:red">{'General_Broken'|translate}</span>
+ <span class="is-broken">{'General_Broken'|translate}</span>
{/if}
</em></strong>
</p>
@@ -93,5 +93,33 @@
</div>
+<h2>{'UserCountry_GeoIPDatabases'|translate}</h2>
+
+{if $showGeoIPUpdateSection}
+<div id="manage-geoip-dbs" style="width:900px">
+
+{if !$geoIPDatabasesInstalled}
+<div id="geoipdb-screen1">
+ <p>{'UserCountry_PiwikNotManagingGeoIPDBs'|translate}</p>
+ <div class="geoipdb-column-1">
+ <p>{'UserCountry_IWantToDownloadFreeGeoIP'|translate}</p>
+ <input type="button" class="submit" value="{'General_GetStarted'|translate}..." id="start-download-free-geoip"/>
+ </div>
+ <div class="geoipdb-column-2">
+ <p>{'UserCountry_IPurchasedGeoIPDBs'|translate}</p>
+ <input type="button" class="submit" value="{'General_GetStarted'|translate}..." id="start-automatic-update-geoip"/>
+ </div>
+</div>
+<div id="geoipdb-screen2-download" style="display:none">
+ <p class='loadingPiwik'><img src='./themes/default/images/loading-blue.gif' />{'UserCountry_DownloadingDb'|translate:'GeoLiteCity.dat'}...</p>
+ <div id="geoip-download-progress"></div>
+</div>
+{/if}
+{include file="UserCountry/templates/updaterSetup.tpl"}
+{else}
+<p style="width:900px" class="form-description">{'UserCountry_CannotSetupGeoIPAutoUpdating'|translate}</p>
+{/if}
+</div>
+
{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/UserCountry/templates/styles.css b/plugins/UserCountry/templates/styles.css
new file mode 100755
index 0000000000..4ded571aa5
--- /dev/null
+++ b/plugins/UserCountry/templates/styles.css
@@ -0,0 +1,69 @@
+.locationProviderTable label {
+ font-size: 1.2em;
+}
+
+input.location-provider {
+ cursor:pointer;
+}
+
+span.is-installed {
+ color:green;
+}
+
+span.is-broken {
+ color:red;
+}
+
+.loc-provider-status {
+ margin-left:.5em;
+}
+
+#manage-geoip-dbs {
+ width:900px;
+ height: 20em;
+}
+
+#geoipdb-update-info tr input[type="text"],#geoipdb-screen2-update tr input[type="text"] {
+ width: 90%;
+}
+
+#geoipdb-screen1>div {
+ display: inline-block;
+ vertical-align:top;
+}
+
+#geoipdb-screen1>div>p {
+ font-size: 2em;
+ height: 4em;
+}
+
+.geoipdb-column-1,.geoipdb-column-2 {
+ width:396px;
+}
+.geoipdb-column-1 {
+ margin-right: 50px;
+}
+.geoipdb-column-2 {
+ border-left: solid #999 1px;
+ padding-left: 50px;
+}
+.geoipdb-column-1>p {
+ padding-left: 20px;
+}
+
+.error {
+ font-weight:bold;
+ color:red;
+ padding:4px 8px 4px 8px;
+}
+
+#geoip-updater-progressbar-label {
+ float: left;
+ margin: -24px 24px;
+}
+
+#geoip-progressbar-container,#geoipdb-update-info-error {
+ margin: 22px 24px;
+ display:inline-block;
+}
+
diff --git a/plugins/UserCountry/templates/updaterSetup.tpl b/plugins/UserCountry/templates/updaterSetup.tpl
new file mode 100755
index 0000000000..96d55b5f1a
--- /dev/null
+++ b/plugins/UserCountry/templates/updaterSetup.tpl
@@ -0,0 +1,53 @@
+<div id="geoipdb-update-info" {if !$geoIPDatabasesInstalled}style="display:none"{/if}>
+ <p>{'UserCountry_GeoIPUpdaterInstructions'|translate:'<a href="http://www.maxmind.com/en/download_files?rId=piwik" _target="blank">':'</a>':'<a href="http://www.maxmind.com/?rId=piwik">':'</a>'}</p>
+ {if $geoIPDatabasesInstalled}
+ <p>{'UserCountry_GeoIPUpdaterIntro'|translate}:</p>
+ {/if}
+ <table class="adminTable" style="width:900px">
+ <tr>
+ <th>{'Live_GoalType'|translate}</th>
+ <th>{'Actions_ColumnDownloadURL'|translate}</th>
+ <th></th>
+ </tr>
+ <tr>
+ <td width="140">{'UserCountry_LocationDatabase'|translate}</td>
+ <td><input type="text" id="geoip-location-db" value="{$geoIPLocUrl}"/></td>
+ <td width="164">
+ {capture assign=locationHint}
+ {'UserCountry_LocationDatabaseHint'|translate}
+ {/capture}
+ {$locationHint|inlineHelp}
+ </td>
+ </tr>
+ <tr>
+ <td width="140">{'UserCountry_ISPDatabase'|translate}</td>
+ <td><input type="text" id="geoip-isp-db" value="{$geoIPIspUrl}"/></td>
+ </tr>
+ <tr>
+ <td width="140">{'UserCountry_OrgDatabase'|translate}</td>
+ <td><input type="text" id="geoip-org-db" value="{$geoIPOrgUrl}"/></td>
+ </tr>
+ <tr>
+ <td width="140">{'General_Period'|translate}</td>
+ <td id="geoip-update-period-cell">
+ <input type="radio" name="geoip-update-period" value="month" id="geoip-update-period-month" {if $geoIPUpdatePeriod eq 'month'}checked="checked"{/if}/>
+ <label for="geoip-update-period-month">{'General_Monthly'|translate}</label>
+
+ <input type="radio" name="geoip-update-period" value="week" id="geoip-update-period-week" {if $geoIPUpdatePeriod eq 'week'}checked="checked"{/if}/>
+ <label for="geoip-update-period-week">{'General_Weekly'|translate}</label>
+ </td>
+ <td width="164">&nbsp;</td>
+ </tr>
+ </table>
+ <p style="display:inline-block;vertical-align:top">
+ <input type="button" class="submit" value="{if !$geoIPDatabasesInstalled}{'General_Continue'|translate}{else}{'General_Save'|translate}{/if}" id="update-geoip-links"/>
+ </p>
+ <div style="display:inline-block;width:700px">
+ <span style="display:none" class="ajaxSuccess" id="done-updating-updater">{'General_Done'|translate}!</span>
+ <span id="geoipdb-update-info-error" style="display:none" class="error"></span>
+ <div id="geoip-progressbar-container" style="display:none">
+ <div id="geoip-updater-progressbar"></div>
+ <span id="geoip-updater-progressbar-label"></span>
+ </div>
+ </div>
+</div>