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-05-26 23:39:16 +0400
committerbenakamoorthi <benaka.moorthi@gmail.com>2012-05-26 23:39:16 +0400
commit71fc2e87eb99212ccd0da6235a8410f55c39c7b5 (patch)
tree6c1ca494613399a26eedbbff0247f46a63ecd84e /plugins
parent6add1fa233d9312651d79efb5f76fb5da62b0960 (diff)
Fixes #3004, redesigned DBStats plugin, added several new reports including database space taken up by tracker tables, database space taken up by archive blob tables, database space taken up by archive metric tables, database space taken up by individual reports & database space taken up by individual metrics.
Notes: * Added ability to highlight the summary row in ViewDataTable and the ability to always show the summary row regardless of what report page is currently being shown. * Fixed small issue w/ ViewDataTable::hasReportBeenPurged: default values should be specified in calls to getRequestVar. * Added Piwik_FetchAssoc function to PluginsFunctions/Sql.php * Augmented ColumnCallbackAddColumnQuotient filter so the divisor can be obtained from the summary row. * Modified AddSummaryRow filter so it wouldn't delete rows if desired. * Added ColumnCallbackAddColumn filer that adds a new column to each row based on the result of a callback. * Modified ColumnCallbackReplace filter so callback can operate on more than one column value if desired. * Modified Limit filter so, if desired, the summary row can be exempted from deletion. * Added GroupBy filter that groups/sums rows by the result of a callback function. * Fixed GenerateGraphData.php bug where priority filters were not called on view data table. * Added getPrettyNumber utility function. git-svn-id: http://dev.piwik.org/svn/trunk@6324 59fd770c-687e-43c8-a1e3-f5a4ff64c105
Diffstat (limited to 'plugins')
-rw-r--r--plugins/CoreAdminHome/templates/generalSettings.tpl2
-rw-r--r--plugins/CoreHome/templates/datatable.css4
-rw-r--r--plugins/CoreHome/templates/datatable.js3
-rw-r--r--plugins/CoreHome/templates/datatable.tpl2
-rw-r--r--plugins/DBStats/API.php334
-rw-r--r--plugins/DBStats/Controller.php352
-rw-r--r--plugins/DBStats/DBStats.php42
-rwxr-xr-xplugins/DBStats/MySQLMetadataProvider.php378
-rw-r--r--plugins/DBStats/templates/DBStats.tpl52
-rwxr-xr-xplugins/DBStats/templates/index.tpl154
-rw-r--r--plugins/PrivacyManager/templates/privacySettings.tpl2
11 files changed, 1190 insertions, 135 deletions
diff --git a/plugins/CoreAdminHome/templates/generalSettings.tpl b/plugins/CoreAdminHome/templates/generalSettings.tpl
index e11c6f2669..9471d7e8e2 100644
--- a/plugins/CoreAdminHome/templates/generalSettings.tpl
+++ b/plugins/CoreAdminHome/templates/generalSettings.tpl
@@ -165,7 +165,7 @@
{capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
<h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
<p>
- {'PrivacyManager_DeleteDataDescription'|translate}
+ {'PrivacyManager_DeleteDataDescription'|translate} {'PrivacyManager_DeleteDataDescription2'|translate}
<br/>
<a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
{'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
diff --git a/plugins/CoreHome/templates/datatable.css b/plugins/CoreHome/templates/datatable.css
index f0fa02828c..3f05071e23 100644
--- a/plugins/CoreHome/templates/datatable.css
+++ b/plugins/CoreHome/templates/datatable.css
@@ -153,6 +153,10 @@ table.dataTable td.labelodd {
background: #fff url(images/bullet1.gif) no-repeat;
}
+.dataTable tr.highlight td {
+ background-color: #ECF9DD;
+ font-weight: bold;
+}
table.dataTable td.label,table.subActionsDataTable td.label,table.actionsDataTable td.label {
border-top: 0;
diff --git a/plugins/CoreHome/templates/datatable.js b/plugins/CoreHome/templates/datatable.js
index cb6827a766..1358ebd189 100644
--- a/plugins/CoreHome/templates/datatable.js
+++ b/plugins/CoreHome/templates/datatable.js
@@ -422,6 +422,8 @@ dataTable.prototype =
var totalRows = Number(self.param.totalRows);
offsetEndDisp = offsetEnd;
+ if (self.param.keep_summary_row) --totalRows;
+
if(offsetEnd > totalRows) offsetEndDisp = totalRows;
// only show this string if there is some rows in the datatable
@@ -439,6 +441,7 @@ dataTable.prototype =
var offsetEnd = Number(self.param.filter_offset)
+ Number(self.param.filter_limit);
var totalRows = Number(self.param.totalRows);
+ if (self.param.keep_summary_row) --totalRows;
if(offsetEnd < totalRows)
{
$(this).css('display','inline');
diff --git a/plugins/CoreHome/templates/datatable.tpl b/plugins/CoreHome/templates/datatable.tpl
index 235e8bcc30..766eb70808 100644
--- a/plugins/CoreHome/templates/datatable.tpl
+++ b/plugins/CoreHome/templates/datatable.tpl
@@ -35,7 +35,7 @@
<tbody>
{foreach from=$arrayDataTable item=row}
- <tr {if $row.idsubdatatable && $javascriptVariablesToSet.controllerActionCalledWhenRequestSubTable != null}class="subDataTable" id="{$row.idsubdatatable}"{/if}>
+ <tr {if $row.idsubdatatable && $javascriptVariablesToSet.controllerActionCalledWhenRequestSubTable != null}class="subDataTable" id="{$row.idsubdatatable}"{/if}{if isset($row.issummaryrow) && $row.issummaryrow && $properties.highlight_summary_row} class="highlight"{/if}>
{foreach from=$dataTableColumns item=column}
<td>
{include file="CoreHome/templates/datatable_cell.tpl"}
diff --git a/plugins/DBStats/API.php b/plugins/DBStats/API.php
index eb35cf86d5..d01b714d03 100644
--- a/plugins/DBStats/API.php
+++ b/plugins/DBStats/API.php
@@ -11,13 +11,23 @@
*/
/**
+ * @see plugins/DBStats/MySQLMetadataProvider.php
+ */
+require_once PIWIK_INCLUDE_PATH . '/plugins/DBStats/MySQLMetadataProvider.php';
+
+/**
* DBStats API is used to request the overall status of the Mysql tables in use by Piwik.
*
* @package Piwik_DBStats
*/
class Piwik_DBStats_API
{
+ /** Singleton instance of this class. */
static private $instance = null;
+
+ /**
+ * Gets or creates the DBStats API singleton.
+ */
static public function getInstance()
{
if (self::$instance == null)
@@ -26,109 +36,283 @@ class Piwik_DBStats_API
}
return self::$instance;
}
-
- public function getDBStatus()
+
+ /**
+ * The MySQLMetadataProvider instance that fetches table/db status information.
+ */
+ private $metadataProvider;
+
+ /**
+ * Constructor.
+ */
+ public function __construct()
{
- Piwik::checkUserIsSuperUser();
-
- if(function_exists('mysql_connect'))
- {
- $configDb = Piwik_Config::getInstance()->database;
- $link = mysql_connect($configDb['host'], $configDb['username'], $configDb['password']);
- $status = mysql_stat($link);
- mysql_close($link);
- $status = explode(" ", $status);
- }
- else
+ $this->metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
+ }
+
+ /**
+ * Forces the next table status request to issue a query by reseting the table status cache.
+ */
+ public function resetTableStatuses()
+ {
+ self::getInstance()->metadataProvider = new Piwik_DBStats_MySQLMetadataProvider();
+ }
+
+ /**
+ * Gets some general information about this Piwik installation, including the count of
+ * websites tracked, the count of users and the total space used by the database.
+ *
+ * @return array Contains the website count, user count and total space used by the database.
+ */
+ public function getGeneralInformation()
+ {
+ // calculate total size
+ $totalSpaceUsed = 0;
+ foreach ($this->metadataProvider->getAllTablesStatus() as $status)
{
- $db = Zend_Registry::get('db');
-
- $fullStatus = $db->fetchAssoc('SHOW STATUS;');
- if(empty($fullStatus)) {
- throw new Exception('Error, SHOW STATUS failed');
- }
-
- $status = array(
- 'Uptime' => $fullStatus['Uptime']['Value'],
- 'Threads' => $fullStatus['Threads_running']['Value'],
- 'Questions' => $fullStatus['Questions']['Value'],
- 'Slow queries' => $fullStatus['Slow_queries']['Value'],
- 'Flush tables' => $fullStatus['Flush_commands']['Value'],
- 'Open tables' => $fullStatus['Open_tables']['Value'],
-// 'Opens: ', // not available via SHOW STATUS
-// 'Queries per second avg' =/ // not available via SHOW STATUS
- );
+ $totalSpaceUsed += $status['Data_length'] + $status['Index_length'];
}
-
- return $status;
+
+ $siteTableStatus = $this->metadataProvider->getTableStatus('site');
+ $userTableStatus = $this->metadataProvider->getTableStatus('user');
+
+ $siteCount = $siteTableStatus['Rows'];
+ $userCount = $userTableStatus['Rows'];
+
+ return array($siteCount, $userCount, $totalSpaceUsed);
}
- public function getTableStatus($table, $field = '')
+ /**
+ * Gets general database info that is not specific to any table.
+ *
+ * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-status.html .
+ */
+ public function getDBStatus()
{
Piwik::checkUserIsSuperUser();
- $db = Zend_Registry::get('db');
- // http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html
- $tables = $db->fetchAll("SHOW TABLE STATUS LIKE ". $db->quote($table));
-
- if(!isset($tables[0])) {
- throw new Exception('Error, table or field not found');
- }
- if ($field == '')
- {
- return $tables[0];
- }
- else
- {
- return $tables[0][$field];
- }
+ return $this->metadataProvider->getDBStatus();
}
/**
- * Gets the result of a SHOW TABLE STATUS query for every table in the DB.
+ * Returns a datatable summarizing how data is distributed among Piwik tables.
+ *
+ * This function will group tracker tables, numeric archive tables, blob archive tables
+ * and other tables together so only four rows are shown.
*
- * @return array The table information.
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
*/
- public function getAllTablesStatus()
+ public function getDatabaseUsageSummary()
{
Piwik::checkUserIsSuperUser();
- $tablesPiwik = Piwik::getTablesInstalled();
- $result = array();
- foreach(Piwik_FetchAll("SHOW TABLE STATUS") as $t)
+ $emptyRow = array('data_size' => 0, 'index_size' => 0, 'row_count' => 0);
+ $rows = array(
+ 'tracker_data' => $emptyRow,
+ 'metric_data' => $emptyRow,
+ 'report_data' => $emptyRow,
+ 'other_data' => $emptyRow
+ );
+
+ foreach ($this->metadataProvider->getAllTablesStatus() as $status)
{
- if (in_array($t['Name'], $tablesPiwik))
+ if ($this->isNumericArchiveTable($status['Name']))
{
- $result[] = $t;
+ $rowToAddTo = &$rows['metric_data'];
}
+ else if ($this->isBlobArchiveTable($status['Name']))
+ {
+ $rowToAddTo = &$rows['report_data'];
+ }
+ else if ($this->isTrackerTable($status['Name']))
+ {
+ $rowToAddTo = &$rows['tracker_data'];
+ }
+ else
+ {
+ $rowToAddTo = &$rows['other_data'];
+ }
+
+ $rowToAddTo['data_size'] += $status['Data_length'];
+ $rowToAddTo['index_size'] += $status['Index_length'];
+ $rowToAddTo['row_count'] += $status['Rows'];
}
+ $result = new Piwik_DataTable();
+ $result->addRowsFromArrayWithIndexLabel($rows);
return $result;
}
- public function getAllTablesStatusPretty()
+ /**
+ * Returns a datatable describing how much space is taken up by each log table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getTrackerDataSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllLogTableStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each numeric
+ * archive table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getMetricDataSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllNumericArchiveStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each numeric
+ * archive table, grouped by year.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getMetricDataSummaryByYear()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $dataTable = $this->getMetricDataSummary();
+
+ $getTableYear = array('Piwik_DBStats_API', 'getArchiveTableYear');
+ $dataTable->filter('GroupBy', array('label', $getTableYear));
+
+ return $dataTable;
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each blob
+ * archive table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getReportDataSummary()
{
Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllBlobArchiveStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by each blob
+ * archive table, grouped by year.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getReportDataSummaryByYear()
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $dataTable = $this->getReportDataSummary();
+
+ $getTableYear = array('Piwik_DBStats_API', 'getArchiveTableYear');
+ $dataTable->filter('GroupBy', array('label', $getTableYear));
- // http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html
- $total = array('Name' => 'Total', 'Data_length' => 0, 'Index_length' => 0, 'Rows' => 0);
- $table = $this->getAllTablesStatus();
- foreach($table as &$t)
+ return $dataTable;
+ }
+
+ /**
+ * Returns a datatable describing how much space is taken up by 'admin' tables.
+ *
+ * An 'admin' table is a table that is not central to analytics functionality.
+ * So any table that isn't an archive table or a log table is an 'admin' table.
+ *
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getAdminDataSummary()
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->getTablesSummary($this->metadataProvider->getAllAdminTableStatus());
+ }
+
+ /**
+ * Returns a datatable describing how much total space is taken up by each
+ * individual report type.
+ *
+ * Goal reports and reports of the format .*_[0-9]+ are grouped together.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getIndividualReportsSummary( $forceCache = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->metadataProvider->getRowCountsAndSizeByBlobName($forceCache);
+ }
+
+ /**
+ * Returns a datatable describing how much total space is taken up by each
+ * individual metric type.
+ *
+ * Goal metrics, metrics of the format .*_[0-9]+ and 'done...' metrics are grouped together.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable A datatable with three columns: 'data_size', 'index_size', 'row_count'.
+ */
+ public function getIndividualMetricsSummary( $forceCache = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ return $this->metadataProvider->getRowCountsAndSizeByMetricName($forceCache);
+ }
+
+ /**
+ * Returns a datatable representation of a set of table statuses.
+ *
+ * @param array $statuses The table statuses to summarize.
+ * @return Piwik_DataTable
+ */
+ private function getTablesSummary( $statuses )
+ {
+ $dataTable = new Piwik_DataTable();
+ foreach ($statuses as $status)
{
- $total['Data_length'] += $t['Data_length'];
- $total['Index_length'] += $t['Index_length'];
- $total['Rows'] += $t['Rows'];
-
- $t['Total_length'] = Piwik::getPrettySizeFromBytes($t['Index_length']+$t['Data_length']);
- $t['Data_length'] = Piwik::getPrettySizeFromBytes($t['Data_length']);
- $t['Index_length'] = Piwik::getPrettySizeFromBytes($t['Index_length']);
- $t['Rows'] = Piwik::getPrettySizeFromBytes($t['Rows']);
+ $dataTable->addRowFromSimpleArray(array(
+ 'label' => $status['Name'],
+ 'data_size' => $status['Data_length'],
+ 'index_size' => $status['Index_length'],
+ 'row_count' => $status['Rows']
+ ));
+ }
+ return $dataTable;
+ }
+
+ /** Returns true if $name is the name of a numeric archive table, false if otherwise. */
+ private function isNumericArchiveTable( $name )
+ {
+ return strpos($name, Piwik_Common::prefixTable('archive_numeric_')) === 0;
+ }
+
+ /** Returns true if $name is the name of a blob archive table, false if otherwise. */
+ private function isBlobArchiveTable( $name )
+ {
+ return strpos($name, Piwik_Common::prefixTable('archive_blob_')) === 0;
+ }
+
+ /** Returns true if $name is the name of a log table, false if otherwise. */
+ private function isTrackerTable( $name )
+ {
+ return strpos($name, Piwik_Common::prefixTable('log_')) === 0;
+ }
+
+ /**
+ * Gets the year of an archive table from its name.
+ *
+ * @param string $tableName
+ * @param string The year.
+ *
+ * @ignore
+ */
+ public static function getArchiveTableYear( $tableName )
+ {
+ if (preg_match("/archive_(?:numeric|blob)_([0-9]+)_/", $tableName, $matches) === 0)
+ {
+ return '';
}
- $total['Total_length'] = Piwik::getPrettySizeFromBytes($total['Data_length']+$total['Index_length']);
- $total['Data_length'] = Piwik::getPrettySizeFromBytes($total['Data_length']);
- $total['Index_length'] = Piwik::getPrettySizeFromBytes($total['Index_length']);
- $total['TotalRows'] = Piwik::getPrettySizeFromBytes($total['Rows']);
- $table['Total'] = $total;
- return $table;
+ return $matches[1];
}
}
diff --git a/plugins/DBStats/Controller.php b/plugins/DBStats/Controller.php
index 91444f91ff..e0fee92c94 100644
--- a/plugins/DBStats/Controller.php
+++ b/plugins/DBStats/Controller.php
@@ -16,13 +16,357 @@
*/
class Piwik_DBStats_Controller extends Piwik_Controller_Admin
{
- function index()
+ /**
+ * Returns the index for this plugin. Shows every other report defined by this plugin,
+ * except the '...ByYear' reports. These can be loaded as related reports.
+ *
+ * Also, the 'getIndividual...Summary' reports are loaded by AJAX, as they can take
+ * a significant amount of time to load on setups w/ lots of websites.
+ */
+ public function index()
{
Piwik::checkUserIsSuperUser();
- $view = Piwik_View::factory('DBStats');
- $view->tablesStatus = Piwik_DBStats_API::getInstance()->getAllTablesStatusPretty();
+ $view = Piwik_View::factory('index');
$this->setBasicVariablesView($view);
$view->menu = Piwik_GetAdminMenu();
- echo $view->render();
+
+ $view->databaseUsageSummary = $this->getDatabaseUsageSummary(true);
+ $view->trackerDataSummary = $this->getTrackerDataSummary(true);
+ $view->metricDataSummary = $this->getMetricDataSummary(true);
+ $view->reportDataSummary = $this->getReportDataSummary(true);
+ $view->adminDataSummary = $this->getAdminDataSummary(true);
+
+ list($siteCount, $userCount, $totalSpaceUsed) = Piwik_DBStats_API::getInstance()->getGeneralInformation();
+ $view->siteCount = Piwik::getPrettyNumber($siteCount);
+ $view->userCount = Piwik::getPrettyNumber($userCount);
+ $view->totalSpaceUsed = Piwik::getPrettySizeFromBytes($totalSpaceUsed);
+
+ echo $view->render();
+ }
+
+ /**
+ * Shows a datatable that displays how much space the tracker tables, numeric
+ * archive tables, report tables and other tables take up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getDatabaseUsageSummary( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'graphPie', $orderDir = 'desc',
+ $addPercentColumn = true);
+ $view->disableOffsetInformationAndPaginationControls();
+
+ // translate the labels themselves
+ $translateSummaryLabel = array($this, 'translateSummarylabel');
+ $view->queueFilter('ColumnCallbackReplace', array(array('label'), $translateSummaryLabel),
+ $runBeforeGenericFilters = true);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each individual log table
+ * takes up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getTrackerDataSummary( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+
+ $view = $this->getDataTableView(__FUNCTION__);
+ $view->disableOffsetInformationAndPaginationControls();
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each numeric archive table
+ * takes up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getMetricDataSummary( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc');
+ $view->addRelatedReports(array(
+ 'DBStats.getMetricDataSummaryByYear' => Piwik_Translate('DBStats_MetricDataByYear')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each numeric archive table
+ * takes up in the MySQL database, for each year of numeric data.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getMetricDataSummaryByYear( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc',
+ $addPercentColumn = false, $labelKey = 'CoreHome_PeriodYear');
+ $view->addRelatedReports(array('DBStats.getMetricDataSummary' => Piwik_Translate('DBStats_MetricTables')));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each blob archive table
+ * takes up in the MySQL database.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getReportDataSummary( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc');
+ $view->addRelatedReports(array(
+ 'DBStats.getReportDataSummaryByYear' => Piwik_Translate('DBStats_ReportDataByYear')
+ ));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each blob archive table
+ * takes up in the MySQL database, for each year of blob data.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getReportDataSummaryByYear( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'desc',
+ $addPercentColumn = false, $labelKey = 'CoreHome_PeriodYear');
+ $view->addRelatedReports(array('DBStats.getReportDataSummary' => Piwik_Translate('DBStats_ReportTables')));
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays how many occurances there are of each individual
+ * report type stored in the MySQL database.
+ *
+ * Goal reports and reports of the format: .*_[0-9]+ are grouped together.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getIndividualReportsSummary( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'asc',
+ $addPercentColumn = false, $labelKey = 'General_Report',
+ $sizeColumns = array('estimated_size'));
+
+ // this report table has some extra columns that shouldn't be shown
+ if ($view instanceof Piwik_ViewDataTable_HtmlTable)
+ {
+ $view->setColumnsToDisplay(array('label', 'row_count', 'estimated_size'));
+ }
+
+ $this->setIndividualSummaryFooterMessage($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays how many occurances there are of each individual
+ * metric type stored in the MySQL database.
+ *
+ * Goal metrics, metrics of the format .*_[0-9]+ and 'done...' metrics are grouped together.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getIndividualMetricsSummary( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table', $orderDir = 'asc',
+ $addPercentColumn = false, $labelKey = 'General_Metric',
+ $sizeColumns = array('estimated_size'));
+
+ $this->setIndividualSummaryFooterMessage($view);
+
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Shows a datatable that displays the amount of space each 'admin' table takes
+ * up in the MySQL database.
+ *
+ * An 'admin' table is a table that is not central to analytics functionality.
+ * So any table that isn't an archive table or a log table is an 'admin' table.
+ *
+ * @param bool $fetch If true, the rendered HTML datatable is returned, otherwise,
+ * it is echoed.
+ */
+ public function getAdminDataSummary( $fetch = false )
+ {
+ Piwik::checkUserIsSuperUser();
+ $view = $this->getDataTableView(__FUNCTION__, $viewType = 'table');
+ $view->disableOffsetInformationAndPaginationControls();
+ return $this->renderView($view, $fetch);
+ }
+
+ /**
+ * Utility function that creates and prepares a ViewDataTable for this plugin.
+ */
+ private function getDataTableView( $function, $viewType = 'table', $orderDir = 'asc', $addPercentColumn = false,
+ $labelKey = 'DBStats_Table', $sizeColumns = array('data_size', 'index_size'),
+ $limit = 25 )
+ {
+ $columnTranslations = array(
+ 'label' => Piwik_Translate($labelKey),
+ 'year' => Piwik_Translate('CoreHome_PeriodYear'),
+ 'data_size' => Piwik_Translate('DBStats_DataSize'),
+ 'index_size' => Piwik_Translate('DBStats_IndexSize'),
+ 'total_size' => Piwik_Translate('DBStats_TotalSize'),
+ 'row_count' => Piwik_Translate('DBStats_RowCount'),
+ 'percent_total' => '%&nbsp;'.Piwik_Translate('DBStats_DBSize'),
+ 'estimated_size' => Piwik_Translate('DBStats_EstimatedSize')
+ );
+
+ $view = Piwik_ViewDataTable::factory($viewType);
+ $view->init($this->pluginName, $function, "DBStats.$function");
+ $view->setSortedColumn('label', $orderDir);
+ $view->setLimit($limit);
+ $view->setHighlightSummaryRow(true);
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->disableTagCloud();
+ $view->disableShowAllColumns();
+ $view->alwaysShowSummaryRow();
+
+ // translate columns
+ foreach ($columnTranslations as $columnName => $translation)
+ {
+ $view->setColumnTranslation($columnName, $translation);
+ }
+
+ // add total_size column (if necessary columns are present)
+ if (in_array('data_size', $sizeColumns) && in_array('index_size', $sizeColumns))
+ {
+ $getTotalTableSize = array($this, 'getTotalTableSize');
+ $view->queueFilter('ColumnCallbackAddColumn',
+ array(array('data_size', 'index_size'), 'total_size', $getTotalTableSize),
+ $runBeforeGenericFilters = true);
+
+ $sizeColumns[] = 'total_size';
+ }
+
+ $runPrettySizeFilterBeforeGeneric = false;
+ $fixedMemoryUnit = false;
+ if ($view instanceof Piwik_ViewDataTable_HtmlTable) // if displaying a table
+ {
+ $view->disableRowEvolution();
+
+ // add summary row only if displaying a table
+ $view->queueFilter('AddSummaryRow', array(0, Piwik_Translate('General_Total'), 'label', false),
+ $runBeforeGenericFilters = true);
+
+ // add other filters
+ if ($addPercentColumn && in_array('total_size', $sizeColumns))
+ {
+ $view->queueFilter('ColumnCallbackAddColumnPercentage',
+ array('percent_total', 'total_size', 'total_size', $quotientPrecision = 0, $shouldSkipRows = false,
+ $getDivisorFromSummaryRow = true),
+ $runBeforeGenericFilters = true);
+ $view->setSortedColumn('percent_total', $orderDir);
+ }
+ }
+ else if ($view instanceof Piwik_ViewDataTable_GenerateGraphData) // if displaying a graph
+ {
+ if (in_array('total_size', $sizeColumns))
+ {
+ $view->setColumnsToDisplay(array('label', 'total_size'));
+
+ // when displaying a graph, we force sizes to be shown as the same unit so axis labels
+ // will be readable. NOTE: The unit should depend on the smallest value of the data table,
+ // however there's no way to know this information, short of creating a custom filter. For
+ // now, just assume KB.
+ $fixedMemoryUnit = 'K';
+ $view->setAxisYUnit(' K');
+
+ $view->setSortedColumn('total_size', 'desc');
+
+ $runPrettySizeFilterBeforeGeneric = true;
+ }
+ else
+ {
+ $view->setColumnsToDisplay(array('label', 'row_count'));
+ $view->setAxisYUnit(' '.Piwik_Translate('General_Rows'));
+
+ $view->setSortedColumn('row_count', 'desc');
+ }
+ }
+
+ $getPrettySize = array('Piwik', 'getPrettySizeFromBytes');
+ $params = $fixedMemoryUnit === false ? array() : array($fixedMemoryUnit);
+ $view->queueFilter(
+ 'ColumnCallbackReplace', array($sizeColumns, $getPrettySize, $params), $runPrettySizeFilterBeforeGeneric);
+
+ // jqPlot will display &nbsp; as, well, '&nbsp;', so don't replace the spaces when rendering as a graph
+ if (!($view instanceof Piwik_ViewDataTable_GenerateGraphData))
+ {
+ $replaceSpaces = array($this, 'replaceColumnSpaces');
+ $view->queueFilter('ColumnCallbackReplace', array($sizeColumns, $replaceSpaces));
+ }
+
+ $getPrettyNumber = array('Piwik', 'getPrettyNumber');
+ $view->queueFilter('ColumnCallbackReplace', array(array('row_count'), $getPrettyNumber));
+
+ return $view;
+ }
+
+ /**
+ * Replaces spaces w/ &nbsp; for correct HTML output.
+ */
+ public function replaceColumnSpaces( $value )
+ {
+ return str_replace(' ', '&nbsp;', $value);
+ }
+
+ /**
+ * Row callback function that calculates a tables total size.
+ */
+ public function getTotalTableSize( $dataSize, $indexSize )
+ {
+ return $dataSize + $indexSize;
+ }
+
+ /**
+ * Column callback used to translate the column values in the database usage summary table.
+ */
+ public function translateSummarylabel( $value )
+ {
+ static $valueToTranslationStr = array(
+ 'tracker_data' => 'DBStats_TrackerTables',
+ 'report_data' => 'DBStats_ReportTables',
+ 'metric_data' => 'DBStats_MetricTables',
+ 'other_data' => 'DBStats_OtherTables'
+ );
+
+ return isset($valueToTranslationStr[$value])
+ ? Piwik_Translate($valueToTranslationStr[$value])
+ : $value;
+ }
+
+ /**
+ * Sets the footer message for the Individual...Summary reports.
+ */
+ private function setIndividualSummaryFooterMessage( $view )
+ {
+ $lastGenerated = Piwik_DBStats::getDateOfLastCachingRun();
+ if ($lastGenerated !== false)
+ {
+ $view->setFooterMessage(Piwik_Translate('Mobile_LastUpdated', $lastGenerated));
+ }
}
}
diff --git a/plugins/DBStats/DBStats.php b/plugins/DBStats/DBStats.php
index 3045810bfc..2b2f00ecae 100644
--- a/plugins/DBStats/DBStats.php
+++ b/plugins/DBStats/DBStats.php
@@ -16,6 +16,8 @@
*/
class Piwik_DBStats extends Piwik_Plugin
{
+ const TIME_OF_LAST_TASK_RUN_OPTION = 'dbstats_time_of_last_cache_task_run';
+
public function getInformation()
{
return array(
@@ -28,7 +30,10 @@ class Piwik_DBStats extends Piwik_Plugin
function getListHooksRegistered()
{
- return array('AdminMenu.add' => 'addMenu');
+ return array(
+ 'AdminMenu.add' => 'addMenu',
+ 'TaskScheduler.getScheduledTasks' => 'getScheduledTasks',
+ );
}
function addMenu()
@@ -38,4 +43,39 @@ class Piwik_DBStats extends Piwik_Plugin
Piwik::isUserIsSuperUser(),
$order = 9);
}
+
+ /**
+ * Gets all scheduled tasks executed by this plugin.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getScheduledTasks($notification)
+ {
+ $tasks = &$notification->getNotificationObject();
+
+ $priority = Piwik_ScheduledTask::LOWEST_PRIORITY;
+ $cacheDataByArchiveNameReportsTask = new Piwik_ScheduledTask(
+ $this, 'cacheDataByArchiveNameReports', new Piwik_ScheduledTime_Weekly(), $priority);
+ $tasks[] = $cacheDataByArchiveNameReportsTask;
+ }
+
+ /**
+ * Caches the intermediate DataTables used in the getIndividualReportsSummary and
+ * getIndividualMetricsSummary reports in the option table.
+ */
+ public function cacheDataByArchiveNameReports()
+ {
+ $api = Piwik_DBStats_API::getInstance();
+ $api->getIndividualReportsSummary(true);
+ $api->getIndividualMetricsSummary(true);
+
+ $now = Piwik_Date::now()->getLocalized("%longYear%, %shortMonth% %day%");
+ Piwik_SetOption(self::TIME_OF_LAST_TASK_RUN_OPTION, $now);
+ }
+
+ /** Returns the date when the cacheDataByArchiveNameReports was last run. */
+ public static function getDateOfLastCachingRun()
+ {
+ return Piwik_GetOption(self::TIME_OF_LAST_TASK_RUN_OPTION);
+ }
}
diff --git a/plugins/DBStats/MySQLMetadataProvider.php b/plugins/DBStats/MySQLMetadataProvider.php
new file mode 100755
index 0000000000..53ec8c3324
--- /dev/null
+++ b/plugins/DBStats/MySQLMetadataProvider.php
@@ -0,0 +1,378 @@
+<?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: MySQLMetadataProvider.php $
+ *
+ * @category Piwik_Plugins
+ * @package Piwik_DBStats
+ */
+
+/**
+ * Utility class that provides general information about databases, including the size of
+ * the entire database, the size and row count of each table and the size and row count
+ * of each metric/report type currently stored.
+ *
+ * This class will cache the table information it retrieves from the database. In order to
+ * issue a new query instead of using this cache, you must create a new instance of this type.
+ */
+class Piwik_DBStats_MySQLMetadataProvider
+{
+ /**
+ * Cached MySQL table statuses. So we won't needlessly re-issue SHOW TABLE STATUS queries.
+ */
+ private $tableStatuses = null;
+
+ /**
+ * Constructor.
+ */
+ public function __construct()
+ {
+ // empty
+ }
+
+ /**
+ * Gets general database info that is not specific to any table.
+ *
+ * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-status.html .
+ */
+ public function getDBStatus()
+ {
+ if (function_exists('mysql_connect'))
+ {
+ $configDb = Piwik_Config::getInstance()->database;
+ $link = mysql_connect($configDb['host'], $configDb['username'], $configDb['password']);
+ $status = mysql_stat($link);
+ mysql_close($link);
+ $status = explode(" ", $status);
+ }
+ else
+ {
+ $fullStatus = Piwik_FetchAssoc('SHOW STATUS');
+ if (empty($fullStatus))
+ {
+ throw new Exception('Error, SHOW STATUS failed');
+ }
+
+ $status = array(
+ 'Uptime' => $fullStatus['Uptime']['Value'],
+ 'Threads' => $fullStatus['Threads_running']['Value'],
+ 'Questions' => $fullStatus['Questions']['Value'],
+ 'Slow queries' => $fullStatus['Slow_queries']['Value'],
+ 'Flush tables' => $fullStatus['Flush_commands']['Value'],
+ 'Open tables' => $fullStatus['Open_tables']['Value'],
+ 'Opens' => 'unavailable', // not available via SHOW STATUS
+ 'Queries per second avg' => 'unavailable' // not available via SHOW STATUS
+ );
+ }
+
+ return $status;
+ }
+
+ /**
+ * Gets the MySQL table status of the requested Piwik table.
+ *
+ * @param string $table The name of the table. Should not be prefixed (ie, 'log_visit' is
+ * correct, 'piwik_log_visit' is not).
+ * @return array See http://dev.mysql.com/doc/refman/5.1/en/show-table-status.html .
+ */
+ public function getTableStatus( $table )
+ {
+ $prefixed = Piwik_Common::prefixTable($table);
+
+ // if we've already gotten every table status, don't issue an uneeded query
+ if (!is_null($this->tableStatuses) && isset($this->tableStatuses[$prefixed]))
+ {
+ return $this->tableStatuses[$prefixed];
+ }
+ else
+ {
+ return Piwik_FetchRow("SHOW TABLE STATUS LIKE ?", array($prefixed));
+ }
+ }
+
+ /**
+ * Gets the result of a SHOW TABLE STATUS query for every Piwik table in the DB.
+ * Non-piwik tables are ignored.
+ *
+ * @param string $matchingRegex Regex used to filter out tables whose name doesn't
+ * match it.
+ * @return array The table information. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html
+ * for specifics.
+ */
+ public function getAllTablesStatus( $matchingRegex = null )
+ {
+ if (is_null($this->tableStatuses))
+ {
+ $tablesPiwik = Piwik::getTablesInstalled();
+
+ $this->tableStatuses = array();
+ foreach(Piwik_FetchAll("SHOW TABLE STATUS") as $t)
+ {
+ if (in_array($t['Name'], $tablesPiwik))
+ {
+ $this->tableStatuses[$t['Name']] = $t;
+ }
+ }
+ }
+
+ if (is_null($matchingRegex))
+ {
+ return $this->tableStatuses;
+ }
+
+ $result = array();
+ foreach ($this->tableStatuses as $status)
+ {
+ if (preg_match($matchingRegex, $status['Name']))
+ {
+ $result[] = $status;
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Returns table statuses for every log table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllLogTableStatus()
+ {
+ $regex = "/^".Piwik_Common::prefixTable('log_')."(?!profiling)/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Returns table statuses for every numeric archive table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllNumericArchiveStatus()
+ {
+ $regex = "/^".Piwik_Common::prefixTable('archive_numeric')."_/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Returns table statuses for every blob archive table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllBlobArchiveStatus()
+ {
+ $regex = "/^".Piwik_Common::prefixTable('archive_blob')."_/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Retruns table statuses for every admin table.
+ *
+ * @return array An array of status arrays. See http://dev.mysql.com/doc/refman/5.5/en/show-table-status.html.
+ */
+ public function getAllAdminTableStatus()
+ {
+ $regex = "/^".Piwik_Common::prefixTable('')."(?!archive_|(?:log_(?!profiling)))/";
+ return $this->getAllTablesStatus($regex);
+ }
+
+ /**
+ * Returns a DataTable that lists the number of rows and the estimated amount of space
+ * each blob archive type takes up in the database.
+ *
+ * Blob types are differentiated by name.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable
+ */
+ public function getRowCountsAndSizeByBlobName( $forceCache = false )
+ {
+ $extraSelects = array("SUM(OCTET_LENGTH(value)) AS 'blob_size'", "SUM(LENGTH(name)) AS 'name_size'");
+ $extraCols = array('blob_size', 'name_size');
+ return $this->getRowCountsByArchiveName(
+ $this->getAllBlobArchiveStatus(), 'getEstimatedBlobArchiveRowSize', $forceCache, $extraSelects,
+ $extraCols);
+ }
+
+ /**
+ * Returns a DataTable that lists the number of rows and the estimated amount of space
+ * each metric archive type takes up in the database.
+ *
+ * Metric types are differentiated by name.
+ *
+ * @param bool $forceCache false to use the cached result, true to run the queries again and
+ * cache the result.
+ * @return Piwik_DataTable
+ */
+ public function getRowCountsAndSizeByMetricName( $forceCache = false )
+ {
+ return $this->getRowCountsByArchiveName(
+ $this->getAllNumericArchiveStatus(), 'getEstimatedRowsSize', $forceCache);
+ }
+
+ /**
+ * Utility function. Gets row count of a set of tables grouped by the 'name' column.
+ * This is the implementation of the getRowCountsAndSizeBy... functions.
+ */
+ private function getRowCountsByArchiveName( $statuses, $getRowSizeMethod, $forceCache = false,
+ $otherSelects = array(), $otherDataTableColumns = array() )
+ {
+ $extraCols = '';
+ if (!empty($otherSelects))
+ {
+ $extraCols = ', '.implode(', ', $otherSelects);
+ }
+
+ $cols = array_merge(array('row_count'), $otherDataTableColumns);
+
+ $dataTable = new Piwik_DataTable();
+ foreach ($statuses as $status)
+ {
+ $dataTableOptionName = $this->getCachedOptionName($status['Name'], 'byArchiveName');
+
+ // if option exists && !$forceCache, use the cached data, otherwise create the
+ $cachedData = Piwik_GetOption($dataTableOptionName);
+ if ($cachedData !== false && !$forceCache)
+ {
+ $table = new Piwik_DataTable();
+ $table->addRowsFromSerializedArray($cachedData);
+ }
+ else
+ {
+ // otherwise, create data table & cache it
+ $sql = "SELECT name as 'label', COUNT(*) as 'row_count'$extraCols FROM {$status['Name']} GROUP BY name";
+
+ $table = new Piwik_DataTable();
+ $table->addRowsFromSimpleArray(Piwik_FetchAll($sql));
+
+ $reduceArchiveRowName = array($this, 'reduceArchiveRowName');
+ $table->filter('GroupBy', array('label', $reduceArchiveRowName));
+
+ Piwik_SetOption($dataTableOptionName, reset($table->getSerialized()));
+ }
+
+ // add estimated_size column
+ $getEstimatedSize = array($this, $getRowSizeMethod);
+ $table->filter('ColumnCallbackAddColumn',
+ array($cols, 'estimated_size', $getEstimatedSize, array($status)));
+
+ $dataTable->addDataTable($table);
+ destroy($table);
+ }
+ return $dataTable;
+ }
+
+ /**
+ * Gets the estimated database size a count of rows takes in a table.
+ */
+ public function getEstimatedRowsSize( $row_count, $status )
+ {
+ $avgRowSize = ($status['Data_length'] + $status['Index_length']) / $status['Rows'];
+ return $avgRowSize * $row_count;
+ }
+
+ /**
+ * Gets the estimated database size a count of rows in a blob_archive table. Depends on
+ * the data table row to contain the size of all blobs & name strings in the row set it
+ * represents.
+ */
+ public function getEstimatedBlobArchiveRowSize( $row_count, $blob_size, $name_size, $status )
+ {
+ // calculate the size of each fixed size column in a blob archive table
+ static $fixedSizeColumnLength = null;
+ if (is_null($fixedSizeColumnLength))
+ {
+ $fixedSizeColumnLength = 0;
+ foreach (Piwik_FetchAll("SHOW COLUMNS FROM ".$status['Name']) as $column)
+ {
+ $columnType = $column['Type'];
+
+ if (($paren = strpos($columnType, '(')) !== false)
+ {
+ $columnType = substr($columnType, 0, $paren);
+ }
+
+ $fixedSizeColumnLength += $this->sizeOfMySQLColumn($columnType);
+ }
+ }
+
+ // calculate the average row size
+ $avgRowSize = $status['Index_length'] / $status['Rows'] + $fixedSizeColumnLength;
+
+ // calculate the row set's size
+ return $avgRowSize * $row_count + $blob_size + $name_size;
+ }
+
+ /** Returns the size in bytes of a fixed size MySQL data type. Returns 0 for unsupported data type. */
+ private function sizeOfMySQLColumn( $columnType )
+ {
+ switch (strtolower($columnType))
+ {
+ case "tinyint":
+ case "year":
+ return 1;
+ case "smallint":
+ return 2;
+ case "mediumint":
+ case "date":
+ case "time":
+ return 3;
+ case "int":
+ case "float": // assumes precision isn't used
+ case "timestamp":
+ return 4;
+ case "bigint":
+ case "double":
+ case "real":
+ case "datetime":
+ return 8;
+ default:
+ return 0;
+ }
+ }
+
+ /**
+ * Gets the option name used to cache the result of an intensive query.
+ */
+ private function getCachedOptionName( $tableName, $suffix )
+ {
+ return 'dbstats_cached_'.$tableName.'_'.$suffix;
+ }
+
+ /**
+ * Reduces the given metric name. Used to simplify certain reports.
+ *
+ * Some metrics, like goal metrics, can have different string names. For goal metrics,
+ * there's one name per goal ID. Grouping metrics and reports like these together
+ * simplifies the tables that display them.
+ *
+ * This function makes goal names, 'done...' names and names of the format .*_[0-9]+
+ * equivalent.
+ */
+ public function reduceArchiveRowName( $name )
+ {
+ // all 'done...' fields are considered the same
+ if (strpos($name, 'done') === 0)
+ {
+ return 'done';
+ }
+
+ // check for goal id, if present (Goals_... reports should not be reduced here, just Goal_... ones)
+ if (preg_match("/^Goal_(?:-?[0-9]+_)?(.*)/", $name, $matches))
+ {
+ $name = "Goal_*_".$matches[1];
+ }
+
+ // remove subtable id suffix, if present
+ if (preg_match("/^(.*)_[0-9]+$/", $name, $matches))
+ {
+ $name = $matches[1]."_*";
+ }
+
+ return $name;
+ }
+}
+
diff --git a/plugins/DBStats/templates/DBStats.tpl b/plugins/DBStats/templates/DBStats.tpl
deleted file mode 100644
index 686e3d1fca..0000000000
--- a/plugins/DBStats/templates/DBStats.tpl
+++ /dev/null
@@ -1,52 +0,0 @@
-{assign var=showSitesSelection value=false}
-{assign var=showPeriodSelection value=false}
-{include file="CoreAdminHome/templates/header.tpl"}
-<div style="max-width:980px;">
-
-<h2>{'DBStats_DatabaseUsage'|translate}</h2>
-{assign var=totalSize value=$tablesStatus.Total.Total_length}
-<p>{'DBStats_MainDescription'|translate:$totalSize}
-<br />
-{'DBStats_LearnMore'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>Piwik Auto Archiving</a>"}
-<br />
-{'PrivacyManager_DeleteDataSettings'|translate}: <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
-{capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
- {'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
- </a>
-
-<table class="dataTable entityTable">
- <thead>
- <tr>
- <th>{'DBStats_Table'|translate}</th>
- <th>{'DBStats_RowCount'|translate}</th>
- <th>{'DBStats_DataSize'|translate}</th>
- <th>{'DBStats_IndexSize'|translate}</th>
- <th>{'DBStats_TotalSize'|translate}</th>
- </tr>
- </thead>
- <tbody id="tables">
- {foreach from=$tablesStatus key=index item=table}
- <tr {if $table.Name == 'Total'}class="highlight" style="font-weight:bold;"{/if}>
- <td>
- {$table.Name}
- </td>
- <td>
- {$table.Rows}
- </td>
- <td>
- {$table.Data_length}b
- </td>
- <td>
- {$table.Index_length}b
- </td>
- <td>
- {$table.Total_length}b
- </td>
- </tr>
- {/foreach}
- </tbody>
-</table>
-
-</div>
-
-{include file="CoreAdminHome/templates/footer.tpl"}
diff --git a/plugins/DBStats/templates/index.tpl b/plugins/DBStats/templates/index.tpl
new file mode 100755
index 0000000000..b84ab4691c
--- /dev/null
+++ b/plugins/DBStats/templates/index.tpl
@@ -0,0 +1,154 @@
+{assign var=showSitesSelection value=false}
+{assign var=showPeriodSelection value=false}
+{include file="CoreAdminHome/templates/header.tpl"}
+{loadJavascriptTranslations plugins='CoreAdminHome CoreHome'}
+
+{literal}
+<style>
+.dbstatsTable {
+ display: inline-block;
+}
+.dbstatsTable>tbody>tr>td:first-child {
+ width: 550px;
+}
+.dbstatsTable h2 {
+ width: 500px;
+}
+.adminTable.dbstatsTable a {
+ color: black;
+ text-decoration: underline;
+}
+</style>
+{/literal}
+
+<a name="databaseUsageSummary"></a>
+<h2>{'DBStats_DatabaseUsage'|translate}</h2>
+<p>
+ {'DBStats_MainDescription'|translate:$totalSpaceUsed}<br/>
+ {'DBStats_LearnMore'|translate:"<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/setup-auto-archiving/' target='_blank'>Piwik Auto Archiving</a>"}<br/>
+ <br/>
+</p>
+<table class="adminTable dbstatsTable">
+ <tbody>
+ <tr>
+ <td>{$databaseUsageSummary}</td>
+ <td>
+ <h3 style="margin-top:0">{'General_GeneralInformation'|translate}</h3><br/>
+ <p style="font-size:1.4em;padding-left:21px;line-height:1.8em">
+ <strong><em>{$userCount}</strong></em>&nbsp;{if $userCount == 1}{'UsersManager_User'|translate}{else}{'UsersManager_MenuUsers'|translate}{/if}<br/>
+ <strong><em>{$siteCount}</strong></em>&nbsp;{if $siteCount == 1}{'General_Website'|translate}{else}{'Referers_Websites'|translate}{/if}
+ </p><br/>
+ {capture assign=clickDeleteLogSettings}{'PrivacyManager_DeleteDataSettings'|translate}{/capture}
+ <h3 style="margin-top:0">{'PrivacyManager_DeleteDataSettings'|translate}</h3><br/>
+ <p>
+ {'PrivacyManager_DeleteDataDescription'|translate}
+ <br/>
+ <a href='{url module="PrivacyManager" action="privacySettings"}#deleteLogsAnchor'>
+ {'PrivacyManager_ClickHereSettings'|translate:"'$clickDeleteLogSettings'"}
+ </a>
+ </p>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<br/>
+
+<a name="trackerDataSummary"></a>
+<table class="adminTable dbstatsTable">
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_TrackerTables'|translate}</h2>
+ {$trackerDataSummary}
+ </td>
+ <td>&nbsp;</td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="reportDataSummary"></a>
+<table class="adminTable dbstatsTable">
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_ReportTables'|translate}</h2>
+ {$reportDataSummary}
+ </td>
+ <td>
+ <h2>{'General_Reports'|translate}</h2>
+ <div class="ajaxLoad" href="/index.php?module=DBStats&action=getIndividualReportsSummary&viewDataTable=table">
+ <span class="loadingPiwik"><img src="themes/default/images/loading-blue.gif" />{'General_LoadingData'|translate}</span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="metricDataSummary"></a>
+<table class="adminTable dbstatsTable">
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_MetricTables'|translate}</h2>
+ {$metricDataSummary}
+ </td>
+ <td>
+ <h2>{'General_Metrics'|translate}</h2>
+ <div class="ajaxLoad" href="/index.php?module=DBStats&action=getIndividualMetricsSummary&viewDataTable=table">
+ <span class="loadingPiwik"><img src="themes/default/images/loading-blue.gif" />{'General_LoadingData'|translate}</span>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<a name="adminDataSummary"></a>
+<table class="adminTable dbstatsTable">
+ <tbody>
+ <tr>
+ <td>
+ <h2>{'DBStats_OtherTables'|translate}</h2>
+ {$adminDataSummary}
+ </td>
+ <td>&nbsp;</td>
+ </tr>
+ </tbody>
+</table>
+
+{literal}
+<script type="text/javascript">
+(function( $ ){
+ $(document).ready(function() {
+ $('.ajaxLoad').each(function() {
+ var self = this,
+ reportUrl = $(this).attr('href');
+
+ // build & execute AJAX request
+ var request =
+ {
+ type: 'GET',
+ url: reportUrl,
+ dataType: 'html',
+ async: true,
+ error: piwikHelper.ajaxHandleError, // Callback when the request fails
+ data: {
+ idSite: broadcast.getValueFromUrl('idSite'),
+ period: broadcast.getValueFromUrl('period'),
+ date: broadcast.getValueFromUrl('date')
+ },
+ success: function(data) {
+ $('.loadingPiwik', self).hide();
+ $(self).html(data);
+ }
+ };
+
+ piwikHelper.queueAjaxRequest($.ajax(request));
+ });
+ });
+})( jQuery );
+</script>
+{/literal}
+
+{include file="CoreAdminHome/templates/footer.tpl"}
+
diff --git a/plugins/PrivacyManager/templates/privacySettings.tpl b/plugins/PrivacyManager/templates/privacySettings.tpl
index 9b05e18d91..1fcb6c0daa 100644
--- a/plugins/PrivacyManager/templates/privacySettings.tpl
+++ b/plugins/PrivacyManager/templates/privacySettings.tpl
@@ -72,7 +72,7 @@ See also our official guide <b><a href='http://piwik.org/privacy/' target='_blan
<a name="deleteLogsAnchor"></a>
<h2>{'PrivacyManager_DeleteDataSettings'|translate}</h2>
-<p>{'PrivacyManager_DeleteDataDescription'|translate}</p>
+<p>{'PrivacyManager_DeleteDataDescription'|translate} {'PrivacyManager_DeleteDataDescription2'|translate}</p>
<form method="post" action="{url action=saveSettings form=formDeleteSettings token_auth=$token_auth}" id="formDeleteSettings" name="formMaskLength">
<table class="adminTable" style='width:800px;'>
<tr id='deleteLogSettingEnabled'>