update Piwik to version 2.16 (fixes #91)
This commit is contained in:
parent
296343bf3b
commit
d885a4baa9
5833 changed files with 418860 additions and 226988 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
|
|
@ -12,14 +12,16 @@ use Exception;
|
|||
use Piwik\API\Request;
|
||||
use Piwik\Archive;
|
||||
use Piwik\Common;
|
||||
use Piwik\DataTable\Filter\CalculateEvolutionFilter;
|
||||
use Piwik\Container\StaticContainer;
|
||||
use Piwik\DataTable;
|
||||
use Piwik\DataTable\Row;
|
||||
use Piwik\Period\Range;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\Goals\Archiver;
|
||||
use Piwik\Plugins\SitesManager\API as APISitesManager;
|
||||
use Piwik\Scheduler\Scheduler;
|
||||
use Piwik\SettingsPiwik;
|
||||
use Piwik\Site;
|
||||
use Piwik\TaskScheduler;
|
||||
|
||||
/**
|
||||
* The MultiSites API lets you request the key metrics (visits, page views, revenue) for all Websites in Piwik.
|
||||
|
|
@ -30,6 +32,7 @@ class API extends \Piwik\Plugin\API
|
|||
const METRIC_TRANSLATION_KEY = 'translation';
|
||||
const METRIC_EVOLUTION_COL_NAME_KEY = 'evolution_column_name';
|
||||
const METRIC_RECORD_NAME_KEY = 'record_name';
|
||||
const METRIC_COL_NAME_KEY = 'metric_column_name';
|
||||
const METRIC_IS_ECOMMERCE_KEY = 'is_ecommerce';
|
||||
|
||||
const NB_VISITS_METRIC = 'nb_visits';
|
||||
|
|
@ -41,17 +44,19 @@ class API extends \Piwik\Plugin\API
|
|||
const ECOMMERCE_ORDERS_METRIC = 'orders';
|
||||
const ECOMMERCE_REVENUE_METRIC = 'ecommerce_revenue';
|
||||
|
||||
static private $baseMetrics = array(
|
||||
private static $baseMetrics = array(
|
||||
self::NB_VISITS_METRIC => array(
|
||||
self::METRIC_TRANSLATION_KEY => 'General_ColumnNbVisits',
|
||||
self::METRIC_EVOLUTION_COL_NAME_KEY => 'visits_evolution',
|
||||
self::METRIC_RECORD_NAME_KEY => self::NB_VISITS_METRIC,
|
||||
self::METRIC_COL_NAME_KEY => self::NB_VISITS_METRIC,
|
||||
self::METRIC_IS_ECOMMERCE_KEY => false,
|
||||
),
|
||||
self::NB_ACTIONS_METRIC => array(
|
||||
self::METRIC_TRANSLATION_KEY => 'General_ColumnNbActions',
|
||||
self::METRIC_EVOLUTION_COL_NAME_KEY => 'actions_evolution',
|
||||
self::METRIC_RECORD_NAME_KEY => self::NB_ACTIONS_METRIC,
|
||||
self::METRIC_COL_NAME_KEY => self::NB_ACTIONS_METRIC,
|
||||
self::METRIC_IS_ECOMMERCE_KEY => false,
|
||||
)
|
||||
);
|
||||
|
|
@ -77,52 +82,81 @@ class API extends \Piwik\Plugin\API
|
|||
* Only used when a scheduled task is running
|
||||
* @param bool|string $enhanced When true, return additional goal & ecommerce metrics
|
||||
* @param bool|string $pattern If specified, only the website which names (or site ID) match the pattern will be returned using SitesManager.getPatternMatchSites
|
||||
* @param array $showColumns If specified, only the requested columns will be fetched
|
||||
* @return DataTable
|
||||
*/
|
||||
public function getAll($period, $date, $segment = false, $_restrictSitesToLogin = false, $enhanced = false, $pattern = false)
|
||||
public function getAll($period, $date, $segment = false, $_restrictSitesToLogin = false, $enhanced = false, $pattern = false, $showColumns = array())
|
||||
{
|
||||
Piwik::checkUserHasSomeViewAccess();
|
||||
|
||||
$idSites = $this->getSitesIdFromPattern($pattern);
|
||||
$sites = $this->getSitesIdFromPattern($pattern, $_restrictSitesToLogin);
|
||||
|
||||
if (empty($idSites)) {
|
||||
if (!empty($showColumns) && !is_array($showColumns)) {
|
||||
$showColumns = explode(',', $showColumns);
|
||||
}
|
||||
|
||||
if (empty($sites)) {
|
||||
return new DataTable();
|
||||
}
|
||||
|
||||
return $this->buildDataTable(
|
||||
$idSites,
|
||||
$sites,
|
||||
$period,
|
||||
$date,
|
||||
$segment,
|
||||
$_restrictSitesToLogin,
|
||||
$enhanced,
|
||||
$multipleWebsitesRequested = true
|
||||
$multipleWebsitesRequested = true,
|
||||
$showColumns
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the list of sites which names match the string pattern
|
||||
*
|
||||
* @param $pattern
|
||||
* @param string $pattern
|
||||
* @param bool $_restrictSitesToLogin
|
||||
* @return array|string
|
||||
*/
|
||||
private function getSitesIdFromPattern($pattern)
|
||||
private function getSitesIdFromPattern($pattern, $_restrictSitesToLogin)
|
||||
{
|
||||
$idSites = 'all';
|
||||
// First clear cache
|
||||
Site::clearCache();
|
||||
|
||||
if (empty($pattern)) {
|
||||
return $idSites;
|
||||
}
|
||||
$idSites = array();
|
||||
$sites = Request::processRequest('SitesManager.getPatternMatchSites',
|
||||
array('pattern' => $pattern,
|
||||
// added because caller could overwrite these
|
||||
'serialize' => 0,
|
||||
'format' => 'original'));
|
||||
if (!empty($sites)) {
|
||||
foreach ($sites as $site) {
|
||||
$idSites[] = $site['idsite'];
|
||||
|
||||
/** @var Scheduler $scheduler */
|
||||
$scheduler = StaticContainer::getContainer()->get('Piwik\Scheduler\Scheduler');
|
||||
// Then, warm the cache with only the data we should have access to
|
||||
if (Piwik::hasUserSuperUserAccess()
|
||||
// Hack: when this API function is called as a Scheduled Task, Super User status is enforced.
|
||||
// This means this function would return ALL websites in all cases.
|
||||
// Instead, we make sure that only the right set of data is returned
|
||||
&& !$scheduler->isRunningTask()
|
||||
) {
|
||||
APISitesManager::getInstance()->getAllSites();
|
||||
} else {
|
||||
APISitesManager::getInstance()->getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin);
|
||||
}
|
||||
|
||||
} else {
|
||||
$sites = Request::processRequest('SitesManager.getPatternMatchSites',
|
||||
array('pattern' => $pattern,
|
||||
// added because caller could overwrite these
|
||||
'limit' => SettingsPiwik::getWebsitesCountToDisplay(),
|
||||
'showColumns' => '',
|
||||
'hideColumns' => '',
|
||||
'format' => 'original'));
|
||||
|
||||
if (!empty($sites)) {
|
||||
Site::setSitesFromArray($sites);
|
||||
}
|
||||
}
|
||||
return $idSites;
|
||||
|
||||
// Both calls above have called Site::setSitesFromArray. We now get these sites:
|
||||
$sitesToProblablyAdd = Site::getSites();
|
||||
|
||||
return $sitesToProblablyAdd;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -141,43 +175,41 @@ class API extends \Piwik\Plugin\API
|
|||
public function getOne($idSite, $period, $date, $segment = false, $_restrictSitesToLogin = false, $enhanced = false)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
|
||||
$sites = $this->getSiteFromId($idSite);
|
||||
|
||||
return $this->buildDataTable(
|
||||
$idSite,
|
||||
$sites,
|
||||
$period,
|
||||
$date,
|
||||
$segment,
|
||||
$_restrictSitesToLogin,
|
||||
$enhanced,
|
||||
$multipleWebsitesRequested = false
|
||||
$multipleWebsitesRequested = false,
|
||||
$showColumns = array()
|
||||
);
|
||||
}
|
||||
|
||||
private function buildDataTable($idSitesOrIdSite, $period, $date, $segment, $_restrictSitesToLogin, $enhanced, $multipleWebsitesRequested)
|
||||
private function getSiteFromId($idSite)
|
||||
{
|
||||
$allWebsitesRequested = ($idSitesOrIdSite == 'all');
|
||||
if ($allWebsitesRequested) {
|
||||
// First clear cache
|
||||
Site::clearCache();
|
||||
// Then, warm the cache with only the data we should have access to
|
||||
if (Piwik::hasUserSuperUserAccess()
|
||||
// Hack: when this API function is called as a Scheduled Task, Super User status is enforced.
|
||||
// This means this function would return ALL websites in all cases.
|
||||
// Instead, we make sure that only the right set of data is returned
|
||||
&& !TaskScheduler::isTaskBeingExecuted()
|
||||
) {
|
||||
APISitesManager::getInstance()->getAllSites();
|
||||
} else {
|
||||
APISitesManager::getInstance()->getSitesWithAtLeastViewAccess($limit = false, $_restrictSitesToLogin);
|
||||
$idSite = (int) $idSite;
|
||||
$sites = array(APISitesManager::getInstance()->getSiteFromId($idSite));
|
||||
|
||||
return $sites;
|
||||
}
|
||||
|
||||
private function buildDataTable($sitesToProblablyAdd, $period, $date, $segment, $_restrictSitesToLogin, $enhanced, $multipleWebsitesRequested, $showColumns)
|
||||
{
|
||||
$idSites = array();
|
||||
if (!empty($sitesToProblablyAdd)) {
|
||||
foreach ($sitesToProblablyAdd as $site) {
|
||||
$idSites[] = $site['idsite'];
|
||||
}
|
||||
// Both calls above have called Site::setSitesFromArray. We now get these sites:
|
||||
$sitesToProblablyAdd = Site::getSites();
|
||||
} else {
|
||||
$sitesToProblablyAdd = array(APISitesManager::getInstance()->getSiteFromId($idSitesOrIdSite));
|
||||
}
|
||||
|
||||
// build the archive type used to query archive data
|
||||
$archive = Archive::build(
|
||||
$idSitesOrIdSite,
|
||||
$idSites,
|
||||
$period,
|
||||
$date,
|
||||
$segment,
|
||||
|
|
@ -190,6 +222,10 @@ class API extends \Piwik\Plugin\API
|
|||
$apiECommerceMetrics = array();
|
||||
$apiMetrics = API::getApiMetrics($enhanced);
|
||||
foreach ($apiMetrics as $metricName => $metricSettings) {
|
||||
if (!empty($showColumns) && !in_array($metricName, $showColumns)) {
|
||||
unset($apiMetrics[$metricName]);
|
||||
continue;
|
||||
}
|
||||
$fieldsToGet[] = $metricSettings[self::METRIC_RECORD_NAME_KEY];
|
||||
$columnNameRewrites[$metricSettings[self::METRIC_RECORD_NAME_KEY]] = $metricName;
|
||||
|
||||
|
|
@ -198,22 +234,11 @@ class API extends \Piwik\Plugin\API
|
|||
}
|
||||
}
|
||||
|
||||
// get the data
|
||||
// $dataTable instanceOf Set
|
||||
$dataTable = $archive->getDataTableFromNumeric($fieldsToGet);
|
||||
$dataTable = $archive->getDataTableFromNumericAndMergeChildren($fieldsToGet);
|
||||
|
||||
$dataTable = $this->mergeDataTableMapAndPopulateLabel($idSitesOrIdSite, $multipleWebsitesRequested, $dataTable);
|
||||
|
||||
if ($dataTable instanceof DataTable\Map) {
|
||||
foreach ($dataTable->getDataTables() as $table) {
|
||||
$this->addMissingWebsites($table, $fieldsToGet, $sitesToProblablyAdd);
|
||||
}
|
||||
} else {
|
||||
$this->addMissingWebsites($dataTable, $fieldsToGet, $sitesToProblablyAdd);
|
||||
}
|
||||
|
||||
// calculate total visits/actions/revenue
|
||||
$this->setMetricsTotalsMetadata($dataTable, $apiMetrics);
|
||||
$this->populateLabel($dataTable);
|
||||
$totalMetrics = $this->preformatApiMetricsForTotalsCalculation($apiMetrics);
|
||||
$this->setMetricsTotalsMetadata($dataTable, $totalMetrics);
|
||||
|
||||
// if the period isn't a range & a lastN/previousN date isn't used, we get the same
|
||||
// data for the last period to show the evolution of visits/actions/revenue
|
||||
|
|
@ -228,48 +253,32 @@ class API extends \Piwik\Plugin\API
|
|||
$dataTable->setMetadata(self::getLastPeriodMetadataName('date'), $lastPeriod);
|
||||
}
|
||||
|
||||
$pastArchive = Archive::build($idSitesOrIdSite, $period, $strLastDate, $segment, $_restrictSitesToLogin);
|
||||
$pastArchive = Archive::build($idSites, $period, $strLastDate, $segment, $_restrictSitesToLogin);
|
||||
$pastData = $pastArchive->getDataTableFromNumericAndMergeChildren($fieldsToGet);
|
||||
|
||||
$pastData = $pastArchive->getDataTableFromNumeric($fieldsToGet);
|
||||
|
||||
$pastData = $this->mergeDataTableMapAndPopulateLabel($idSitesOrIdSite, $multipleWebsitesRequested, $pastData);
|
||||
|
||||
// use past data to calculate evolution percentages
|
||||
$this->populateLabel($pastData); // labels are needed to calculate evolution
|
||||
$this->calculateEvolutionPercentages($dataTable, $pastData, $apiMetrics);
|
||||
Common::destroy($pastData);
|
||||
}
|
||||
$this->setPastTotalVisitsMetadata($dataTable, $pastData);
|
||||
|
||||
// remove eCommerce related metrics on non eCommerce Piwik sites
|
||||
// note: this is not optimal in terms of performance: those metrics should not be retrieved in the first place
|
||||
if ($enhanced) {
|
||||
if ($dataTable instanceof DataTable\Map) {
|
||||
foreach ($dataTable->getDataTables() as $table) {
|
||||
$this->removeEcommerceRelatedMetricsOnNonEcommercePiwikSites($table, $apiECommerceMetrics);
|
||||
}
|
||||
} else {
|
||||
$this->removeEcommerceRelatedMetricsOnNonEcommercePiwikSites($dataTable, $apiECommerceMetrics);
|
||||
if ($dataTable instanceof DataTable) {
|
||||
// needed for MultiSites\Dashboard
|
||||
$dataTable->setMetadata('pastData', $pastData);
|
||||
}
|
||||
}
|
||||
|
||||
// move the site id to a metadata column
|
||||
$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'group', array('\Piwik\Site', 'getGroupFor'), array()));
|
||||
$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'main_url', array('\Piwik\Site', 'getMainUrlFor'), array()));
|
||||
$dataTable->filter('ColumnCallbackAddMetadata', array('label', 'idsite'));
|
||||
// move the site id to a metadata column
|
||||
$dataTable->queueFilter('MetadataCallbackAddMetadata', array('idsite', 'group', array('\Piwik\Site', 'getGroupFor'), array()));
|
||||
$dataTable->queueFilter('MetadataCallbackAddMetadata', array('idsite', 'main_url', array('\Piwik\Site', 'getMainUrlFor'), array()));
|
||||
|
||||
// set the label of each row to the site name
|
||||
if ($multipleWebsitesRequested) {
|
||||
$dataTable->filter('ColumnCallbackReplace', array('label', '\Piwik\Site::getNameFor'));
|
||||
$dataTable->queueFilter('ColumnCallbackReplace', array('label', '\Piwik\Site::getNameFor'));
|
||||
} else {
|
||||
$dataTable->filter('ColumnDelete', array('label'));
|
||||
$dataTable->queueFilter('ColumnDelete', array('label'));
|
||||
}
|
||||
|
||||
Site::clearCache();
|
||||
|
||||
// replace record names with user friendly metric names
|
||||
$dataTable->filter('ReplaceColumnNames', array($columnNameRewrites));
|
||||
|
||||
// Ensures data set sorted, for Metadata output
|
||||
$dataTable->filter('Sort', array(self::NB_VISITS_METRIC, 'desc', $naturalSort = false));
|
||||
$dataTable->queueFilter('ReplaceColumnNames', array($columnNameRewrites));
|
||||
|
||||
// filter rows without visits
|
||||
// note: if only one website is queried and there are no visits, we can not remove the row otherwise
|
||||
|
|
@ -289,6 +298,13 @@ class API extends \Piwik\Plugin\API
|
|||
);
|
||||
}
|
||||
|
||||
if ($multipleWebsitesRequested && $dataTable->getRowsCount() === 1 && $dataTable instanceof DataTable\Simple) {
|
||||
$simpleTable = $dataTable;
|
||||
$dataTable = $simpleTable->getEmptyClone();
|
||||
$dataTable->addRow($simpleTable->getFirstRow());
|
||||
unset($simpleTable);
|
||||
}
|
||||
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
|
|
@ -316,16 +332,20 @@ class API extends \Piwik\Plugin\API
|
|||
next($pastArray);
|
||||
}
|
||||
} else {
|
||||
$extraProcessedMetrics = $currentData->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
|
||||
foreach ($apiMetrics as $metricSettings) {
|
||||
$currentData->filter(
|
||||
'CalculateEvolutionFilter',
|
||||
array(
|
||||
$pastData,
|
||||
$metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY],
|
||||
$metricSettings[self::METRIC_RECORD_NAME_KEY],
|
||||
$quotientPrecision = 1)
|
||||
$evolutionMetricClass = $this->isEcommerceEvolutionMetric($metricSettings)
|
||||
? "Piwik\\Plugins\\MultiSites\\Columns\\Metrics\\EcommerceOnlyEvolutionMetric"
|
||||
: "Piwik\\Plugins\\CoreHome\\Columns\\Metrics\\EvolutionMetric";
|
||||
|
||||
$extraProcessedMetrics[] = new $evolutionMetricClass(
|
||||
$metricSettings[self::METRIC_RECORD_NAME_KEY],
|
||||
$pastData,
|
||||
$metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY],
|
||||
$quotientPrecision = 1
|
||||
);
|
||||
}
|
||||
$currentData->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -336,11 +356,12 @@ class API extends \Piwik\Plugin\API
|
|||
{
|
||||
$metrics = self::$baseMetrics;
|
||||
|
||||
if(Common::isActionsPluginEnabled()) {
|
||||
if (Common::isActionsPluginEnabled()) {
|
||||
$metrics[self::NB_PAGEVIEWS_LABEL] = array(
|
||||
self::METRIC_TRANSLATION_KEY => 'General_ColumnPageviews',
|
||||
self::METRIC_EVOLUTION_COL_NAME_KEY => 'pageviews_evolution',
|
||||
self::METRIC_RECORD_NAME_KEY => self::NB_PAGEVIEWS_METRIC,
|
||||
self::METRIC_COL_NAME_KEY => self::NB_PAGEVIEWS_LABEL,
|
||||
self::METRIC_IS_ECOMMERCE_KEY => false,
|
||||
);
|
||||
}
|
||||
|
|
@ -351,6 +372,7 @@ class API extends \Piwik\Plugin\API
|
|||
self::METRIC_TRANSLATION_KEY => 'General_ColumnRevenue',
|
||||
self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_REVENUE_METRIC . '_evolution',
|
||||
self::METRIC_RECORD_NAME_KEY => Archiver::getRecordName(self::GOAL_REVENUE_METRIC),
|
||||
self::METRIC_COL_NAME_KEY => self::GOAL_REVENUE_METRIC,
|
||||
self::METRIC_IS_ECOMMERCE_KEY => false,
|
||||
);
|
||||
|
||||
|
|
@ -360,6 +382,7 @@ class API extends \Piwik\Plugin\API
|
|||
self::METRIC_TRANSLATION_KEY => 'Goals_ColumnConversions',
|
||||
self::METRIC_EVOLUTION_COL_NAME_KEY => self::GOAL_CONVERSION_METRIC . '_evolution',
|
||||
self::METRIC_RECORD_NAME_KEY => Archiver::getRecordName(self::GOAL_CONVERSION_METRIC),
|
||||
self::METRIC_COL_NAME_KEY => self::GOAL_CONVERSION_METRIC,
|
||||
self::METRIC_IS_ECOMMERCE_KEY => false,
|
||||
);
|
||||
|
||||
|
|
@ -368,6 +391,7 @@ class API extends \Piwik\Plugin\API
|
|||
self::METRIC_TRANSLATION_KEY => 'General_EcommerceOrders',
|
||||
self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_ORDERS_METRIC . '_evolution',
|
||||
self::METRIC_RECORD_NAME_KEY => Archiver::getRecordName(self::GOAL_CONVERSION_METRIC, 0),
|
||||
self::METRIC_COL_NAME_KEY => self::ECOMMERCE_ORDERS_METRIC,
|
||||
self::METRIC_IS_ECOMMERCE_KEY => true,
|
||||
);
|
||||
|
||||
|
|
@ -376,6 +400,7 @@ class API extends \Piwik\Plugin\API
|
|||
self::METRIC_TRANSLATION_KEY => 'General_ProductRevenue',
|
||||
self::METRIC_EVOLUTION_COL_NAME_KEY => self::ECOMMERCE_REVENUE_METRIC . '_evolution',
|
||||
self::METRIC_RECORD_NAME_KEY => Archiver::getRecordName(self::GOAL_REVENUE_METRIC, 0),
|
||||
self::METRIC_COL_NAME_KEY => self::ECOMMERCE_REVENUE_METRIC,
|
||||
self::METRIC_IS_ECOMMERCE_KEY => true,
|
||||
);
|
||||
}
|
||||
|
|
@ -384,6 +409,17 @@ class API extends \Piwik\Plugin\API
|
|||
return $metrics;
|
||||
}
|
||||
|
||||
private function preformatApiMetricsForTotalsCalculation($apiMetrics)
|
||||
{
|
||||
$metrics = array();
|
||||
foreach ($apiMetrics as $label => $metricsInfo) {
|
||||
$totalMetadataName = self::getTotalMetadataName($label);
|
||||
$metrics[$totalMetadataName] = $metricsInfo[self::METRIC_RECORD_NAME_KEY];
|
||||
}
|
||||
|
||||
return $metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the total visits, actions & revenue for a DataTable returned by
|
||||
* $this->buildDataTable.
|
||||
|
|
@ -399,27 +435,38 @@ class API extends \Piwik\Plugin\API
|
|||
$this->setMetricsTotalsMetadata($table, $apiMetrics);
|
||||
}
|
||||
} else {
|
||||
$revenueMetric = '';
|
||||
if (Common::isGoalPluginEnabled()) {
|
||||
$revenueMetric = Archiver::getRecordName(self::GOAL_REVENUE_METRIC);
|
||||
}
|
||||
|
||||
$totals = array();
|
||||
foreach ($apiMetrics as $label => $metricInfo) {
|
||||
$totalMetadataName = self::getTotalMetadataName($label);
|
||||
$totals[$totalMetadataName] = 0;
|
||||
foreach ($apiMetrics as $label => $recordName) {
|
||||
$totals[$label] = 0;
|
||||
}
|
||||
|
||||
foreach ($dataTable->getRows() as $row) {
|
||||
foreach ($apiMetrics as $label => $metricInfo) {
|
||||
$totalMetadataName = self::getTotalMetadataName($label);
|
||||
$totals[$totalMetadataName] += $row->getColumn($metricInfo[self::METRIC_RECORD_NAME_KEY]);
|
||||
foreach ($apiMetrics as $totalMetadataName => $recordName) {
|
||||
$totals[$totalMetadataName] += $row->getColumn($recordName);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($totals as $name => $value) {
|
||||
$dataTable->setMetadata($name, $value);
|
||||
$dataTable->setMetadataValues($totals);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of total visits in tha pastTable on the dataTable as metadata.
|
||||
*
|
||||
* @param DataTable $dataTable
|
||||
* @param DataTable $pastTable
|
||||
*/
|
||||
private function setPastTotalVisitsMetadata($dataTable, $pastTable)
|
||||
{
|
||||
if ($pastTable instanceof DataTable) {
|
||||
$total = 0;
|
||||
$metric = 'nb_visits';
|
||||
|
||||
foreach ($pastTable->getRows() as $row) {
|
||||
$total += $row->getColumn($metric);
|
||||
}
|
||||
|
||||
$dataTable->setMetadata(self::getTotalMetadataName($metric . '_lastdate'), $total);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -433,64 +480,27 @@ class API extends \Piwik\Plugin\API
|
|||
return 'last_period_' . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataTable|DataTable\Map $dataTable
|
||||
* @param $fieldsToGet
|
||||
* @param $sitesToProblablyAdd
|
||||
*/
|
||||
private function addMissingWebsites($dataTable, $fieldsToGet, $sitesToProblablyAdd)
|
||||
private function populateLabel($dataTable)
|
||||
{
|
||||
$siteIdsInDataTable = array();
|
||||
foreach ($dataTable->getRows() as $row) {
|
||||
/** @var DataTable\Row $row */
|
||||
$siteIdsInDataTable[] = $row->getColumn('label');
|
||||
}
|
||||
|
||||
foreach ($sitesToProblablyAdd as $site) {
|
||||
if (!in_array($site['idsite'], $siteIdsInDataTable)) {
|
||||
$siteRow = array_combine($fieldsToGet, array_pad(array(), count($fieldsToGet), 0));
|
||||
$siteRow['label'] = (int) $site['idsite'];
|
||||
$dataTable->addRowFromSimpleArray($siteRow);
|
||||
$dataTable->filter(function (DataTable $table) {
|
||||
foreach ($table->getRowsWithoutSummaryRow() as $row) {
|
||||
$row->setColumn('label', $row->getMetadata('idsite'));
|
||||
}
|
||||
}
|
||||
});
|
||||
// make sure label column is always first column
|
||||
$dataTable->queueFilter(function (DataTable $table) {
|
||||
foreach ($table->getRowsWithoutSummaryRow() as $row) {
|
||||
$row->setColumns(array_merge(array('label' => $row->getColumn('label')), $row->getColumns()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private function removeEcommerceRelatedMetricsOnNonEcommercePiwikSites($dataTable, $apiECommerceMetrics)
|
||||
private function isEcommerceEvolutionMetric($metricSettings)
|
||||
{
|
||||
// $dataTableRows instanceOf Row[]
|
||||
$dataTableRows = $dataTable->getRows();
|
||||
|
||||
foreach ($dataTableRows as $dataTableRow) {
|
||||
$siteId = $dataTableRow->getColumn('label');
|
||||
if (!Site::isEcommerceEnabledFor($siteId)) {
|
||||
foreach ($apiECommerceMetrics as $metricSettings) {
|
||||
$dataTableRow->deleteColumn($metricSettings[self::METRIC_RECORD_NAME_KEY]);
|
||||
$dataTableRow->deleteColumn($metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function mergeDataTableMapAndPopulateLabel($idSitesOrIdSite, $multipleWebsitesRequested, $dataTable)
|
||||
{
|
||||
// get rid of the DataTable\Map that is created by the IndexedBySite archive type
|
||||
if ($dataTable instanceof DataTable\Map && $multipleWebsitesRequested) {
|
||||
|
||||
return $dataTable->mergeChildren();
|
||||
|
||||
} else {
|
||||
|
||||
if (!$dataTable instanceof DataTable\Map && $dataTable->getRowsCount() > 0) {
|
||||
|
||||
$firstSite = is_array($idSitesOrIdSite) ? reset($idSitesOrIdSite) : $idSitesOrIdSite;
|
||||
|
||||
$firstDataTableRow = $dataTable->getFirstRow();
|
||||
|
||||
$firstDataTableRow->setColumn('label', $firstSite);
|
||||
}
|
||||
}
|
||||
|
||||
return $dataTable;
|
||||
return in_array($metricSettings[self::METRIC_EVOLUTION_COL_NAME_KEY], array(
|
||||
self::GOAL_REVENUE_METRIC . '_evolution',
|
||||
self::ECOMMERCE_ORDERS_METRIC . '_evolution',
|
||||
self::ECOMMERCE_REVENUE_METRIC . '_evolution'
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace Piwik\Plugins\MultiSites\Columns\Metrics;
|
||||
|
||||
use Piwik\DataTable;
|
||||
use Piwik\DataTable\Row;
|
||||
use Piwik\Plugins\CoreHome\Columns\Metrics\EvolutionMetric;
|
||||
use Piwik\Site;
|
||||
|
||||
/**
|
||||
* Ecommerce evolution metric adapter. This is a special processed metric for MultiSites API methods. It will
|
||||
* only be calculated for sites that have ecommerce enabled. The site is determined by the label
|
||||
* of each row.
|
||||
*/
|
||||
class EcommerceOnlyEvolutionMetric extends EvolutionMetric
|
||||
{
|
||||
private $isRevenueEvolution;
|
||||
|
||||
public function __construct($wrapped, DataTable $pastData, $evolutionMetricName = false, $quotientPrecision = 0)
|
||||
{
|
||||
parent::__construct($wrapped, $pastData, $evolutionMetricName, $quotientPrecision);
|
||||
|
||||
$this->isRevenueEvolution = $this->getName() == 'revenue_evolution';
|
||||
}
|
||||
|
||||
public function compute(Row $row)
|
||||
{
|
||||
$columnName = $this->getWrappedName();
|
||||
$currentValue = $this->getMetric($row, $columnName);
|
||||
|
||||
// if the site this is for doesn't support ecommerce & this is for the revenue_evolution column,
|
||||
// we don't add the new column
|
||||
if ($currentValue === false || !$this->isRevenueEvolution) {
|
||||
$idSite = $row->getMetadata('idsite');
|
||||
if (!$idSite || !Site::isEcommerceEnabledFor($idSite)) {
|
||||
$row->deleteColumn($columnName);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::compute($row);
|
||||
}
|
||||
}
|
||||
20
www/analytics/plugins/MultiSites/Columns/Website.php
Normal file
20
www/analytics/plugins/MultiSites/Columns/Website.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites\Columns;
|
||||
|
||||
use Piwik\Columns\Dimension;
|
||||
use Piwik\Piwik;
|
||||
|
||||
class Website extends Dimension
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return Piwik::translate('General_Website');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
|
|
@ -11,22 +11,25 @@ namespace Piwik\Plugins\MultiSites;
|
|||
use Piwik\Common;
|
||||
use Piwik\Config;
|
||||
use Piwik\Date;
|
||||
use Piwik\MetricsFormatter;
|
||||
use Piwik\Period;
|
||||
use Piwik\DataTable;
|
||||
use Piwik\DataTable\Row;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\MultiSites\API as APIMultiSites;
|
||||
use Piwik\Plugins\SitesManager\API as APISitesManager;
|
||||
use Piwik\Site;
|
||||
use Piwik\Translation\Translator;
|
||||
use Piwik\View;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Controller extends \Piwik\Plugin\Controller
|
||||
{
|
||||
public function __construct()
|
||||
/**
|
||||
* @var Translator
|
||||
*/
|
||||
private $translator;
|
||||
|
||||
public function __construct(Translator $translator)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->translator = $translator;
|
||||
}
|
||||
|
||||
public function index()
|
||||
|
|
@ -39,6 +42,34 @@ class Controller extends \Piwik\Plugin\Controller
|
|||
return $this->getSitesInfo($isWidgetized = true);
|
||||
}
|
||||
|
||||
public function getAllWithGroups()
|
||||
{
|
||||
Piwik::checkUserHasSomeViewAccess();
|
||||
|
||||
$period = Common::getRequestVar('period', null, 'string');
|
||||
$date = Common::getRequestVar('date', null, 'string');
|
||||
$segment = Common::getRequestVar('segment', false, 'string');
|
||||
$pattern = Common::getRequestVar('pattern', '', 'string');
|
||||
$limit = Common::getRequestVar('filter_limit', 0, 'int');
|
||||
$segment = $segment ?: false;
|
||||
$request = $_GET + $_POST;
|
||||
|
||||
$dashboard = new Dashboard($period, $date, $segment);
|
||||
|
||||
if ($pattern !== '') {
|
||||
$dashboard->search(strtolower($pattern));
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'numSites' => $dashboard->getNumSites(),
|
||||
'totals' => $dashboard->getTotals(),
|
||||
'lastDate' => $dashboard->getLastDate(),
|
||||
'sites' => $dashboard->getSites($request, $limit)
|
||||
);
|
||||
|
||||
return json_encode($response);
|
||||
}
|
||||
|
||||
public function getSitesInfo($isWidgetized = false)
|
||||
{
|
||||
Piwik::checkUserHasSomeViewAccess();
|
||||
|
|
@ -68,6 +99,8 @@ class Controller extends \Piwik\Plugin\Controller
|
|||
|
||||
$this->setGeneralVariablesView($view);
|
||||
|
||||
$view->siteName = $this->translator->translate('General_AllWebsitesDashboard');
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
|
|
|
|||
342
www/analytics/plugins/MultiSites/Dashboard.php
Normal file
342
www/analytics/plugins/MultiSites/Dashboard.php
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites;
|
||||
|
||||
use Piwik\API\DataTablePostProcessor;
|
||||
use Piwik\API\ResponseBuilder;
|
||||
use Piwik\Config;
|
||||
use Piwik\Metrics\Formatter;
|
||||
use Piwik\NumberFormatter;
|
||||
use Piwik\Period;
|
||||
use Piwik\DataTable;
|
||||
use Piwik\DataTable\Row;
|
||||
use Piwik\DataTable\Row\DataTableSummaryRow;
|
||||
use Piwik\Plugin\Report;
|
||||
use Piwik\Plugins\API\ProcessedReport;
|
||||
use Piwik\Site;
|
||||
use Piwik\View;
|
||||
|
||||
/**
|
||||
* Fetches and formats the response of `MultiSites.getAll` in a way that it can be used by the All Websites AngularJS
|
||||
* widget. Eg sites are moved into groups if one is assigned, stats are calculated for groups, etc.
|
||||
*/
|
||||
class Dashboard
|
||||
{
|
||||
/** @var DataTable */
|
||||
private $sitesByGroup;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $numSites = 0;
|
||||
|
||||
/**
|
||||
* Array of metrics that will be displayed and will be number formatted
|
||||
* @var array
|
||||
*/
|
||||
private $displayedMetricColumns = array('nb_visits', 'nb_pageviews', 'nb_actions', 'revenue');
|
||||
|
||||
/**
|
||||
* @param string $period
|
||||
* @param string $date
|
||||
* @param string|false $segment
|
||||
*/
|
||||
public function __construct($period, $date, $segment)
|
||||
{
|
||||
$sites = API::getInstance()->getAll($period, $date, $segment, $_restrictSitesToLogin = false,
|
||||
$enhanced = true, $searchTerm = false,
|
||||
$this->displayedMetricColumns);
|
||||
$sites->deleteRow(DataTable::ID_SUMMARY_ROW);
|
||||
|
||||
/** @var DataTable $pastData */
|
||||
$pastData = $sites->getMetadata('pastData');
|
||||
|
||||
$sites->filter(function (DataTable $table) use ($pastData) {
|
||||
$pastRow = null;
|
||||
|
||||
foreach ($table->getRows() as $row) {
|
||||
$idSite = $row->getColumn('label');
|
||||
$site = Site::getSite($idSite);
|
||||
// we cannot queue label and group as we might need them for search and sorting!
|
||||
$row->setColumn('label', $site['name']);
|
||||
$row->setMetadata('group', $site['group']);
|
||||
|
||||
if ($pastData) {
|
||||
// if we do not update the pastData labels, the evolution cannot be calculated correctly.
|
||||
$pastRow = $pastData->getRowFromLabel($idSite);
|
||||
if ($pastRow) {
|
||||
$pastRow->setColumn('label', $site['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($pastData && $pastRow) {
|
||||
$pastData->setLabelsHaveChanged();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$this->setSitesTable($sites);
|
||||
}
|
||||
|
||||
public function setSitesTable(DataTable $sites)
|
||||
{
|
||||
$this->sitesByGroup = $this->moveSitesHavingAGroupIntoSubtables($sites);
|
||||
$this->rememberNumberOfSites();
|
||||
}
|
||||
|
||||
public function getSites($request, $limit)
|
||||
{
|
||||
$request['filter_limit'] = $limit;
|
||||
$request['filter_offset'] = isset($request['filter_offset']) ? $request['filter_offset'] : 0;
|
||||
|
||||
$this->makeSitesFlatAndApplyGenericFilters($this->sitesByGroup, $request);
|
||||
$sites = $this->convertDataTableToArrayAndApplyQueuedFilters($this->sitesByGroup, $request);
|
||||
$sites = $this->enrichValues($sites);
|
||||
|
||||
return $sites;
|
||||
}
|
||||
|
||||
public function getTotals()
|
||||
{
|
||||
$totals = array(
|
||||
'nb_pageviews' => $this->sitesByGroup->getMetadata('total_nb_pageviews'),
|
||||
'nb_visits' => $this->sitesByGroup->getMetadata('total_nb_visits'),
|
||||
'nb_actions' => $this->sitesByGroup->getMetadata('total_nb_actions'),
|
||||
'revenue' => $this->sitesByGroup->getMetadata('total_revenue'),
|
||||
'nb_visits_lastdate' => $this->sitesByGroup->getMetadata('total_nb_visits_lastdate') ? : 0,
|
||||
);
|
||||
$this->formatMetrics($totals);
|
||||
return $totals;
|
||||
}
|
||||
|
||||
private function formatMetrics(&$metrics)
|
||||
{
|
||||
$formatter = NumberFormatter::getInstance();
|
||||
foreach($metrics as $metricName => &$value) {
|
||||
if(in_array($metricName, $this->displayedMetricColumns)) {
|
||||
|
||||
if( strpos($metricName, 'revenue') !== false) {
|
||||
$currency = isset($metrics['idsite']) ? Site::getCurrencySymbolFor($metrics['idsite']) : '';
|
||||
$value = $formatter->formatCurrency($value, $currency);
|
||||
continue;
|
||||
}
|
||||
$value = $formatter->format($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getNumSites()
|
||||
{
|
||||
return $this->numSites;
|
||||
}
|
||||
|
||||
public function search($pattern)
|
||||
{
|
||||
$this->nestedSearch($this->sitesByGroup, $pattern);
|
||||
$this->rememberNumberOfSites();
|
||||
}
|
||||
|
||||
private function rememberNumberOfSites()
|
||||
{
|
||||
$this->numSites = $this->sitesByGroup->getRowsCountRecursive();
|
||||
}
|
||||
|
||||
private function nestedSearch(DataTable $sitesByGroup, $pattern)
|
||||
{
|
||||
foreach ($sitesByGroup->getRows() as $index => $site) {
|
||||
|
||||
$label = strtolower($site->getColumn('label'));
|
||||
$labelMatches = false !== strpos($label, $pattern);
|
||||
|
||||
if ($site->getMetadata('isGroup')) {
|
||||
$subtable = $site->getSubtable();
|
||||
$this->nestedSearch($subtable, $pattern);
|
||||
|
||||
if (!$labelMatches && !$subtable->getRowsCount()) {
|
||||
// we keep the group if at least one site within the group matches the pattern
|
||||
$sitesByGroup->deleteRow($index);
|
||||
}
|
||||
|
||||
} elseif (!$labelMatches) {
|
||||
$group = $site->getMetadata('group');
|
||||
|
||||
if (!$group || false === strpos(strtolower($group), $pattern)) {
|
||||
$sitesByGroup->deleteRow($index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getLastDate()
|
||||
{
|
||||
$lastPeriod = $this->sitesByGroup->getMetadata('last_period_date');
|
||||
|
||||
if (!empty($lastPeriod)) {
|
||||
$lastPeriod = $lastPeriod->toString();
|
||||
} else {
|
||||
$lastPeriod = '';
|
||||
}
|
||||
|
||||
return $lastPeriod;
|
||||
}
|
||||
|
||||
private function convertDataTableToArrayAndApplyQueuedFilters(DataTable $table, $request)
|
||||
{
|
||||
$request['serialize'] = 0;
|
||||
$request['expanded'] = 0;
|
||||
$request['totals'] = 0;
|
||||
$request['format_metrics'] = 1;
|
||||
$request['disable_generic_filters'] = 1;
|
||||
|
||||
$responseBuilder = new ResponseBuilder('php', $request);
|
||||
$rows = $responseBuilder->getResponse($table, 'MultiSites', 'getAll');
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
private function moveSitesHavingAGroupIntoSubtables(DataTable $sites)
|
||||
{
|
||||
/** @var DataTableSummaryRow[] $groups */
|
||||
$groups = array();
|
||||
|
||||
$sitesByGroup = $this->makeCloneOfDataTableSites($sites);
|
||||
$sitesByGroup->enableRecursiveFilters(); // we need to make sure filters get applied to subtables (groups)
|
||||
|
||||
foreach ($sites->getRows() as $site) {
|
||||
|
||||
$group = $site->getMetadata('group');
|
||||
|
||||
if (!empty($group) && !array_key_exists($group, $groups)) {
|
||||
$row = new DataTableSummaryRow();
|
||||
$row->setColumn('label', $group);
|
||||
$row->setMetadata('isGroup', 1);
|
||||
$row->setSubtable($this->createGroupSubtable($sites));
|
||||
$sitesByGroup->addRow($row);
|
||||
|
||||
$groups[$group] = $row;
|
||||
}
|
||||
|
||||
if (!empty($group)) {
|
||||
$groups[$group]->getSubtable()->addRow($site);
|
||||
} else {
|
||||
$sitesByGroup->addRow($site);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($groups as $group) {
|
||||
// we need to recalculate as long as all rows are there, as soon as some rows are removed
|
||||
// we can no longer recalculate the correct value. We might even calculate values for groups
|
||||
// that are not returned. If this becomes a problem we need to keep a copy of this to recalculate
|
||||
// only actual returned groups.
|
||||
$group->recalculate();
|
||||
}
|
||||
|
||||
return $sitesByGroup;
|
||||
}
|
||||
|
||||
private function createGroupSubtable(DataTable $sites)
|
||||
{
|
||||
$table = new DataTable();
|
||||
$processedMetrics = $sites->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
|
||||
$table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $processedMetrics);
|
||||
|
||||
return $table;
|
||||
}
|
||||
|
||||
private function makeCloneOfDataTableSites(DataTable $sites)
|
||||
{
|
||||
$sitesByGroup = $sites->getEmptyClone(true);
|
||||
// we handle them ourselves for faster performance etc. This way we also avoid to apply them twice.
|
||||
$sitesByGroup->disableFilter('ColumnCallbackReplace');
|
||||
$sitesByGroup->disableFilter('MetadataCallbackAddMetadata');
|
||||
|
||||
return $sitesByGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure to not have any subtables anymore.
|
||||
*
|
||||
* So if $table is
|
||||
* array(
|
||||
* site1
|
||||
* site2
|
||||
* subtable => site3
|
||||
* site4
|
||||
* site5
|
||||
* site6
|
||||
* site7
|
||||
* )
|
||||
*
|
||||
* it will return
|
||||
*
|
||||
* array(
|
||||
* site1
|
||||
* site2
|
||||
* site3
|
||||
* site4
|
||||
* site5
|
||||
* site6
|
||||
* site7
|
||||
* )
|
||||
*
|
||||
* in a sorted order
|
||||
*
|
||||
* @param DataTable $table
|
||||
* @param array $request
|
||||
*/
|
||||
private function makeSitesFlatAndApplyGenericFilters(DataTable $table, $request)
|
||||
{
|
||||
// we handle limit here as we have to apply sort filter, then make sites flat, then apply limit filter.
|
||||
$filterOffset = $request['filter_offset'];
|
||||
$filterLimit = $request['filter_limit'];
|
||||
unset($request['filter_offset']);
|
||||
unset($request['filter_limit']);
|
||||
|
||||
// filter_sort_column does not work correctly is a bug in MultiSites.getAll
|
||||
if (!empty($request['filter_sort_column']) && $request['filter_sort_column'] === 'nb_pageviews') {
|
||||
$request['filter_sort_column'] = 'Actions_nb_pageviews';
|
||||
} elseif (!empty($request['filter_sort_column']) && $request['filter_sort_column'] === 'revenue') {
|
||||
$request['filter_sort_column'] = 'Goal_revenue';
|
||||
}
|
||||
|
||||
// make sure no limit filter is applied, we will do this manually
|
||||
$table->disableFilter('Limit');
|
||||
|
||||
// this will apply the sort filter
|
||||
/** @var DataTable $table */
|
||||
$genericFilter = new DataTablePostProcessor('MultiSites', 'getAll', $request);
|
||||
$table = $genericFilter->applyGenericFilters($table);
|
||||
|
||||
// make sure from now on the sites will be no longer sorted, they were already sorted
|
||||
$table->disableFilter('Sort');
|
||||
|
||||
// make sites flat and limit
|
||||
$table->filter('Piwik\Plugins\MultiSites\DataTable\Filter\NestedSitesLimiter', array($filterOffset, $filterLimit));
|
||||
}
|
||||
|
||||
private function enrichValues($sites)
|
||||
{
|
||||
foreach ($sites as &$site) {
|
||||
if (!isset($site['idsite'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$site['main_url'] = Site::getMainUrlFor($site['idsite']);
|
||||
|
||||
$this->formatMetrics($site);
|
||||
}
|
||||
|
||||
return $sites;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites\DataTable\Filter;
|
||||
|
||||
use Piwik\DataTable\BaseFilter;
|
||||
use Piwik\DataTable\Row;
|
||||
use Piwik\DataTable;
|
||||
|
||||
/**
|
||||
* Makes sure to not have any subtables anymore and applies the limit to the flattened table.
|
||||
*
|
||||
* So if $table is
|
||||
* array(
|
||||
* site1
|
||||
* group2
|
||||
* subtable => site3
|
||||
* site4
|
||||
* site5
|
||||
* site6
|
||||
* site7
|
||||
* )
|
||||
*
|
||||
* it will format this to
|
||||
*
|
||||
* array(
|
||||
* site1
|
||||
* group2
|
||||
* site3
|
||||
* site4
|
||||
* site5
|
||||
* site6
|
||||
* site7
|
||||
* )
|
||||
*
|
||||
* and then apply the limit filter.
|
||||
*
|
||||
* Each group will not count into the limit/offset. This way, if one requests a limit of 50 sites,
|
||||
* we make sure to return 50 sites.
|
||||
*
|
||||
* @param $sites
|
||||
* @return array
|
||||
*/
|
||||
class NestedSitesLimiter extends BaseFilter
|
||||
{
|
||||
private $offset = 0;
|
||||
private $limit = 0;
|
||||
/**
|
||||
* @var Row[]
|
||||
*/
|
||||
private $rows = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param DataTable $table The table to eventually filter.
|
||||
*/
|
||||
public function __construct($table, $offset, $limit)
|
||||
{
|
||||
parent::__construct($table);
|
||||
$this->offset = $offset;
|
||||
$this->limit = $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DataTable $table
|
||||
*/
|
||||
public function filter($table)
|
||||
{
|
||||
$numRows = 0;
|
||||
$lastGroupFromPreviousPage = null;
|
||||
|
||||
foreach ($table->getRows() as $row) {
|
||||
|
||||
$this->addRowIfNeeded($row, $numRows);
|
||||
$numRows++;
|
||||
|
||||
$subtable = $row->getSubtable();
|
||||
if ($subtable) {
|
||||
if (!$this->hasRows()) {
|
||||
$lastGroupFromPreviousPage = $row;
|
||||
}
|
||||
foreach ($subtable->getRows() as $subRow) {
|
||||
$this->addRowIfNeeded($subRow, $numRows);
|
||||
$numRows++;
|
||||
}
|
||||
$row->removeSubtable();
|
||||
}
|
||||
|
||||
if ($this->hasNumberOfRequestedRowsFound()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->prependGroupIfFirstSiteBelongsToAGroupButGroupIsMissingInRows($lastGroupFromPreviousPage);
|
||||
|
||||
$table->setRows($this->rows);
|
||||
}
|
||||
|
||||
private function hasNumberOfRequestedRowsFound()
|
||||
{
|
||||
return count($this->rows) >= $this->limit;
|
||||
}
|
||||
|
||||
private function hasRows()
|
||||
{
|
||||
return count($this->rows) !== 0;
|
||||
}
|
||||
|
||||
private function addRowIfNeeded(Row $row, $numRows)
|
||||
{
|
||||
$inOffset = $numRows >= $this->offset;
|
||||
|
||||
if ($inOffset && !$this->hasNumberOfRequestedRowsFound()) {
|
||||
$this->rows[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Row $lastGroupFromPreviousPage
|
||||
*/
|
||||
private function prependGroupIfFirstSiteBelongsToAGroupButGroupIsMissingInRows($lastGroupFromPreviousPage)
|
||||
{
|
||||
if ($lastGroupFromPreviousPage && !empty($this->rows)) {
|
||||
// the result starts with a row that does belong to a group, we make sure to show this group before that site
|
||||
$group = reset($this->rows)->getMetadata('group');
|
||||
if ($group && $lastGroupFromPreviousPage->getColumn('label') === $group) {
|
||||
array_unshift($this->rows, $lastGroupFromPreviousPage);
|
||||
// we do not remove the last item as it could result in errors, instead we show $limit+1 entries
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
www/analytics/plugins/MultiSites/Menu.php
Normal file
23
www/analytics/plugins/MultiSites/Menu.php
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites;
|
||||
|
||||
use Piwik\Menu\MenuTop;
|
||||
use Piwik\Piwik;
|
||||
|
||||
class Menu extends \Piwik\Plugin\Menu
|
||||
{
|
||||
public function configureTopMenu(MenuTop $menu)
|
||||
{
|
||||
$urlParams = $this->urlForActionWithDefaultUserParams('index', array('segment' => false, 'idSite' => false));
|
||||
$tooltip = Piwik::translate('MultiSites_TopLinkTooltip');
|
||||
|
||||
$menu->add('General_MultiSitesSummary', null, $urlParams, true, 3, $tooltip);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +1,53 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites;
|
||||
use Piwik\Menu\MenuTop;
|
||||
|
||||
use Piwik\Piwik;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class MultiSites extends \Piwik\Plugin
|
||||
{
|
||||
public function getInformation()
|
||||
{
|
||||
$info = parent::getInformation();
|
||||
$info['authors'] = array(array('name' => 'Piwik PRO', 'homepage' => 'http://piwik.pro'));
|
||||
return $info;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Piwik\Plugin::getListHooksRegistered
|
||||
* @see Piwik\Plugin::registerEvents
|
||||
*/
|
||||
public function getListHooksRegistered()
|
||||
public function registerEvents()
|
||||
{
|
||||
return array(
|
||||
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
|
||||
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
|
||||
'Menu.Top.addItems' => 'addTopMenu',
|
||||
'API.getReportMetadata' => 'getReportMetadata',
|
||||
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
|
||||
'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations'
|
||||
);
|
||||
}
|
||||
|
||||
public function addMetricTranslations(&$translations)
|
||||
{
|
||||
$appendix = " " . Piwik::translate('MultiSites_Evolution');
|
||||
$metrics = array(
|
||||
'visits_evolution' => Piwik::translate('General_ColumnNbVisits') . $appendix,
|
||||
'actions_evolution' => Piwik::translate('General_ColumnNbActions') . $appendix,
|
||||
'pageviews_evolution' => Piwik::translate('General_ColumnPageviews') . $appendix,
|
||||
'revenue_evolution' => Piwik::translate('General_ColumnRevenue') . $appendix,
|
||||
'nb_conversions_evolution' => Piwik::translate('Goals_ColumnConversions') . $appendix,
|
||||
'orders_evolution' => Piwik::translate('General_EcommerceOrders') . $appendix,
|
||||
'ecommerce_revenue_evolution' => Piwik::translate('General_ProductRevenue') . $appendix,
|
||||
);
|
||||
|
||||
$translations = array_merge($translations, $metrics);
|
||||
}
|
||||
|
||||
public function getClientSideTranslationKeys(&$translations)
|
||||
{
|
||||
$translations[] = 'General_Website';
|
||||
$translations[] = 'General_ColumnNbVisits';
|
||||
$translations[] = 'General_ColumnPageviews';
|
||||
$translations[] = 'General_ColumnRevenue';
|
||||
$translations[] = 'General_TotalVisitsPageviewsRevenue';
|
||||
$translations[] = 'General_TotalVisitsPageviewsActionsRevenue';
|
||||
$translations[] = 'General_EvolutionSummaryGeneric';
|
||||
$translations[] = 'General_AllWebsitesDashboard';
|
||||
$translations[] = 'General_NVisits';
|
||||
|
|
@ -56,61 +60,25 @@ class MultiSites extends \Piwik\Plugin
|
|||
$translations[] = 'Actions_SubmenuSitesearch';
|
||||
$translations[] = 'MultiSites_LoadingWebsites';
|
||||
$translations[] = 'General_ErrorRequest';
|
||||
}
|
||||
|
||||
public function getReportMetadata(&$reports)
|
||||
{
|
||||
$metadataMetrics = array();
|
||||
foreach (API::getApiMetrics($enhanced = true) as $metricName => $metricSettings) {
|
||||
$metadataMetrics[$metricName] =
|
||||
Piwik::translate($metricSettings[API::METRIC_TRANSLATION_KEY]);
|
||||
$metadataMetrics[$metricSettings[API::METRIC_EVOLUTION_COL_NAME_KEY]] =
|
||||
Piwik::translate($metricSettings[API::METRIC_TRANSLATION_KEY]) . " " . Piwik::translate('MultiSites_Evolution');
|
||||
}
|
||||
|
||||
$reports[] = array(
|
||||
'category' => Piwik::translate('General_MultiSitesSummary'),
|
||||
'name' => Piwik::translate('General_AllWebsitesDashboard'),
|
||||
'module' => 'MultiSites',
|
||||
'action' => 'getAll',
|
||||
'dimension' => Piwik::translate('General_Website'), // re-using translation
|
||||
'metrics' => $metadataMetrics,
|
||||
'processedMetrics' => false,
|
||||
'constantRowsCount' => false,
|
||||
'order' => 4
|
||||
);
|
||||
|
||||
$reports[] = array(
|
||||
'category' => Piwik::translate('General_MultiSitesSummary'),
|
||||
'name' => Piwik::translate('General_SingleWebsitesDashboard'),
|
||||
'module' => 'MultiSites',
|
||||
'action' => 'getOne',
|
||||
'dimension' => Piwik::translate('General_Website'), // re-using translation
|
||||
'metrics' => $metadataMetrics,
|
||||
'processedMetrics' => false,
|
||||
'constantRowsCount' => false,
|
||||
'order' => 5
|
||||
);
|
||||
}
|
||||
|
||||
public function addTopMenu()
|
||||
{
|
||||
$urlParams = array('module' => 'MultiSites', 'action' => 'index', 'segment' => false);
|
||||
$tooltip = Piwik::translate('MultiSites_TopLinkTooltip');
|
||||
MenuTop::addEntry('General_MultiSitesSummary', $urlParams, true, 3, $isHTML = false, $tooltip);
|
||||
$translations[] = 'General_Pagination';
|
||||
$translations[] = 'General_ClickToSearch';
|
||||
$translations[] = 'General_NeedMoreHelp';
|
||||
$translations[] = 'General_Faq';
|
||||
$translations[] = 'Feedback_CommunityHelp';
|
||||
$translations[] = 'Feedback_ProfessionalHelp';
|
||||
}
|
||||
|
||||
public function getJsFiles(&$jsFiles)
|
||||
{
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/dashboard/dashboard-model.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/dashboard/dashboard-controller.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/dashboard/dashboard-filter.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/dashboard/dashboard-directive.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/site/site-directive.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/dashboard/dashboard-model.service.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/dashboard/dashboard.controller.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/dashboard/dashboard.directive.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/site/site.controller.js";
|
||||
$jsFiles[] = "plugins/MultiSites/angularjs/site/site.directive.js";
|
||||
}
|
||||
|
||||
public function getStylesheetFiles(&$stylesheets)
|
||||
{
|
||||
$stylesheets[] = "plugins/MultiSites/angularjs/dashboard/dashboard.less";
|
||||
$stylesheets[] = "plugins/MultiSites/angularjs/dashboard/dashboard.directive.less";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
37
www/analytics/plugins/MultiSites/Reports/Base.php
Normal file
37
www/analytics/plugins/MultiSites/Reports/Base.php
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites\Reports;
|
||||
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\MultiSites\API;
|
||||
|
||||
abstract class Base extends \Piwik\Plugin\Report
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
$this->category = 'General_MultiSitesSummary';
|
||||
|
||||
$allMetricsInfo = API::getApiMetrics($enhanced = true);
|
||||
|
||||
$metadataMetrics = array();
|
||||
$processedMetricsMetadata = array();
|
||||
|
||||
foreach ($allMetricsInfo as $metricName => $metricSettings) {
|
||||
$metadataMetrics[$metricName] =
|
||||
Piwik::translate($metricSettings[API::METRIC_TRANSLATION_KEY]);
|
||||
|
||||
$processedMetricsMetadata[$metricSettings[API::METRIC_EVOLUTION_COL_NAME_KEY]] =
|
||||
Piwik::translate($metricSettings[API::METRIC_TRANSLATION_KEY]) . " " . Piwik::translate('MultiSites_Evolution');
|
||||
}
|
||||
|
||||
$this->metrics = array_keys($metadataMetrics);
|
||||
$this->processedMetrics = array_keys($processedMetricsMetadata);
|
||||
}
|
||||
|
||||
}
|
||||
25
www/analytics/plugins/MultiSites/Reports/GetAll.php
Normal file
25
www/analytics/plugins/MultiSites/Reports/GetAll.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites\Reports;
|
||||
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\MultiSites\Columns\Website;
|
||||
|
||||
class GetAll extends Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->dimension = new Website();
|
||||
$this->name = Piwik::translate('General_AllWebsitesDashboard');
|
||||
$this->documentation = ''; // TODO
|
||||
$this->constantRowsCount = false;
|
||||
$this->order = 4;
|
||||
}
|
||||
}
|
||||
26
www/analytics/plugins/MultiSites/Reports/GetOne.php
Normal file
26
www/analytics/plugins/MultiSites/Reports/GetOne.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\MultiSites\Reports;
|
||||
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\MultiSites\Columns\Website;
|
||||
|
||||
class GetOne extends Base
|
||||
{
|
||||
protected function init()
|
||||
{
|
||||
parent::init();
|
||||
$this->dimension = new Website();
|
||||
$this->name = Piwik::translate('General_SingleWebsitesDashboard');
|
||||
$this->documentation = ''; // TODO
|
||||
$this->constantRowsCount = false;
|
||||
$this->order = 5;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp').controller('MultiSitesDashboardController', function($scope, piwik, multisitesDashboardModel){
|
||||
|
||||
$scope.model = multisitesDashboardModel;
|
||||
|
||||
$scope.reverse = true;
|
||||
$scope.predicate = 'nb_visits';
|
||||
$scope.evolutionSelector = 'visits_evolution';
|
||||
$scope.hasSuperUserAccess = piwik.hasSuperUserAccess;
|
||||
$scope.date = piwik.broadcast.getValueFromUrl('date');
|
||||
$scope.url = piwik.piwik_url;
|
||||
$scope.period = piwik.period;
|
||||
|
||||
$scope.sortBy = function (metric) {
|
||||
|
||||
var reverse = $scope.reverse;
|
||||
if ($scope.predicate == metric) {
|
||||
reverse = !reverse;
|
||||
}
|
||||
|
||||
$scope.predicate = metric;
|
||||
$scope.reverse = reverse;
|
||||
};
|
||||
|
||||
this.refresh = function (interval) {
|
||||
multisitesDashboardModel.fetchAllSites(interval);
|
||||
};
|
||||
});
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders the multisites dashboard
|
||||
* Example usage:
|
||||
*
|
||||
* <div piwik-multisites-dashboard
|
||||
* display-revenue-column="true"
|
||||
* show-sparklines="true"
|
||||
* date-sparkline="true"
|
||||
* page-size="50"
|
||||
* auto-refresh-today-report="500" // or 0 to disable
|
||||
* ></div>
|
||||
*/
|
||||
angular.module('piwikApp').directive('piwikMultisitesDashboard', function($document, piwik, $filter){
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
displayRevenueColumn: '@',
|
||||
showSparklines: '@',
|
||||
dateSparkline: '@'
|
||||
},
|
||||
templateUrl: 'plugins/MultiSites/angularjs/dashboard/dashboard.html?cb=' + piwik.cacheBuster,
|
||||
controller: 'MultiSitesDashboardController',
|
||||
link: function (scope, element, attrs, controller) {
|
||||
|
||||
if (attrs.pageSize) {
|
||||
scope.model.pageSize = attrs.pageSize;
|
||||
}
|
||||
|
||||
controller.refresh(attrs.autoRefreshTodayReport);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Filters a given list of websites and groups and makes sure only the websites within a given offset and limit are
|
||||
* displayed. It also makes sure sites are displayed under the groups. That means it flattens a structure like this:
|
||||
*
|
||||
* - website1
|
||||
* - website2
|
||||
* - website3.sites // this is a group
|
||||
* - website4
|
||||
* - website5
|
||||
* - website6
|
||||
*
|
||||
* to the following structure
|
||||
* - website1
|
||||
* - website2
|
||||
* - website3.sites // this is a group
|
||||
* - website4
|
||||
* - website5
|
||||
* - website6
|
||||
*/
|
||||
angular.module('piwikApp').filter('multiSitesGroupFilter', function() {
|
||||
return function(websites, from, to) {
|
||||
var offsetEnd = parseInt(from, 10) + parseInt(to, 10);
|
||||
var groups = {};
|
||||
|
||||
var sites = [];
|
||||
for (var index = 0; index < websites.length; index++) {
|
||||
var website = websites[index];
|
||||
|
||||
sites.push(website);
|
||||
if (website.sites && website.sites.length) {
|
||||
groups[website.label] = website;
|
||||
for (var innerIndex = 0; innerIndex < website.sites.length; innerIndex++) {
|
||||
sites.push(website.sites[innerIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
if (sites.length >= offsetEnd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if the first site is a website having a group, then try to find the related group and prepend it to the list
|
||||
// of sites to make sure we always display the name of the group that belongs to a website.
|
||||
var filteredSites = sites.slice(from, offsetEnd);
|
||||
|
||||
if (filteredSites.length && filteredSites[0] && filteredSites[0].group) {
|
||||
var groupName = filteredSites[0].group;
|
||||
if (groups[groupName]) {
|
||||
filteredSites.unshift(groups[groupName]);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredSites;
|
||||
};
|
||||
});
|
||||
|
||||
|
|
@ -1,273 +0,0 @@
|
|||
/**
|
||||
* Model for Multisites Dashboard aka All Websites Dashboard.
|
||||
*
|
||||
*/
|
||||
angular.module('piwikApp').factory('multisitesDashboardModel', function (piwikApi, $filter, $timeout) {
|
||||
/**
|
||||
*
|
||||
* this is the list of all available sites. For performance reason this list is different to model.sites. ngRepeat
|
||||
* won't operate on the whole list this way. The allSites array contains websites and groups in the following
|
||||
* structure
|
||||
*
|
||||
* - website1
|
||||
* - website2
|
||||
* - website3.sites = [ // this is a group
|
||||
* - website4
|
||||
* - website5
|
||||
* ]
|
||||
* - website6
|
||||
*
|
||||
* This structure allows us to display the sites within a group directly under the group without big logic and also
|
||||
* allows us to calculate the summary for each group easily
|
||||
*/
|
||||
var allSitesByGroup = [];
|
||||
|
||||
var model = {};
|
||||
// those sites are going to be displayed
|
||||
model.sites = [];
|
||||
model.isLoading = false;
|
||||
model.pageSize = 5;
|
||||
model.currentPage = 0;
|
||||
model.totalVisits = '?';
|
||||
model.totalActions = '?';
|
||||
model.totalRevenue = '?';
|
||||
model.searchTerm = '';
|
||||
model.lastVisits = '?';
|
||||
model.lastVisitsDate = '?';
|
||||
|
||||
fetchPreviousSummary();
|
||||
|
||||
// create a new group object which has similar structure than a website
|
||||
function createGroup(name){
|
||||
return {
|
||||
label: name,
|
||||
sites: [],
|
||||
nb_visits: 0,
|
||||
nb_pageviews: 0,
|
||||
revenue: 0,
|
||||
isGroup: true
|
||||
};
|
||||
}
|
||||
|
||||
// create a new group with empty site to make sure we do not change the original group in $allSites
|
||||
function copyGroup(group)
|
||||
{
|
||||
return {
|
||||
label: group.label,
|
||||
sites: [],
|
||||
nb_visits: group.nb_visits,
|
||||
nb_pageviews: group.nb_pageviews,
|
||||
revenue: group.revenue,
|
||||
isGroup: true
|
||||
};
|
||||
}
|
||||
|
||||
function onError () {
|
||||
model.errorLoadingSites = true;
|
||||
model.sites = [];
|
||||
allSitesByGroup = [];
|
||||
}
|
||||
|
||||
function calculateMetricsForEachGroup(groups)
|
||||
{
|
||||
angular.forEach(groups, function (group) {
|
||||
angular.forEach(group.sites, function (site) {
|
||||
var revenue = (site.revenue+'').match(/(\d+\.?\d*)/); // convert $ 0.00 to 0.00 or 5€ to 5
|
||||
group.nb_visits += parseInt(site.nb_visits, 10);
|
||||
group.nb_pageviews += parseInt(site.nb_pageviews, 10);
|
||||
if (revenue.length) {
|
||||
group.revenue += parseInt(revenue[0], 10);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createGroupsAndMoveSitesIntoRelatedGroup(allSitesUnordered, reportMetadata)
|
||||
{
|
||||
var sitesByGroup = [];
|
||||
var groups = {};
|
||||
|
||||
// we do 3 things (complete site information, create groups, move sites into group) in one step for
|
||||
// performance reason, there can be > 20k sites
|
||||
angular.forEach(allSitesUnordered, function (site, index) {
|
||||
site.idsite = reportMetadata[index].idsite;
|
||||
site.group = reportMetadata[index].group;
|
||||
site.main_url = reportMetadata[index].main_url;
|
||||
|
||||
if (site.group) {
|
||||
|
||||
if (!groups[site.group]) {
|
||||
var group = createGroup(site.group);
|
||||
|
||||
groups[site.group] = group;
|
||||
sitesByGroup.push(group);
|
||||
}
|
||||
|
||||
groups[site.group].sites.push(site);
|
||||
|
||||
} else {
|
||||
sitesByGroup.push(site);
|
||||
}
|
||||
});
|
||||
|
||||
calculateMetricsForEachGroup(groups);
|
||||
|
||||
return sitesByGroup;
|
||||
}
|
||||
|
||||
model.updateWebsitesList = function (processedReport) {
|
||||
if (!processedReport) {
|
||||
onError();
|
||||
return;
|
||||
}
|
||||
|
||||
var allSitesUnordered = processedReport.reportData;
|
||||
model.totalVisits = processedReport.reportTotal.nb_visits;
|
||||
model.totalActions = processedReport.reportTotal.nb_actions;
|
||||
model.totalRevenue = processedReport.reportTotal.revenue;
|
||||
|
||||
allSitesByGroup = createGroupsAndMoveSitesIntoRelatedGroup(allSitesUnordered, processedReport.reportMetadata);
|
||||
|
||||
if (!allSitesByGroup.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.searchTerm) {
|
||||
model.searchSite(model.searchTerm);
|
||||
} else {
|
||||
model.sites = allSitesByGroup;
|
||||
}
|
||||
};
|
||||
|
||||
model.getNumberOfFilteredSites = function () {
|
||||
var numSites = model.sites.length;
|
||||
|
||||
var groupNames = {};
|
||||
|
||||
for (var index = 0; index < model.sites.length; index++) {
|
||||
var site = model.sites[index];
|
||||
if (site && site.isGroup) {
|
||||
numSites += site.sites.length;
|
||||
}
|
||||
}
|
||||
|
||||
return numSites;
|
||||
};
|
||||
|
||||
model.getNumberOfPages = function () {
|
||||
return Math.ceil(model.getNumberOfFilteredSites() / model.pageSize - 1);
|
||||
};
|
||||
|
||||
model.getCurrentPagingOffsetStart = function() {
|
||||
return Math.ceil(model.currentPage * model.pageSize);
|
||||
};
|
||||
|
||||
model.getCurrentPagingOffsetEnd = function() {
|
||||
var end = model.getCurrentPagingOffsetStart() + parseInt(model.pageSize, 10);
|
||||
var max = model.getNumberOfFilteredSites();
|
||||
if (end > max) {
|
||||
end = max;
|
||||
}
|
||||
return parseInt(end, 10);
|
||||
};
|
||||
|
||||
model.previousPage = function () {
|
||||
model.currentPage = model.currentPage - 1;
|
||||
};
|
||||
|
||||
model.nextPage = function () {
|
||||
model.currentPage = model.currentPage + 1;
|
||||
};
|
||||
|
||||
function nestedSearch(sitesByGroup, term)
|
||||
{
|
||||
var filteredSites = [];
|
||||
|
||||
for (var index in sitesByGroup) {
|
||||
var site = sitesByGroup[index];
|
||||
if (site.isGroup) {
|
||||
var matchingSites = nestedSearch(site.sites, term);
|
||||
if (matchingSites.length || (''+site.label).toLowerCase().indexOf(term) > -1) {
|
||||
var clonedGroup = copyGroup(site);
|
||||
clonedGroup.sites = matchingSites;
|
||||
filteredSites.push(clonedGroup);
|
||||
}
|
||||
} else if ((''+site.label).toLowerCase().indexOf(term) > -1) {
|
||||
filteredSites.push(site);
|
||||
} else if (site.group && (''+site.group).toLowerCase().indexOf(term) > -1) {
|
||||
filteredSites.push(site);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredSites;
|
||||
}
|
||||
|
||||
model.searchSite = function (term) {
|
||||
model.searchTerm = term;
|
||||
model.currentPage = 0;
|
||||
model.sites = nestedSearch(allSitesByGroup, term);
|
||||
};
|
||||
|
||||
function fetchPreviousSummary () {
|
||||
piwikApi.fetch({
|
||||
method: 'API.getLastDate'
|
||||
}).then(function (response) {
|
||||
if (response && response.value) {
|
||||
return response.value;
|
||||
}
|
||||
}).then(function (lastDate) {
|
||||
if (!lastDate) {
|
||||
return;
|
||||
}
|
||||
|
||||
model.lastVisitsDate = lastDate;
|
||||
|
||||
return piwikApi.fetch({
|
||||
method: 'API.getProcessedReport',
|
||||
apiModule: 'MultiSites',
|
||||
apiAction: 'getAll',
|
||||
hideMetricsDoc: '1',
|
||||
filter_limit: '0',
|
||||
showColumns: 'label,nb_visits',
|
||||
enhanced: 1,
|
||||
date: lastDate
|
||||
});
|
||||
}).then(function (response) {
|
||||
if (response && response.reportTotal) {
|
||||
model.lastVisits = response.reportTotal.nb_visits;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
model.fetchAllSites = function (refreshInterval) {
|
||||
|
||||
if (model.isLoading) {
|
||||
piwikApi.abort();
|
||||
}
|
||||
|
||||
model.isLoading = true;
|
||||
model.errorLoadingSites = false;
|
||||
|
||||
return piwikApi.fetch({
|
||||
method: 'API.getProcessedReport',
|
||||
apiModule: 'MultiSites',
|
||||
apiAction: 'getAll',
|
||||
hideMetricsDoc: '1',
|
||||
filter_limit: '-1',
|
||||
showColumns: 'label,nb_visits,nb_pageviews,visits_evolution,pageviews_evolution,revenue_evolution,nb_actions,revenue',
|
||||
enhanced: 1
|
||||
}).then(function (response) {
|
||||
model.updateWebsitesList(response);
|
||||
}, onError)['finally'](function () {
|
||||
model.isLoading = false;
|
||||
|
||||
if (refreshInterval && refreshInterval > 0) {
|
||||
$timeout(function () {
|
||||
model.fetchAllSites(refreshInterval);
|
||||
}, refreshInterval * 1000);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return model;
|
||||
});
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/**
|
||||
* Model for Multisites Dashboard aka All Websites Dashboard.
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').factory('multisitesDashboardModel', multisitesDashboardModel);
|
||||
|
||||
multisitesDashboardModel.$inject = ['piwikApi', '$filter', '$timeout'];
|
||||
|
||||
function multisitesDashboardModel(piwikApi, $filter, $timeout) {
|
||||
|
||||
var refreshPromise = null;
|
||||
|
||||
// those sites are going to be displayed
|
||||
var model = {
|
||||
sites : [],
|
||||
isLoading : false,
|
||||
pageSize : 25,
|
||||
currentPage : 0,
|
||||
totalVisits : '?',
|
||||
totalPageviews : '?',
|
||||
totalActions : '?',
|
||||
totalRevenue : '?',
|
||||
searchTerm : '',
|
||||
lastVisits : '?',
|
||||
lastVisitsDate : '?',
|
||||
numberOfSites : 0,
|
||||
updateWebsitesList: updateWebsitesList,
|
||||
getNumberOfFilteredSites: getNumberOfFilteredSites,
|
||||
getNumberOfPages: getNumberOfPages,
|
||||
getCurrentPagingOffsetStart: getCurrentPagingOffsetStart,
|
||||
getCurrentPagingOffsetEnd: getCurrentPagingOffsetEnd,
|
||||
previousPage: previousPage,
|
||||
nextPage: nextPage,
|
||||
searchSite: searchSite,
|
||||
sortBy: sortBy,
|
||||
reverse: true,
|
||||
sortColumn: 'nb_visits',
|
||||
fetchAllSites: fetchAllSites,
|
||||
refreshInterval: 0
|
||||
};
|
||||
|
||||
return model;
|
||||
|
||||
function cancelRefereshInterval()
|
||||
{
|
||||
if (refreshPromise) {
|
||||
$timeout.cancel(refreshPromise);
|
||||
refreshPromise = null;
|
||||
};
|
||||
}
|
||||
|
||||
function onError () {
|
||||
model.errorLoadingSites = true;
|
||||
model.sites = [];
|
||||
}
|
||||
|
||||
function updateWebsitesList(report) {
|
||||
if (!report) {
|
||||
onError();
|
||||
return;
|
||||
}
|
||||
|
||||
var allSites = report.sites;
|
||||
angular.forEach(allSites, function (site, index) {
|
||||
site.visits_evolution = parseInt(site.visits_evolution, 10);
|
||||
site.pageviews_evolution = parseInt(site.pageviews_evolution, 10);
|
||||
site.revenue_evolution = parseInt(site.revenue_evolution, 10);
|
||||
});
|
||||
|
||||
model.totalVisits = report.totals.nb_visits;
|
||||
model.totalPageviews = report.totals.nb_pageviews;
|
||||
model.totalActions = report.totals.nb_actions;
|
||||
model.totalRevenue = report.totals.revenue;
|
||||
model.lastVisits = report.totals.nb_visits_lastdate;
|
||||
model.sites = allSites;
|
||||
model.numberOfSites = report.numSites;
|
||||
model.lastVisitsDate = report.lastDate;
|
||||
}
|
||||
|
||||
function getNumberOfFilteredSites () {
|
||||
return model.numberOfSites;
|
||||
}
|
||||
|
||||
function getNumberOfPages() {
|
||||
return Math.ceil(getNumberOfFilteredSites() / model.pageSize - 1);
|
||||
}
|
||||
|
||||
function getCurrentPagingOffsetStart() {
|
||||
return Math.ceil(model.currentPage * model.pageSize);
|
||||
}
|
||||
|
||||
function getCurrentPagingOffsetEnd() {
|
||||
var end = getCurrentPagingOffsetStart() + parseInt(model.pageSize, 10);
|
||||
var max = getNumberOfFilteredSites();
|
||||
if (end > max) {
|
||||
end = max;
|
||||
}
|
||||
return parseInt(end, 10);
|
||||
}
|
||||
|
||||
function previousPage() {
|
||||
model.currentPage = model.currentPage - 1;
|
||||
fetchAllSites();
|
||||
}
|
||||
|
||||
function sortBy(metric) {
|
||||
if (model.sortColumn == metric) {
|
||||
model.reverse = !model.reverse;
|
||||
}
|
||||
|
||||
model.sortColumn = metric;
|
||||
fetchAllSites();
|
||||
};
|
||||
|
||||
function previousPage() {
|
||||
model.currentPage = model.currentPage - 1;
|
||||
fetchAllSites();
|
||||
}
|
||||
|
||||
function nextPage() {
|
||||
model.currentPage = model.currentPage + 1;
|
||||
fetchAllSites();
|
||||
}
|
||||
|
||||
function searchSite (term) {
|
||||
model.searchTerm = term;
|
||||
model.currentPage = 0;
|
||||
fetchAllSites();
|
||||
}
|
||||
|
||||
function fetchAllSites() {
|
||||
|
||||
if (model.isLoading) {
|
||||
piwikApi.abort();
|
||||
cancelRefereshInterval();
|
||||
}
|
||||
|
||||
model.isLoading = true;
|
||||
model.errorLoadingSites = false;
|
||||
|
||||
var params = {
|
||||
module: 'MultiSites',
|
||||
action: 'getAllWithGroups',
|
||||
hideMetricsDoc: '1',
|
||||
filter_sort_order: 'asc',
|
||||
filter_limit: model.pageSize,
|
||||
filter_offset: getCurrentPagingOffsetStart(),
|
||||
showColumns: 'label,nb_visits,nb_pageviews,visits_evolution,pageviews_evolution,revenue_evolution,nb_actions,revenue'
|
||||
};
|
||||
|
||||
if (model.searchTerm) {
|
||||
params.pattern = model.searchTerm;
|
||||
}
|
||||
|
||||
if (model.sortColumn) {
|
||||
params.filter_sort_column = model.sortColumn;
|
||||
}
|
||||
|
||||
if (model.reverse) {
|
||||
params.filter_sort_order = 'desc';
|
||||
}
|
||||
|
||||
return piwikApi.fetch(params).then(function (response) {
|
||||
updateWebsitesList(response);
|
||||
}, onError)['finally'](function () {
|
||||
model.isLoading = false;
|
||||
|
||||
if (model.refreshInterval && model.refreshInterval > 0) {
|
||||
cancelRefereshInterval();
|
||||
|
||||
refreshPromise = $timeout(function () {
|
||||
refreshPromise = null;
|
||||
fetchAllSites(model.refreshInterval);
|
||||
}, model.refreshInterval * 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
/*!
|
||||
* 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('MultiSitesDashboardController', MultiSitesDashboardController);
|
||||
|
||||
MultiSitesDashboardController.$inject = ['$scope', 'piwik', 'multisitesDashboardModel'];
|
||||
|
||||
function MultiSitesDashboardController($scope, piwik, multisitesDashboardModel){
|
||||
|
||||
$scope.model = multisitesDashboardModel;
|
||||
|
||||
$scope.evolutionSelector = 'visits_evolution';
|
||||
$scope.hasSuperUserAccess = piwik.hasSuperUserAccess;
|
||||
$scope.date = piwik.broadcast.getValueFromUrl('date');
|
||||
$scope.idSite = piwik.broadcast.getValueFromUrl('idSite');
|
||||
$scope.url = piwik.piwik_url;
|
||||
$scope.period = piwik.period;
|
||||
$scope.arePiwikProAdsEnabled = piwik.config && piwik.config.are_ads_enabled;
|
||||
|
||||
this.refresh = function (interval) {
|
||||
multisitesDashboardModel.refreshInterval = interval;
|
||||
multisitesDashboardModel.fetchAllSites();
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
|
@ -5,37 +5,37 @@
|
|||
{{ 'General_AllWebsitesDashboard'|translate }}
|
||||
<span class='smallTitle'
|
||||
title="{{ 'General_EvolutionSummaryGeneric'|translate:('General_NVisits'|translate:model.totalVisits):date:model.lastVisits:model.lastVisitsDate:(model.totalVisits|evolution:model.lastVisits)}}"
|
||||
ng-bind-html="'General_TotalVisitsPageviewsRevenue' | translate:('<strong>'+model.totalVisits+'</strong>'):('<strong>'+model.totalActions+'</strong>'):('<strong>' + model.totalRevenue + '</strong>')">
|
||||
ng-bind-html="'General_TotalVisitsPageviewsActionsRevenue' | translate:('<strong>'+model.totalVisits+'</strong>'):('<strong>'+model.totalPageviews+'</strong>'):('<strong>'+model.totalActions+'</strong>'):('<strong>' + model.totalRevenue + '</strong>')">
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
<table id="mt" class="dataTable" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th id="names" class="label" ng-click="sortBy('label')" ng-class="{columnSorted: 'label' == predicate}">
|
||||
<th id="names" class="label" ng-click="model.sortBy('label')" ng-class="{columnSorted: 'label' == model.sortColumn}">
|
||||
<span class="heading">{{ 'General_Website'|translate }}</span>
|
||||
<span ng-class="{multisites_asc: !reverse && 'label' == predicate, multisites_desc: reverse && 'label' == predicate}" class="arrow"></span>
|
||||
<span ng-class="{multisites_asc: !model.reverse && 'label' == model.sortColumn, multisites_desc: model.reverse && 'label' == model.sortColumn}" class="arrow"></span>
|
||||
</th>
|
||||
<th id="visits" class="multisites-column" ng-click="sortBy('nb_visits')" ng-class="{columnSorted: 'nb_visits' == predicate}">
|
||||
<th id="visits" class="multisites-column" ng-click="model.sortBy('nb_visits')" ng-class="{columnSorted: 'nb_visits' == model.sortColumn}">
|
||||
<span ng-class="{multisites_asc: !model.reverse && 'nb_visits' == model.sortColumn, multisites_desc: model.reverse && 'nb_visits' == model.sortColumn}" class="arrow"></span>
|
||||
<span class="heading">{{ 'General_ColumnNbVisits'|translate }}</span>
|
||||
<span ng-class="{multisites_asc: !reverse && 'nb_visits' == predicate, multisites_desc: reverse && 'nb_visits' == predicate}" class="arrow"></span>
|
||||
</th>
|
||||
<th id="pageviews" class="multisites-column" ng-click="sortBy('nb_pageviews')" ng-class="{columnSorted: 'nb_pageviews' == predicate}">
|
||||
<th id="pageviews" class="multisites-column" ng-click="model.sortBy('nb_pageviews')" ng-class="{columnSorted: 'nb_pageviews' == model.sortColumn}">
|
||||
<span ng-class="{multisites_asc: !model.reverse && 'nb_pageviews' == model.sortColumn, multisites_desc: model.reverse && 'nb_pageviews' == model.sortColumn}" class="arrow"></span>
|
||||
<span class="heading">{{ 'General_ColumnPageviews'|translate }}</span>
|
||||
<span ng-class="{multisites_asc: !reverse && 'nb_pageviews' == predicate, multisites_desc: reverse && 'nb_pageviews' == predicate}" class="arrow"></span>
|
||||
</th>
|
||||
|
||||
<th ng-if="displayRevenueColumn" id="revenue" class="multisites-column" ng-click="sortBy('revenue')" ng-class="{columnSorted: 'revenue' == predicate}">
|
||||
<th ng-if="displayRevenueColumn" id="revenue" class="multisites-column" ng-click="model.sortBy('revenue')" ng-class="{columnSorted: 'revenue' == model.sortColumn}">
|
||||
<span ng-class="{multisites_asc: !model.reverse && 'revenue' == model.sortColumn, multisites_desc: model.reverse && 'revenue' == model.sortColumn}" class="arrow"></span>
|
||||
<span class="heading">{{ 'General_ColumnRevenue'|translate }}</span>
|
||||
<span ng-class="{multisites_asc: !reverse && 'revenue' == predicate, multisites_desc: reverse && 'revenue' == predicate}" class="arrow"></span>
|
||||
</th>
|
||||
|
||||
<th id="evolution" colspan="{{ showSparklines ? 2 : 1 }}" ng-class="{columnSorted: evolutionSelector == predicate}">
|
||||
<span class="arrow" ng-class="{multisites_asc: !reverse && evolutionSelector == predicate, multisites_desc: reverse && evolutionSelector == predicate}"></span>
|
||||
<th id="evolution" colspan="{{ showSparklines ? 2 : 1 }}" ng-class="{columnSorted: evolutionSelector == model.sortColumn}">
|
||||
<span class="arrow" ng-class="{multisites_asc: !model.reverse && evolutionSelector == model.sortColumn, multisites_desc: model.reverse && evolutionSelector == model.sortColumn}"></span>
|
||||
<span class="evolution"
|
||||
ng-click="sortBy(evolutionSelector)"> {{ 'MultiSites_Evolution'|translate }}</span>
|
||||
<select class="selector" id="evolution_selector" ng-model="evolutionSelector"
|
||||
ng-change="predicate = evolutionSelector">
|
||||
ng-click="model.sortBy(evolutionSelector)"> {{ 'MultiSites_Evolution'|translate }}</span>
|
||||
<select class="selector native-select" id="evolution_selector" ng-model="evolutionSelector"
|
||||
ng-change="model.sortBy(evolutionSelector)">
|
||||
<option value="visits_evolution">{{ 'General_ColumnNbVisits'|translate }}</option>
|
||||
<option value="pageviews_evolution">{{ 'General_ColumnPageviews'|translate }}</option>
|
||||
<option ng-if="displayRevenueColumn" value="revenue_evolution">{{ 'General_ColumnRevenue'|translate }}</option>
|
||||
|
|
@ -58,7 +58,16 @@
|
|||
<tr ng-if="model.errorLoadingSites">
|
||||
<td colspan="7">
|
||||
<div class="notification system notification-error">
|
||||
{{ 'General_ErrorRequest'|translate }}
|
||||
{{ 'General_ErrorRequest'|translate:(''):('') }}
|
||||
|
||||
<br /><br />
|
||||
{{ 'General_NeedMoreHelp'|translate }}
|
||||
|
||||
<a rel="noreferrer" target="_blank" href="https://piwik.org/faq/troubleshooting/faq_19489/">{{ 'General_Faq'|translate }}</a>
|
||||
–
|
||||
<a rel="noreferrer" target="_blank" href="http://forum.piwik.org/">{{ 'Feedback_CommunityHelp'|translate }}</a>
|
||||
<span ng-show="arePiwikProAdsEnabled"> – </span>
|
||||
<a ng-show="arePiwikProAdsEnabled" rel="noreferrer" target="_blank" href="https://piwik.pro/?pk_campaign=Help&pk_medium=AjaxError&pk_content=MultiSites&pk_source=Piwik_App">{{ 'Feedback_ProfessionalHelp'|translate }}</a>.
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -68,10 +77,9 @@
|
|||
piwik-multisites-site
|
||||
date-sparkline="dateSparkline"
|
||||
show-sparklines="showSparklines"
|
||||
metric="predicate"
|
||||
ng-class-odd="'columnodd'"
|
||||
metric="model.sortColumn"
|
||||
display-revenue-column="displayRevenueColumn"
|
||||
ng-repeat="website in model.sites | orderBy:predicate:reverse | multiSitesGroupFilter:model.getCurrentPagingOffsetStart():model.pageSize">
|
||||
ng-repeat="website in model.sites">
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
|
|
@ -79,43 +87,25 @@
|
|||
|
||||
<tr ng-if="hasSuperUserAccess">
|
||||
<td colspan="8" class="add_new_site">
|
||||
<a href="{{ url }}?module=SitesManager&action=index&showaddsite=1&period={{ period }}&date={{ date }}">
|
||||
<img src='plugins/UsersManager/images/add.png' alt=""/> {{ 'SitesManager_AddSite'|translate }}
|
||||
<a href="{{ url }}?module=SitesManager&action=index&showaddsite=1&period={{ period }}&date={{ date }}&idSite={{ idSite }}">
|
||||
<img src='plugins/Morpheus/images/add.png' alt=""/> {{ 'SitesManager_AddSite'|translate }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr ng-if="!hasSuperUserAccess">
|
||||
<td colspan="8">
|
||||
<td colspan="8" class="empty-row">
|
||||
<br/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="8" class="site_search">
|
||||
<input type="text"
|
||||
ng-change="model.searchSite(searchTerm)"
|
||||
ng-model="searchTerm"
|
||||
placeholder="{{ 'Actions_SubmenuSitesearch' | translate }}">
|
||||
<img title="Search"
|
||||
ng-show="!searchTerm"
|
||||
class="search_ico"
|
||||
src="plugins/Zeitgeist/images/search_ico.png"/>
|
||||
<img title="Clear"
|
||||
ng-show="searchTerm"
|
||||
ng-click="searchTerm='';model.searchSite('')"
|
||||
class="reset"
|
||||
src="plugins/CoreHome/images/reset_search.png"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr row_id="last">
|
||||
<td colspan="8" class="paging" ng-hide="model.numberOfPages() <= 1">
|
||||
<span id="prev" class="previous" ng-hide="model.currentPage == 0" ng-click="model.previousPage()">
|
||||
<span style="cursor:pointer;">« {{ 'General_Previous'|translate }}</span>
|
||||
</span>
|
||||
<span class="dataTablePages">
|
||||
<span id="counter">
|
||||
{{ model.getCurrentPagingOffsetStart() }} - {{ model.getCurrentPagingOffsetEnd() }} of {{ model.getNumberOfFilteredSites() }}
|
||||
{{ 'General_Pagination'|translate:model.getCurrentPagingOffsetStart():model.getCurrentPagingOffsetEnd():model.getNumberOfFilteredSites() }}
|
||||
</span>
|
||||
</span>
|
||||
<span id="next" class="next" ng-hide="model.currentPage >= model.getNumberOfPages()" ng-click="model.nextPage()">
|
||||
|
|
@ -123,6 +113,19 @@
|
|||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr row_id="last">
|
||||
<td colspan="8" class="site_search">
|
||||
<input type="text"
|
||||
ng-model="searchTerm"
|
||||
piwik-onenter="model.searchSite(searchTerm)"
|
||||
placeholder="{{ 'Actions_SubmenuSitesearch' | translate }}">
|
||||
<img title="{{ 'General_ClickToSearch' | translate }}"
|
||||
ng-click="model.searchSite(searchTerm)"
|
||||
class="search_ico"
|
||||
src="plugins/Morpheus/images/search_ico.png"/>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*!
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders the multisites dashboard
|
||||
* Example usage:
|
||||
*
|
||||
* <div piwik-multisites-dashboard
|
||||
* display-revenue-column="true"
|
||||
* show-sparklines="true"
|
||||
* date-sparkline="true"
|
||||
* page-size="50"
|
||||
* auto-refresh-today-report="500" // or 0 to disable
|
||||
* ></div>
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').directive('piwikMultisitesDashboard', piwikMultisitesDashboard);
|
||||
|
||||
piwikMultisitesDashboard.$inject = ['piwik'];
|
||||
|
||||
function piwikMultisitesDashboard(piwik){
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
displayRevenueColumn: '@',
|
||||
showSparklines: '@',
|
||||
dateSparkline: '@'
|
||||
},
|
||||
templateUrl: 'plugins/MultiSites/angularjs/dashboard/dashboard.directive.html?cb=' + piwik.cacheBuster,
|
||||
controller: 'MultiSitesDashboardController',
|
||||
link: function (scope, element, attrs, controller) {
|
||||
|
||||
if (attrs.pageSize) {
|
||||
scope.model.pageSize = attrs.pageSize;
|
||||
}
|
||||
|
||||
controller.refresh(attrs.autoRefreshTodayReport);
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
|
@ -3,13 +3,27 @@
|
|||
font-size: 15px;
|
||||
}
|
||||
|
||||
.widget {
|
||||
#multisites {
|
||||
padding: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
#multisites {
|
||||
border: 0;
|
||||
padding: 0 15px;
|
||||
font-size: 14px;
|
||||
|
||||
h2 {
|
||||
border-bottom: 0px;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.notification-error {
|
||||
margin-top: 15px;
|
||||
a {
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
}
|
||||
|
||||
.add_new_site,
|
||||
|
|
@ -30,15 +44,32 @@
|
|||
border: 0 !important;
|
||||
}
|
||||
|
||||
.multisites-evolution,
|
||||
.multisites-column {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.multisites-evolution {
|
||||
width:170px;
|
||||
}
|
||||
|
||||
td, tr, .sparkline {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 1px;
|
||||
padding: 2px 6px 2px 12px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
padding: 12px 6px 12px 12px;
|
||||
}
|
||||
|
||||
td.empty-row {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.indicator {
|
||||
background: url(plugins/MultiSites/images/loading-blue.gif) no-repeat center;
|
||||
background: url(plugins/Morpheus/images/loading-blue.gif) no-repeat center;
|
||||
height: 20px;
|
||||
width: 60px;
|
||||
margin: auto;
|
||||
|
|
@ -46,7 +77,9 @@
|
|||
}
|
||||
|
||||
.paging {
|
||||
padding: 20px;
|
||||
padding: 5px;
|
||||
font-size: 10px;
|
||||
border-bottom: 0px !important;
|
||||
|
||||
.previous {
|
||||
padding-right: 20px;
|
||||
|
|
@ -56,23 +89,37 @@
|
|||
}
|
||||
}
|
||||
|
||||
.top_controls {
|
||||
height: 10px;
|
||||
th:first-child {
|
||||
text-align:left;
|
||||
}
|
||||
|
||||
th {
|
||||
cursor: pointer;
|
||||
border-left: 0px;
|
||||
text-align: right;
|
||||
border-bottom: 0px;
|
||||
|
||||
&#evolution {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.columnSorted {
|
||||
font-weight: normal !important;
|
||||
}
|
||||
}
|
||||
|
||||
.site_search input {
|
||||
margin-right: 0px;
|
||||
margin-left: 25px;
|
||||
padding-right: 25px;
|
||||
}
|
||||
|
||||
.search_ico {
|
||||
position: relative;
|
||||
left: -25px;
|
||||
margin-right: 0px;
|
||||
margin-top: -1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.reset {
|
||||
position: relative;
|
||||
|
|
@ -81,14 +128,6 @@
|
|||
margin-right: 0px;
|
||||
}
|
||||
|
||||
tr.columnodd:hover td, tr.columnodd td {
|
||||
background: #F2F2F2 !important;
|
||||
}
|
||||
|
||||
tr:hover td {
|
||||
background: #FFF !important;
|
||||
}
|
||||
|
||||
tr.group {
|
||||
font-weight: bold;
|
||||
height: 30px;
|
||||
|
|
@ -97,7 +136,6 @@
|
|||
padding-left: 50px;
|
||||
}
|
||||
td.multisites-label {
|
||||
padding-left: 15px;
|
||||
text-align: left;
|
||||
width: 250px;
|
||||
}
|
||||
|
|
@ -141,28 +179,68 @@
|
|||
}
|
||||
|
||||
.allWebsitesLoadingIndicator {
|
||||
background: url(plugins/Zeitgeist/images/loading-blue.gif) no-repeat right 3px;
|
||||
background: url(plugins/Morpheus/images/loading-blue.gif) no-repeat right 3px;
|
||||
display: inline-block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.heading {
|
||||
display: inline-block;
|
||||
display: inline;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
#evolution_selector {
|
||||
margin: -6px 0 0 5px;
|
||||
}
|
||||
|
||||
.label .arrow {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.multisites_asc,
|
||||
.multisites_desc {
|
||||
width: 16px;
|
||||
height: 13px;
|
||||
float: none;
|
||||
display: inline-block;
|
||||
background-image: url(plugins/Zeitgeist/images/sortdesc.png);
|
||||
vertical-align: top;
|
||||
margin: 0px;
|
||||
margin-left: 6px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
#evolution {
|
||||
.multisites_asc,
|
||||
.multisites_desc {
|
||||
margin-right: 6px;
|
||||
margin-left: 0px;
|
||||
}
|
||||
.evolution {
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.multisites_asc {
|
||||
width: 16px;
|
||||
height: 13px;
|
||||
display: inline-block;
|
||||
background-image: url(plugins/Zeitgeist/images/sortasc.png);
|
||||
margin-top: -7px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.multisites_desc:after {
|
||||
content: " \25BC";
|
||||
font-size: 1px;
|
||||
color: @theme-color-brand;
|
||||
border-left: 4px solid rgba(0, 0, 0, 0);
|
||||
border-right: 4px solid rgba(0, 0, 0, 0);
|
||||
border-top: 5px solid @theme-color-brand;
|
||||
}
|
||||
.multisites_asc:after {
|
||||
content: " \25BC";
|
||||
font-size: 1px;
|
||||
color: @theme-color-brand;
|
||||
border-left: 4px solid rgba(0, 0, 0, 0);
|
||||
border-right: 4px solid rgba(0, 0, 0, 0);
|
||||
border-bottom: 5px solid @theme-color-brand;
|
||||
}
|
||||
|
||||
div.sparkline {
|
||||
float:none;
|
||||
}
|
||||
|
|
@ -182,4 +260,4 @@
|
|||
|
||||
#mt thead *:last-child {
|
||||
border-top-right-radius: 7px;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders a single website row, for instance to be used within the MultiSites Dashboard.
|
||||
*
|
||||
* Usage:
|
||||
* <div piwik-multisites-site>
|
||||
* website="{label: 'Name', main_url: 'http://...', idsite: '...'}"
|
||||
* evolution-metric="visits_evolution"
|
||||
* show-sparklines="true"
|
||||
* date-sparkline="2014-01-01,2014-02-02"
|
||||
* display-revenue-column="true"
|
||||
* </div>
|
||||
*/
|
||||
angular.module('piwikApp').directive('piwikMultisitesSite', function($document, piwik, $filter){
|
||||
|
||||
return {
|
||||
restrict: 'AC',
|
||||
replace: true,
|
||||
scope: {
|
||||
website: '=',
|
||||
evolutionMetric: '=',
|
||||
showSparklines: '=',
|
||||
dateSparkline: '=',
|
||||
displayRevenueColumn: '=',
|
||||
metric: '='
|
||||
},
|
||||
templateUrl: 'plugins/MultiSites/angularjs/site/site.html?cb=' + piwik.cacheBuster,
|
||||
controller: function ($scope) {
|
||||
|
||||
$scope.period = piwik.period;
|
||||
$scope.date = piwik.broadcast.getValueFromUrl('date');
|
||||
$scope.parseInt = parseInt;
|
||||
|
||||
this.getWebsite = function () {
|
||||
return $scope.website;
|
||||
};
|
||||
|
||||
$scope.sparklineImage = function(website){
|
||||
var append = '';
|
||||
var token_auth = piwik.broadcast.getValueFromUrl('token_auth');
|
||||
if (token_auth.length) {
|
||||
append = '&token_auth=' + token_auth;
|
||||
}
|
||||
|
||||
return piwik.piwik_url + '?module=MultiSites&action=getEvolutionGraph&period=' + $scope.period + '&date=' + $scope.dateSparkline + '&evolutionBy=' +$scope.metric + '&columns=' + $scope.metric + '&idSite=' + website.idsite + '&idsite=' + website.idsite + '&viewDataTable=sparkline' + append + '&colors=' + encodeURIComponent(JSON.stringify(piwik.getSparklineColors()));
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
/*!
|
||||
* 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('MultiSitesSiteController', MultiSitesSiteController);
|
||||
|
||||
MultiSitesSiteController.$inject = ['$scope', 'piwik'];
|
||||
|
||||
function MultiSitesSiteController($scope, piwik){
|
||||
|
||||
$scope.period = piwik.period;
|
||||
$scope.date = piwik.broadcast.getValueFromUrl('date');
|
||||
$scope.sparklineImage = sparklineImage;
|
||||
$scope.website.label = piwik.helper.htmlDecode($scope.website.label);
|
||||
|
||||
this.getWebsite = function () {
|
||||
return $scope.website;
|
||||
};
|
||||
|
||||
function sparklineImage(website){
|
||||
var append = '';
|
||||
var token_auth = piwik.broadcast.getValueFromUrl('token_auth');
|
||||
if (token_auth.length) {
|
||||
append = '&token_auth=' + token_auth;
|
||||
}
|
||||
|
||||
var metric = $scope.metric;
|
||||
|
||||
switch ($scope.evolutionMetric) {
|
||||
case 'visits_evolution':
|
||||
metric = 'nb_visits';
|
||||
break;
|
||||
case 'pageviews_evolution':
|
||||
metric = 'nb_pageviews';
|
||||
break;
|
||||
case 'revenue_evolution':
|
||||
metric = 'revenue';
|
||||
break;
|
||||
}
|
||||
|
||||
return piwik.piwik_url + '?module=MultiSites&action=getEvolutionGraph&period=' + $scope.period + '&date=' + $scope.dateSparkline + '&evolutionBy=' + metric + '&columns=' + metric + '&idSite=' + website.idsite + '&idsite=' + website.idsite + '&viewDataTable=sparkline' + append + '&colors=' + encodeURIComponent(JSON.stringify(piwik.getSparklineColors()));
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
<a title="View reports" ng-href="index.php?module=CoreHome&action=index&date={{ date }}&period={{ period }}&idSite={{ website.idsite }}">{{ website.label }}</a>
|
||||
|
||||
<span style="width: 10px; margin-left:3px;">
|
||||
<a target="_blank" title="{{ 'General_GoTo'|translate:website.main_url }}" ng-href="{{ website.main_url }}">
|
||||
<img src="plugins/MultiSites/images/link.gif"/></a>
|
||||
<a rel="noreferrer" target="_blank" title="{{ 'General_GoTo'|translate:website.main_url }}" ng-href="{{ website.main_url }}">
|
||||
<img src="plugins/Morpheus/images/link.gif"/></a>
|
||||
</span>
|
||||
</td>
|
||||
<td ng-if="website.isGroup" class="multisites-label label">
|
||||
|
|
@ -20,20 +20,20 @@
|
|||
{{ website.revenue }}
|
||||
</td>
|
||||
|
||||
<td ng-if="period != 'range'" style="width:170px;">
|
||||
<td ng-if="period != 'range'" class="multisites-evolution">
|
||||
<div class="visits" ng-if="!website.isGroup">
|
||||
<span ng-show="parseInt(website[evolutionMetric]) > 0"><img src="plugins/MultiSites/images/arrow_up.png" alt="" /> <span style="color: green;">{{ website[evolutionMetric] }} </span></span>
|
||||
<span ng-show="parseInt(website[evolutionMetric]) == 0"><img src="plugins/MultiSites/images/stop.png" alt="" /> <span>{{ website[evolutionMetric] }}</span></span>
|
||||
<span ng-show="parseInt(website[evolutionMetric]) < 0"><img src="plugins/MultiSites/images/arrow_down.png" alt="" /> <span style="color: red;">{{ website[evolutionMetric] }} </span></span>
|
||||
<span ng-show="website[evolutionMetric] > 0"><img src="plugins/MultiSites/images/arrow_up.png" alt="" /> <span style="color: green;">{{ website[evolutionMetric] }}%</span></span>
|
||||
<span ng-show="website[evolutionMetric] == 0"><img src="plugins/MultiSites/images/stop.png" alt="" /> <span>{{ website[evolutionMetric] }}%</span></span>
|
||||
<span ng-show="website[evolutionMetric] < 0"><img src="plugins/MultiSites/images/arrow_down.png" alt="" /> <span style="color: red;">{{ website[evolutionMetric] }}%</span></span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td ng-if="showSparklines" style="width:180px;">
|
||||
<div ng-if="!website.isGroup" class="sparkline" style="width: 100px; margin: auto;">
|
||||
<a target="_blank" ng-href="index.php?module=CoreHome&action=index&date={{ date }}&period={{ period }}&idSite={{ website.idsite }}"
|
||||
<a rel="noreferrer" target="_blank" ng-href="index.php?module=CoreHome&action=index&date={{ date }}&period={{ period }}&idSite={{ website.idsite }}"
|
||||
title="{{ 'General_GoTo'|translate:('Dashboard_DashboardOf'|translate:website.label) }}">
|
||||
<img alt="" ng-src="{{ sparklineImage(website) }}" width="100" height="25" />
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tr>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*!
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Renders a single website row, for instance to be used within the MultiSites Dashboard.
|
||||
*
|
||||
* Usage:
|
||||
* <div piwik-multisites-site>
|
||||
* website="{label: 'Name', main_url: 'http://...', idsite: '...'}"
|
||||
* evolution-metric="visits_evolution"
|
||||
* show-sparklines="true"
|
||||
* date-sparkline="2014-01-01,2014-02-02"
|
||||
* display-revenue-column="true"
|
||||
* </div>
|
||||
*/
|
||||
(function () {
|
||||
angular.module('piwikApp').directive('piwikMultisitesSite', piwikMultisitesSite);
|
||||
|
||||
piwikMultisitesSite.$inject = ['piwik'];
|
||||
|
||||
function piwikMultisitesSite(piwik){
|
||||
|
||||
return {
|
||||
restrict: 'AC',
|
||||
replace: true,
|
||||
scope: {
|
||||
website: '=',
|
||||
evolutionMetric: '=',
|
||||
showSparklines: '=',
|
||||
dateSparkline: '=',
|
||||
displayRevenueColumn: '=',
|
||||
metric: '='
|
||||
},
|
||||
templateUrl: 'plugins/MultiSites/angularjs/site/site.directive.html?cb=' + piwik.cacheBuster,
|
||||
controller: 'MultiSitesSiteController'
|
||||
};
|
||||
}
|
||||
})();
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 75 B |
Binary file not shown.
|
Before Width: | Height: | Size: 1.8 KiB |
5
www/analytics/plugins/MultiSites/lang/ar.json
Normal file
5
www/analytics/plugins/MultiSites/lang/ar.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "التطور"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/be.json
Normal file
5
www/analytics/plugins/MultiSites/lang/be.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Змены"
|
||||
}
|
||||
}
|
||||
8
www/analytics/plugins/MultiSites/lang/bg.json
Normal file
8
www/analytics/plugins/MultiSites/lang/bg.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Развитие",
|
||||
"LoadingWebsites": "Зарежда сайтовете",
|
||||
"TopLinkTooltip": "Сравнете статистиката за всички ваши уебсайтове.",
|
||||
"Pagination": "%1$s - %2$s като %3$s"
|
||||
}
|
||||
}
|
||||
7
www/analytics/plugins/MultiSites/lang/ca.json
Normal file
7
www/analytics/plugins/MultiSites/lang/ca.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolució",
|
||||
"TopLinkTooltip": "Compareu les estadístiques anàlitiques de tots els vostres llocs web.",
|
||||
"Pagination": "%1$s - %2$s de %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/cs.json
Normal file
9
www/analytics/plugins/MultiSites/lang/cs.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Vývoj",
|
||||
"LoadingWebsites": "Načítání webových stránek",
|
||||
"PluginDescription": "Zobrazte a porovnejte všechny vaše stránky a aplikace na této užitečné nástěnce všech stránek.",
|
||||
"TopLinkTooltip": "Porovnejte webové analytické statistiky pro všechny vaše webové stránky.",
|
||||
"Pagination": "%1$s - %2$s z %3$s"
|
||||
}
|
||||
}
|
||||
8
www/analytics/plugins/MultiSites/lang/da.json
Normal file
8
www/analytics/plugins/MultiSites/lang/da.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Udvikling",
|
||||
"LoadingWebsites": "Loading websites",
|
||||
"TopLinkTooltip": "Sammenlign analyse statistik for alle hjemmesider.",
|
||||
"Pagination": "%1$s - %2$s af %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/de.json
Normal file
9
www/analytics/plugins/MultiSites/lang/de.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Entwicklung",
|
||||
"LoadingWebsites": "Lade Websites",
|
||||
"PluginDescription": "Zeige und vergleiche alle Websites und Apps im hilfreichen 'Alle Websites' Dashboard.",
|
||||
"TopLinkTooltip": "Vergleichen Sie Webanalytikstatistiken für alle Ihre Websites.",
|
||||
"Pagination": "%1$s - %2$s von %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/el.json
Normal file
9
www/analytics/plugins/MultiSites/lang/el.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Πρόοδος",
|
||||
"LoadingWebsites": "Γίνεται φόρτωση των ιστοτόπων",
|
||||
"PluginDescription": "Δείτε και συγκρίνετε όλους τους ιστοτόπους σας και τις εφαρμογές σας σε αυτό τον χρήσιμο πίνακα 'Όλοι οι Ιστοτόποι'.",
|
||||
"TopLinkTooltip": "Σύγκριση στατιστικών Ιστού για όλες τις ιστοσελίδες σας.",
|
||||
"Pagination": "%1$s - %2$s από %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/en.json
Normal file
9
www/analytics/plugins/MultiSites/lang/en.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolution",
|
||||
"LoadingWebsites": "Loading websites",
|
||||
"PluginDescription": "View and compare all your websites and apps in this useful 'All Websites' dashboard. ",
|
||||
"TopLinkTooltip": "Compare Web Analytics stats for all of your Websites.",
|
||||
"Pagination": "%1$s - %2$s of %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/es.json
Normal file
9
www/analytics/plugins/MultiSites/lang/es.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolución",
|
||||
"LoadingWebsites": "Cargando sitios de internet",
|
||||
"PluginDescription": "Vea y compare todos sus sitios de internet y aplicaciones en este tablero 'Todos los sitios de internet'",
|
||||
"TopLinkTooltip": "Comparar la estadísticas de Análisis de internet para todos sus sitios.",
|
||||
"Pagination": "%1$s - %2$s de %3$s"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/et.json
Normal file
5
www/analytics/plugins/MultiSites/lang/et.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Muutus ajas"
|
||||
}
|
||||
}
|
||||
7
www/analytics/plugins/MultiSites/lang/fa.json
Normal file
7
www/analytics/plugins/MultiSites/lang/fa.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "تکامل",
|
||||
"LoadingWebsites": "درحال بارگزاری وب سایت ها",
|
||||
"TopLinkTooltip": "اطلاعات آماری همه وب سایت هایتان را مقایسه کنید."
|
||||
}
|
||||
}
|
||||
8
www/analytics/plugins/MultiSites/lang/fi.json
Normal file
8
www/analytics/plugins/MultiSites/lang/fi.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Kehitys",
|
||||
"LoadingWebsites": "Lataa verkkosivuja",
|
||||
"TopLinkTooltip": "Vertaile eri sivujen tilastoja.",
|
||||
"Pagination": "%1$s - %2$s %3$s:sta."
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/fr.json
Normal file
9
www/analytics/plugins/MultiSites/lang/fr.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Évolution",
|
||||
"LoadingWebsites": "Chargement des sites",
|
||||
"PluginDescription": "Visualisez et comparez tous vos sites web et application en utilisant le tableau de bord \"tous les sites web\".",
|
||||
"TopLinkTooltip": "Comparez les statistiques pour tous vos sites web.",
|
||||
"Pagination": "%1$s - %2$s de %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/hi.json
Normal file
9
www/analytics/plugins/MultiSites/lang/hi.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "विकास",
|
||||
"LoadingWebsites": "लोड हो रहा है वेबसाइट",
|
||||
"PluginDescription": "देखें और इस उपयोगी 'सभी वेबसाइटों के' डैशबोर्ड में अपने सभी वेबसाइटों और क्षुधा की तुलना करें।",
|
||||
"TopLinkTooltip": "आपकी वेबसाइट सभी के लिए वेब विश्लेषिकी आँकड़ों की तुलना करें.",
|
||||
"Pagination": "%1$s - %2$s के लिए %3$s"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/hu.json
Normal file
5
www/analytics/plugins/MultiSites/lang/hu.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Trendek"
|
||||
}
|
||||
}
|
||||
6
www/analytics/plugins/MultiSites/lang/id.json
Normal file
6
www/analytics/plugins/MultiSites/lang/id.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Perkembangan",
|
||||
"TopLinkTooltip": "Bandingkan statistik Analisis Ramatraya untuk seluruh Situs Anda."
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/it.json
Normal file
9
www/analytics/plugins/MultiSites/lang/it.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evoluzione",
|
||||
"LoadingWebsites": "Caricamento siti web",
|
||||
"PluginDescription": "Guarda e confronta tutti i siti e le app in questa utile dashboard 'Tutti i Siti'.",
|
||||
"TopLinkTooltip": "Compara le statistiche web di tutti i tuoi siti web.",
|
||||
"Pagination": "%1$s - %2$s di %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/ja.json
Normal file
9
www/analytics/plugins/MultiSites/lang/ja.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "推移",
|
||||
"LoadingWebsites": "ウェブサイトをロード中",
|
||||
"PluginDescription": "この便利な「すべてのウェブサイト」ダッシュボードで、すべてのウェブサイトとアプリケーションを見て、比較してください。",
|
||||
"TopLinkTooltip": "あなたの全ウェブサイトに対するウェブ解析統計情報を比較してください。",
|
||||
"Pagination": "%1$s の %2$s - %3$s"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/ka.json
Normal file
5
www/analytics/plugins/MultiSites/lang/ka.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "ევოლუცია"
|
||||
}
|
||||
}
|
||||
7
www/analytics/plugins/MultiSites/lang/ko.json
Normal file
7
www/analytics/plugins/MultiSites/lang/ko.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "변화 추이",
|
||||
"TopLinkTooltip": "웹사이트의 모든 웹분석 통계를 비교합니다.",
|
||||
"Pagination": "%1$s - %2$s 의 %3$s"
|
||||
}
|
||||
}
|
||||
7
www/analytics/plugins/MultiSites/lang/lt.json
Normal file
7
www/analytics/plugins/MultiSites/lang/lt.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Raida",
|
||||
"LoadingWebsites": "Įkeliamos svetainės",
|
||||
"Pagination": "%1$s - %2$s iš %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/nb.json
Normal file
9
www/analytics/plugins/MultiSites/lang/nb.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Utvikling",
|
||||
"LoadingWebsites": "Laster nettsteder",
|
||||
"PluginDescription": "Se og sammenligne alle dine nettsteder og apper i denne nyttige «Alle nettsteder»-oversikten.",
|
||||
"TopLinkTooltip": "Sammenligne web-statistikk for alle dine nettsteder.",
|
||||
"Pagination": "%1$s – %2$s av %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/nl.json
Normal file
9
www/analytics/plugins/MultiSites/lang/nl.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolutie",
|
||||
"LoadingWebsites": "Websites laden",
|
||||
"PluginDescription": "Bekijk en vergelijk al uw websites en apps in dit handige 'Alle Websites' paneel.",
|
||||
"TopLinkTooltip": "Vergelijk Website statistieken van al je websites.",
|
||||
"Pagination": "%1$s - %2$s van %3$s"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/nn.json
Normal file
5
www/analytics/plugins/MultiSites/lang/nn.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolusjon"
|
||||
}
|
||||
}
|
||||
7
www/analytics/plugins/MultiSites/lang/pl.json
Normal file
7
www/analytics/plugins/MultiSites/lang/pl.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Postęp",
|
||||
"LoadingWebsites": "Ładowanie stron",
|
||||
"Pagination": "%1$s - %2$s z %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/pt-br.json
Normal file
9
www/analytics/plugins/MultiSites/lang/pt-br.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolução",
|
||||
"LoadingWebsites": "Carregando sites",
|
||||
"PluginDescription": "Veja e compare todos os seus sites e aplicativos neste útil painel 'Todos Websites'.",
|
||||
"TopLinkTooltip": "Comparar estatísticas de todos os websites.",
|
||||
"Pagination": "%1$s - %2$s de %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/pt.json
Normal file
9
www/analytics/plugins/MultiSites/lang/pt.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolução",
|
||||
"LoadingWebsites": "A carregar websites",
|
||||
"PluginDescription": "Veja e compare todos os seus websites e aplicações no painel \"Todos os Websites\".",
|
||||
"TopLinkTooltip": "Compare as estatísticas de todos os seus websites.",
|
||||
"Pagination": "%1$s - %2$s de %3$s"
|
||||
}
|
||||
}
|
||||
8
www/analytics/plugins/MultiSites/lang/ro.json
Normal file
8
www/analytics/plugins/MultiSites/lang/ro.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evoluţie",
|
||||
"LoadingWebsites": "încărcare site-uri web",
|
||||
"TopLinkTooltip": "Compara statisticiLE Web Analytics pentru toate site-urile dvs..",
|
||||
"Pagination": "%1$s - %2$s of %3$s"
|
||||
}
|
||||
}
|
||||
8
www/analytics/plugins/MultiSites/lang/ru.json
Normal file
8
www/analytics/plugins/MultiSites/lang/ru.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Эволюция",
|
||||
"LoadingWebsites": "Загрузка сайтов",
|
||||
"TopLinkTooltip": "Сравните веб-аналитику для всех ваших сайтов.",
|
||||
"Pagination": "%1$s - %2$s из %3$s"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/sk.json
Normal file
5
www/analytics/plugins/MultiSites/lang/sk.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Vývoj"
|
||||
}
|
||||
}
|
||||
7
www/analytics/plugins/MultiSites/lang/sl.json
Normal file
7
www/analytics/plugins/MultiSites/lang/sl.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolucija",
|
||||
"LoadingWebsites": "Nalaganje strani",
|
||||
"Pagination": "%1$s - %2$s od %3$s"
|
||||
}
|
||||
}
|
||||
6
www/analytics/plugins/MultiSites/lang/sq.json
Normal file
6
www/analytics/plugins/MultiSites/lang/sq.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Evolution",
|
||||
"Pagination": "%1$s - %2$s of %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/sr.json
Normal file
9
www/analytics/plugins/MultiSites/lang/sr.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Razvoj",
|
||||
"LoadingWebsites": "Učitavanje sajtova",
|
||||
"PluginDescription": "Pregledajte i uporedite sve vaše sajtove i aplikacije preko ovog korisnog 'Svi sajtovi' dodatka.",
|
||||
"TopLinkTooltip": "Uporedi statistiku za sve vaše sajtove",
|
||||
"Pagination": "%1$s - %2$s od %3$s"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/sv.json
Normal file
9
www/analytics/plugins/MultiSites/lang/sv.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Utveckling",
|
||||
"LoadingWebsites": "Laddar webbplatser",
|
||||
"PluginDescription": "Visa och jämför alla dina webbplatser i panelen 'All Websites'.",
|
||||
"TopLinkTooltip": "Jämför statistik för alla dina webbplatser.",
|
||||
"Pagination": "%1$s - %2$s av %3$s"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/ta.json
Normal file
5
www/analytics/plugins/MultiSites/lang/ta.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "பரிணாமம்"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/th.json
Normal file
5
www/analytics/plugins/MultiSites/lang/th.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "ความเปลี่ยนแปลง"
|
||||
}
|
||||
}
|
||||
8
www/analytics/plugins/MultiSites/lang/tl.json
Normal file
8
www/analytics/plugins/MultiSites/lang/tl.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Ebolusyon",
|
||||
"LoadingWebsites": "Naglo-load ang mga website",
|
||||
"TopLinkTooltip": "Ikumpara ang stats ng Web Analytics para sa lahat ng iyong Website.",
|
||||
"Pagination": "%1$s - %2$s ng %3$s"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/uk.json
Normal file
5
www/analytics/plugins/MultiSites/lang/uk.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Зміна"
|
||||
}
|
||||
}
|
||||
9
www/analytics/plugins/MultiSites/lang/vi.json
Normal file
9
www/analytics/plugins/MultiSites/lang/vi.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "Sự tiến triển",
|
||||
"LoadingWebsites": "Đang tải website",
|
||||
"PluginDescription": "Xem và so sánh toàn bộ các website bạn quản lý",
|
||||
"TopLinkTooltip": "So sánh các số liệu thống kê Web Analytics cho tất cả các trang web của bạn.",
|
||||
"Pagination": "%1$s - %2$s trên %3$s"
|
||||
}
|
||||
}
|
||||
6
www/analytics/plugins/MultiSites/lang/zh-cn.json
Normal file
6
www/analytics/plugins/MultiSites/lang/zh-cn.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "趋势",
|
||||
"TopLinkTooltip": "比较所有网站的分析数据"
|
||||
}
|
||||
}
|
||||
5
www/analytics/plugins/MultiSites/lang/zh-tw.json
Normal file
5
www/analytics/plugins/MultiSites/lang/zh-tw.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"MultiSites": {
|
||||
"Evolution": "發展趨勢"
|
||||
}
|
||||
}
|
||||
8
www/analytics/plugins/MultiSites/plugin.json
Normal file
8
www/analytics/plugins/MultiSites/plugin.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"authors": [
|
||||
{
|
||||
"name": "Piwik PRO",
|
||||
"homepage": "http://piwik.pro"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,17 +1,19 @@
|
|||
{% extends isWidgetized ? 'empty.twig' : 'dashboard.twig' %}
|
||||
|
||||
{% block content %}
|
||||
{% if not isWidgetized %}
|
||||
<div class="top_controls">
|
||||
{% include "@CoreHome/_periodSelect.twig" %}
|
||||
{% include "@CoreHome/_headerMessage.twig" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% block topcontrols %}
|
||||
{% if not isWidgetized %}
|
||||
<div class="top_controls">
|
||||
{% include "@CoreHome/_periodSelect.twig" %}
|
||||
{% include "@CoreHome/_headerMessage.twig" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container" id="multisites">
|
||||
|
||||
<div class="pageWrap" id="multisites">
|
||||
<div id="main">
|
||||
<div piwik-multisites-dashboard
|
||||
class="centerLargeDiv"
|
||||
display-revenue-column="{% if displayRevenueColumn %}true{% else %}false{%endif%}"
|
||||
page-size="{{ limit }}"
|
||||
show-sparklines="{% if show_sparklines %}true{% else %}false{%endif%}"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue