update Piwik to version 2.16 (fixes #91)

This commit is contained in:
oliver 2016-04-10 18:55:57 +02:00
commit d885a4baa9
5833 changed files with 418860 additions and 226988 deletions

View file

@ -1,33 +1,8 @@
<Files ~ "\.(php|php4|php5|inc|tpl|in|twig)$">
<IfModule mod_access.c>
Deny from all
Require all denied
</IfModule>
<IfModule !mod_access_compat>
<IfModule mod_authz_host.c>
Deny from all
Require all denied
</IfModule>
</IfModule>
<IfModule mod_access_compat>
Deny from all
Require all denied
</IfModule>
</Files>
<Files ~ "\.(test\.php|gif|ico|jpg|png|svg|js|css|swf)$">
<IfModule mod_access.c>
Allow from all
Require all granted
</IfModule>
<IfModule !mod_access_compat>
<IfModule mod_authz_host.c>
Allow from all
Require all granted
</IfModule>
</IfModule>
<IfModule mod_access_compat>
Allow from all
Require all granted
</IfModule>
Satisfy any
<Files "*">
<IfVersion < 2.4>
Deny from all
</IfVersion>
<IfVersion >= 2.4>
Require all denied
</IfVersion>
</Files>

View file

@ -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
@ -10,24 +10,29 @@ namespace Piwik\Plugins\API;
use Piwik\API\Proxy;
use Piwik\API\Request;
use Piwik\Columns\Dimension;
use Piwik\Common;
use Piwik\Config;
use Piwik\Container\StaticContainer;
use Piwik\DataTable;
use Piwik\DataTable\Filter\ColumnDelete;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\Date;
use Piwik\Menu\MenuTop;
use Piwik\IP;
use Piwik\Metrics;
use Piwik\Period;
use Piwik\Period\Range;
use Piwik\Piwik;
use Piwik\Plugins\API\DataTable\MergeDataTables;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
use Piwik\Tracker\GoalManager;
use Piwik\Translate;
use Piwik\Translation\Translator;
use Piwik\Measurable\Type\TypeManager;
use Piwik\Version;
require_once PIWIK_INCLUDE_PATH . '/core/Config.php';
/**
* This API is the <a href='http://piwik.org/docs/analytics-api/metadata/' target='_blank'>Metadata API</a>: it gives information about all other available APIs methods, as well as providing
* This API is the <a href='http://piwik.org/docs/analytics-api/metadata/' rel='noreferrer' target='_blank'>Metadata API</a>: it gives information about all other available APIs methods, as well as providing
* human readable and more complete outputs than normal API methods.
*
* Some of the information that is returned by the Metadata API:
@ -40,7 +45,7 @@ require_once PIWIK_INCLUDE_PATH . '/core/Config.php';
* <li>the method "getSuggestedValuesForSegment" returns top suggested values for a particular segment. It uses the Live.getLastVisitsDetails API to fetch the most recently used values, and will return the most often used values first.</li>
* </ul>
* The Metadata API is for example used by the Piwik Mobile App to automatically display all Piwik reports, with translated report & columns names and nicely formatted values.
* More information on the <a href='http://piwik.org/docs/analytics-api/metadata/' target='_blank'>Metadata API documentation page</a>
* More information on the <a href='http://piwik.org/docs/analytics-api/metadata/' rel='noreferrer' target='_blank'>Metadata API documentation page</a>
*
* @method static \Piwik\Plugins\API\API getInstance()
*/
@ -56,6 +61,18 @@ class API extends \Piwik\Plugin\API
return Version::VERSION;
}
/**
* Returns the most accurate IP address availble for the current user, in
* IPv4 format. This could be the proxy client's IP address.
*
* @return string IP address in presentation format.
*/
public function getIpFromHeader()
{
Piwik::checkUserHasSomeViewAccess();
return IP::getIpFromHeader();
}
/**
* Returns the section [APISettings] if defined in config.ini.php
* @return array
@ -71,23 +88,67 @@ class API extends \Piwik\Plugin\API
* are not visible in the UI and not present in the API meta data. These columns are
* translated here.
* @return array
* @deprecated since Piwik 2.15.1
*/
static public function getDefaultMetricTranslations()
public function getDefaultMetricTranslations()
{
return Metrics::getDefaultMetricTranslations();
}
/**
* Returns all available measurable types.
* Marked as deprecated so it won't appear in API page. It won't be a public API for now.
* @deprecated
* @return array
*/
public function getAvailableMeasurableTypes()
{
Piwik::checkUserHasSomeViewAccess();
$typeManager = new TypeManager();
$types = $typeManager->getAllTypes();
$available = array();
foreach ($types as $type) {
$available[] = array(
'id' => $type->getId(),
'name' => Piwik::translate($type->getName()),
'description' => Piwik::translate($type->getDescription()),
'howToSetupUrl' => $type->getHowToSetupUrl()
);
}
return $available;
}
public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true)
{
if (empty($idSites)) {
Piwik::checkUserHasSomeViewAccess();
} else {
Piwik::checkUserHasViewAccess($idSites);
}
$isNotAnonymous = !Piwik::isUserIsAnonymous();
$segments = array();
foreach (Dimension::getAllDimensions() as $dimension) {
foreach ($dimension->getSegments() as $segment) {
if ($segment->isRequiresAtLeastViewAccess()) {
$segment->setPermission($isNotAnonymous);
}
$segments[] = $segment->toArray();
}
}
/**
* Triggered when gathering all available segment dimensions.
*
*
* This event can be used to make new segment dimensions available.
*
*
* **Example**
*
*
* public function getSegmentsMetadata(&$segments, $idSites)
* {
* $segments[] = array(
@ -101,11 +162,11 @@ class API extends \Piwik\Plugin\API
* 'permission' => $isAuthenticatedWithViewAccess,
* );
* }
*
*
* @param array &$dimensions The list of available segment dimensions. Append to this list to add
* new segments. Each element in this list must contain the
* following information:
*
*
* - **type**: Either `'metric'` or `'dimension'`. `'metric'` means
* the value is a numeric and `'dimension'` means it is
* a string. Also, `'metric'` values will be displayed
@ -129,121 +190,6 @@ class API extends \Piwik\Plugin\API
*/
Piwik::postEvent('API.getSegmentDimensionMetadata', array(&$segments, $idSites));
$isAuthenticatedWithViewAccess = Piwik::isUserHasViewAccess($idSites) && !Piwik::isUserIsAnonymous();
$segments[] = array(
'type' => 'dimension',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_VisitorID',
'segment' => 'visitorId',
'acceptedValues' => '34c31e04394bdc63 - any 16 Hexadecimal chars ID, which can be fetched using the Tracking API function getVisitorId()',
'sqlSegment' => 'log_visit.idvisitor',
'sqlFilterValue' => array('Piwik\Common', 'convertVisitorIdToBin'),
'permission' => $isAuthenticatedWithViewAccess,
);
$segments[] = array(
'type' => 'dimension',
'category' => Piwik::translate('General_Visit'),
'name' => Piwik::translate('General_Visit') . " ID",
'segment' => 'visitId',
'acceptedValues' => 'Any integer. ',
'sqlSegment' => 'log_visit.idvisit',
'permission' => $isAuthenticatedWithViewAccess,
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_VisitorIP',
'segment' => 'visitIp',
'acceptedValues' => '13.54.122.1. </code>Select IP ranges with notation: <code>visitIp>13.54.122.0;visitIp<13.54.122.255',
'sqlSegment' => 'log_visit.location_ip',
'sqlFilterValue' => array('Piwik\IP', 'P2N'),
'permission' => $isAuthenticatedWithViewAccess,
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_NbActions',
'segment' => 'actions',
'sqlSegment' => 'log_visit.visit_total_actions',
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_NbSearches',
'segment' => 'searches',
'sqlSegment' => 'log_visit.visit_total_searches',
'acceptedValues' => 'To select all visits who used internal Site Search, use: &segment=searches>0',
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_ColumnVisitDuration',
'segment' => 'visitDuration',
'sqlSegment' => 'log_visit.visit_total_time',
);
$segments[] = array(
'type' => 'dimension',
'category' => Piwik::translate('General_Visit'),
'name' => Piwik::translate('General_VisitType'),
'segment' => 'visitorType',
'acceptedValues' => 'new, returning, returningCustomer' . ". " . Piwik::translate('General_VisitTypeExample', '"&segment=visitorType==returning,visitorType==returningCustomer"'),
'sqlSegment' => 'log_visit.visitor_returning',
'sqlFilterValue' => function ($type) {
return $type == "new" ? 0 : ($type == "returning" ? 1 : 2);
}
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_DaysSinceLastVisit',
'segment' => 'daysSinceLastVisit',
'sqlSegment' => 'log_visit.visitor_days_since_last',
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_DaysSinceFirstVisit',
'segment' => 'daysSinceFirstVisit',
'sqlSegment' => 'log_visit.visitor_days_since_first',
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_NumberOfVisits',
'segment' => 'visitCount',
'sqlSegment' => 'log_visit.visitor_count_visits',
);
$segments[] = array(
'type' => 'dimension',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_VisitConvertedGoal',
'segment' => 'visitConverted',
'acceptedValues' => '0, 1',
'sqlSegment' => 'log_visit.visit_goal_converted',
);
$segments[] = array(
'type' => 'dimension',
'category' => Piwik::translate('General_Visit'),
'name' => Piwik::translate('General_EcommerceVisitStatusDesc'),
'segment' => 'visitEcommerceStatus',
'acceptedValues' => implode(", ", self::$visitEcommerceStatus)
. '. ' . Piwik::translate('General_EcommerceVisitStatusEg', '"&segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart"'),
'sqlSegment' => 'log_visit.visit_goal_buyer',
'sqlFilterValue' => __NAMESPACE__ . '\API::getVisitEcommerceStatus',
);
$segments[] = array(
'type' => 'metric',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_DaysSinceLastEcommerceOrder',
'segment' => 'daysSinceLastEcommerceOrder',
'sqlSegment' => 'log_visit.visitor_days_since_order',
);
foreach ($segments as &$segment) {
$segment['name'] = Piwik::translate($segment['name']);
$segment['category'] = Piwik::translate($segment['category']);
@ -252,6 +198,12 @@ class API extends \Piwik\Plugin\API
unset($segment['sqlFilter']);
unset($segment['sqlFilterValue']);
unset($segment['sqlSegment']);
if (isset($segment['suggestedValuesCallback'])
&& !is_string($segment['suggestedValuesCallback'])
) {
unset($segment['suggestedValuesCallback']);
}
}
}
@ -259,47 +211,37 @@ class API extends \Piwik\Plugin\API
return $segments;
}
static protected $visitEcommerceStatus = array(
GoalManager::TYPE_BUYER_NONE => 'none',
GoalManager::TYPE_BUYER_ORDERED => 'ordered',
GoalManager::TYPE_BUYER_OPEN_CART => 'abandonedCart',
GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART => 'orderedThenAbandonedCart',
);
/**
* @ignore
* @param $segmentName
* @param $table
* @return array
*/
static public function getVisitEcommerceStatusFromId($id)
protected function getSegmentValuesFromVisitorLog($segmentName, $table)
{
if (!isset(self::$visitEcommerceStatus[$id])) {
throw new \Exception("Unexpected ECommerce status value ");
}
return self::$visitEcommerceStatus[$id];
}
// Cleanup data to return the top suggested (non empty) labels for this segment
$values = $table->getColumn($segmentName);
/**
* @ignore
*/
static public function getVisitEcommerceStatus($status)
{
$id = array_search($status, self::$visitEcommerceStatus);
if ($id === false) {
throw new \Exception("Invalid 'visitEcommerceStatus' segment value $status");
}
return $id;
// Select also flattened keys (custom variables "page" scope, page URLs for one visit, page titles for one visit)
$valuesBis = $table->getColumnsStartingWith($segmentName . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP);
$values = array_merge($values, $valuesBis);
return $values;
}
private function sortSegments($row1, $row2)
{
$customVarCategory = Piwik::translate('CustomVariables_CustomVariables');
$columns = array('type', 'category', 'name', 'segment');
foreach ($columns as $column) {
// Keep segments ordered alphabetically inside categories..
$type = -1;
if ($column == 'name') $type = 1;
$compare = $type * strcmp($row1[$column], $row2[$column]);
// hack so that custom variables "page" are grouped together in the doc
if ($row1['category'] == Piwik::translate('CustomVariables_CustomVariables')
if ($row1['category'] == $customVarCategory
&& $row1['category'] == $row2['category']
) {
$compare = strcmp($row1['segment'], $row2['segment']);
@ -312,12 +254,12 @@ class API extends \Piwik\Plugin\API
return $compare;
}
/**
* Returns the url to application logo (~280x110px)
*
* @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
* @return string
* @deprecated since Piwik 2.15.1
*/
public function getLogoUrl($pathOnly = false)
{
@ -330,6 +272,7 @@ class API extends \Piwik\Plugin\API
*
* @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
* @return string
* @deprecated since Piwik 2.15.1
*/
public function getHeaderLogoUrl($pathOnly = false)
{
@ -350,7 +293,6 @@ class API extends \Piwik\Plugin\API
return $logo->getSVGLogoUrl($pathOnly);
}
/**
* Returns whether there is an SVG Logo available.
* @ignore
@ -362,7 +304,6 @@ class API extends \Piwik\Plugin\API
return $logo->hasSVGLogo();
}
/**
* Loads reports metadata, then return the requested one,
* matching optional API parameters.
@ -370,7 +311,14 @@ class API extends \Piwik\Plugin\API
public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false,
$period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false)
{
Translate::reloadLanguage($language);
Piwik::checkUserHasViewAccess($idSite);
if ($language) {
/** @var Translator $translator */
$translator = StaticContainer::get('Piwik\Translation\Translator');
$translator->setCurrentLanguage($language);
}
$reporter = new ProcessedReport();
$metadata = $reporter->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date, $hideMetricsDoc, $showSubtableReports);
return $metadata;
@ -390,6 +338,8 @@ class API extends \Piwik\Plugin\API
public function getReportMetadata($idSites = '', $period = false, $date = false, $hideMetricsDoc = false,
$showSubtableReports = false)
{
Piwik::checkUserHasViewAccess($idSites);
$reporter = new ProcessedReport();
$metadata = $reporter->getReportMetadata($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports);
return $metadata;
@ -397,11 +347,14 @@ class API extends \Piwik\Plugin\API
public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false,
$apiParameters = false, $idGoal = false, $language = false,
$showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false)
$showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false,
$format_metrics = null, $idDimension = false)
{
Piwik::checkUserHasViewAccess($idSite);
$reporter = new ProcessedReport();
$processed = $reporter->getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment,
$apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics);
$apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics, $format_metrics, $idDimension);
return $processed;
}
@ -411,6 +364,8 @@ class API extends \Piwik\Plugin\API
*/
public function get($idSite, $period, $date, $segment = false, $columns = false)
{
Piwik::checkUserHasViewAccess($idSite);
$columns = Piwik::getArrayFromApiParameter($columns);
// build columns map for faster checks later on
@ -430,7 +385,8 @@ class API extends \Piwik\Plugin\API
&& !empty($reportMeta['metrics'])
) {
$plugin = $reportMeta['module'];
foreach ($reportMeta['metrics'] as $column => $columnTranslation) {
$allMetrics = array_merge($reportMeta['metrics'], @$reportMeta['processedMetrics'] ?: array());
foreach ($allMetrics as $column => $columnTranslation) {
// a metric from this report has been requested
if (isset($columnsMap[$column])
// or by default, return all metrics
@ -450,60 +406,29 @@ class API extends \Piwik\Plugin\API
$className = Request::getClassNameAPI($plugin);
$params['columns'] = implode(',', $columns);
$dataTable = Proxy::getInstance()->call($className, 'get', $params);
// make sure the table has all columns
$array = ($dataTable instanceof DataTable\Map ? $dataTable->getDataTables() : array($dataTable));
foreach ($array as $table) {
// we don't support idSites=all&date=DATE1,DATE2
if ($table instanceof DataTable) {
$firstRow = $table->getFirstRow();
if (!$firstRow) {
$firstRow = new Row;
$table->addRow($firstRow);
}
foreach ($columns as $column) {
if ($firstRow->getColumn($column) === false) {
$firstRow->setColumn($column, 0);
}
}
}
}
$dataTable->filter(function (DataTable $table) {
$table->clearQueuedFilters();
});
// merge reports
if ($mergedDataTable === false) {
$mergedDataTable = $dataTable;
} else {
$this->mergeDataTables($mergedDataTable, $dataTable);
$merger = new MergeDataTables();
$merger->mergeDataTables($mergedDataTable, $dataTable);
}
}
if (!empty($columnsMap)
&& !empty($mergedDataTable)
) {
$mergedDataTable->queueFilter('ColumnDelete', array(false, array_keys($columnsMap)));
}
return $mergedDataTable;
}
/**
* Merge the columns of two data tables.
* Manipulates the first table.
*/
private function mergeDataTables($table1, $table2)
{
// handle table arrays
if ($table1 instanceof DataTable\Map && $table2 instanceof DataTable\Map) {
$subTables2 = $table2->getDataTables();
foreach ($table1->getDataTables() as $index => $subTable1) {
$subTable2 = $subTables2[$index];
$this->mergeDataTables($subTable1, $subTable2);
}
return;
}
$firstRow1 = $table1->getFirstRow();
$firstRow2 = $table2->getFirstRow();
if ($firstRow2 instanceof Row) {
foreach ($firstRow2->getColumns() as $metric => $value) {
$firstRow1->setColumn($metric, $value);
}
}
}
/**
* Given an API report to query (eg. "Referrers.getKeywords", and a Label (eg. "free%20software"),
* this function will query the API for the previous days/weeks/etc. and will return
@ -521,15 +446,21 @@ class API extends \Piwik\Plugin\API
* @param bool|int $idGoal
* @param bool|string $legendAppendMetric
* @param bool|string $labelUseAbsoluteUrl
* @param bool|int $idDimension
* @return array
*/
public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false)
{
Piwik::checkUserHasViewAccess($idSite);
$rowEvolution = new RowEvolution();
return $rowEvolution->getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $column,
$language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl);
$language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl, $idDimension);
}
/**
* @deprecated
*/
public function getLastDate($date, $period)
{
$lastDate = Range::getLastDate($date, $period);
@ -554,12 +485,38 @@ class API extends \Piwik\Plugin\API
$result = array();
foreach ($urls as $url) {
$req = new Request($url . '&format=php&serialize=0');
$params = Request::getRequestArrayFromString($url . '&format=php&serialize=0');
if (isset($params['urls']) && $params['urls'] == $urls) {
// by default 'urls' is added to $params as Request::getRequestArrayFromString adds all $_GET/$_POST
// default parameters
unset($params['urls']);
}
if (!empty($params['segment']) && strpos($url, 'segment=') > -1) {
// only unsanitize input when segment is actually present in URL, not when it was used from
// $defaultRequest in Request::getRequestArrayFromString from $_GET/$_POST
$params['segment'] = urlencode(Common::unsanitizeInputValue($params['segment']));
}
$req = new Request($params);
$result[] = $req->process();
}
return $result;
}
/**
* Return true if plugin is activated, false otherwise
*
* @param string $pluginName
* @return bool
*/
public function isPluginActivated($pluginName)
{
Piwik::checkUserHasSomeViewAccess();
return \Piwik\Plugin\Manager::getInstance()->isPluginActivated($pluginName);
}
/**
* Given a segment, will return a list of the most used values for this particular segment.
* @param $segmentName
@ -569,8 +526,63 @@ class API extends \Piwik\Plugin\API
*/
public function getSuggestedValuesForSegment($segmentName, $idSite)
{
if (empty(Config::getInstance()->General['enable_segment_suggested_values'])) {
return array();
}
Piwik::checkUserHasViewAccess($idSite);
$maxSuggestionsToReturn = 30;
$segment = $this->findSegment($segmentName, $idSite);
// if segment has suggested values callback then return result from it instead
$suggestedValuesCallbackRequiresTable = false;
if (isset($segment['suggestedValuesCallback'])) {
$suggestedValuesCallbackRequiresTable = $this->doesSuggestedValuesCallbackNeedData(
$segment['suggestedValuesCallback']);
if (!$suggestedValuesCallbackRequiresTable) {
return call_user_func($segment['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn);
}
}
// if period=range is disabled, do not proceed
if (!Period\Factory::isPeriodEnabledForAPI('range')) {
return array();
}
if (!empty($segment['unionOfSegments'])) {
$values = array();
foreach ($segment['unionOfSegments'] as $unionSegmentName) {
$unionSegment = $this->findSegment($unionSegmentName, $idSite);
try {
$result = $this->getSuggestedValuesForSegmentName($idSite, $unionSegment, $maxSuggestionsToReturn);
if (!empty($result)) {
$values = array_merge($result, $values);
}
} catch (\Exception $e) {
// we ignore if there was no data found for $unionSegmentName
}
}
if (empty($values)) {
throw new \Exception("There was no data to suggest for $segmentName");
}
} else {
$values = $this->getSuggestedValuesForSegmentName($idSite, $segment, $maxSuggestionsToReturn);
}
$values = $this->getMostFrequentValues($values);
$values = array_slice($values, 0, $maxSuggestionsToReturn);
$values = array_map(array('Piwik\Common', 'unsanitizeInputValue'), $values);
return $values;
}
private function findSegment($segmentName, $idSite)
{
$segmentsMetadata = $this->getSegmentsMetadata($idSite, $_hideImplementationData = false);
$segmentFound = false;
@ -580,11 +592,17 @@ class API extends \Piwik\Plugin\API
break;
}
}
if (empty($segmentFound)) {
throw new \Exception("Requested segment not found.");
throw new \Exception("Requested segment $segmentName not found.");
}
$startDate = Date::now()->subDay(60)->toString();
return $segmentFound;
}
private function getSuggestedValuesForSegmentName($idSite, $segment, $maxSuggestionsToReturn)
{
$startDate = Date::now()->subDay(60)->toString();
$requestLastVisits = "method=Live.getLastVisitsDetails
&idSite=$idSite
&period=range
@ -593,40 +611,61 @@ class API extends \Piwik\Plugin\API
&serialize=0
&flat=1";
$segmentName = $segment['segment'];
// Select non empty fields only
// Note: this optimization has only a very minor impact
$requestLastVisits .= "&segment=$segmentName" . urlencode('!=');
// By default Live fetches all actions for all visitors, but we'd rather do this only when required
if ($this->doesSegmentNeedActionsData($segmentName)) {
$requestLastVisits .= "&filter_limit=500";
$requestLastVisits .= "&filter_limit=400";
} else {
$requestLastVisits .= "&doNotFetchActions=1";
$requestLastVisits .= "&filter_limit=1000";
$requestLastVisits .= "&filter_limit=800";
}
$request = new Request($requestLastVisits);
$table = $request->process();
if (empty($table)) {
throw new \Exception("There was no data to suggest for $segmentName");
}
// Cleanup data to return the top suggested (non empty) labels for this segment
$values = $table->getColumn($segmentName);
// Select also flattened keys (custom variables "page" scope, page URLs for one visit, page titles for one visit)
$valuesBis = $table->getColumnsStartingWith($segmentName . ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP);
$values = array_merge($values, $valuesBis);
$values = $this->getMostFrequentValues($values);
$values = array_slice($values, 0, $maxSuggestionsToReturn);
$values = array_map(array('Piwik\Common', 'unsanitizeInputValue'), $values);
if (isset($segment['suggestedValuesCallback']) &&
$this->doesSuggestedValuesCallbackNeedData($segment['suggestedValuesCallback'])) {
$values = call_user_func($segment['suggestedValuesCallback'], $idSite, $maxSuggestionsToReturn, $table);
} else {
$values = $this->getSegmentValuesFromVisitorLog($segmentName, $table);
}
return $values;
}
/**
* A glossary of all reports and their definition
*
* @param $idSite
* @return array
*/
public function getGlossaryReports($idSite)
{
$glossary = StaticContainer::get('Piwik\Plugins\API\Glossary');
return $glossary->reportsGlossary($idSite);
}
/**
* A glossary of all metrics and their definition
*
* @param $idSite
* @return array
*/
public function getGlossaryMetrics($idSite)
{
$glossary = StaticContainer::get('Piwik\Plugins\API\Glossary');
return $glossary->metricsGlossary($idSite);
}
/**
* @param $segmentName
* @return bool
@ -636,10 +675,13 @@ class API extends \Piwik\Plugin\API
// If you update this, also update flattenVisitorDetailsArray
$segmentsNeedActionsInfo = array('visitConvertedGoalId',
'pageUrl', 'pageTitle', 'siteSearchKeyword',
'entryPageTitle', 'entryPageUrl', 'exitPageTitle', 'exitPageUrl');
'entryPageTitle', 'entryPageUrl', 'exitPageTitle', 'exitPageUrl',
'outlinkUrl', 'downloadUrl'
);
$isCustomVariablePage = stripos($segmentName, 'customVariablePage') !== false;
$isEventSegment = stripos($segmentName, 'event') !== false;
$doesSegmentNeedActionsInfo = in_array($segmentName, $segmentsNeedActionsInfo) || $isCustomVariablePage || $isEventSegment;
$isContentSegment = stripos($segmentName, 'content') !== false;
$doesSegmentNeedActionsInfo = in_array($segmentName, $segmentsNeedActionsInfo) || $isCustomVariablePage || $isEventSegment || $isContentSegment;
return $doesSegmentNeedActionsInfo;
}
@ -666,6 +708,23 @@ class API extends \Piwik\Plugin\API
$values = array_keys($values);
return $values;
}
private function doesSuggestedValuesCallbackNeedData($suggestedValuesCallback)
{
if (is_string($suggestedValuesCallback)
&& strpos($suggestedValuesCallback, '::') !== false
) {
$suggestedValuesCallback = explode('::', $suggestedValuesCallback);
}
if (is_array($suggestedValuesCallback)) {
$methodMetadata = new \ReflectionMethod($suggestedValuesCallback[0], $suggestedValuesCallback[1]);
} else {
$methodMetadata = new \ReflectionFunction($suggestedValuesCallback);
}
return $methodMetadata->getNumberOfParameters() >= 3;
}
}
/**
@ -679,43 +738,15 @@ class Plugin extends \Piwik\Plugin
}
/**
* @see Piwik\Plugin::getListHooksRegistered
* @see Piwik\Plugin::registerEvents
*/
public function getListHooksRegistered()
public function registerEvents()
{
return array(
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'Menu.Top.addItems' => 'addTopMenu',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles'
);
}
public function addTopMenu()
{
$apiUrlParams = array('module' => 'API', 'action' => 'listAllAPI', 'segment' => false);
$tooltip = Piwik::translate('API_TopLinkTooltip');
MenuTop::addEntry('General_API', $apiUrlParams, true, 7, $isHTML = false, $tooltip);
$this->addTopMenuMobileApp();
}
protected function addTopMenuMobileApp()
{
if (empty($_SERVER['HTTP_USER_AGENT'])) {
return;
}
if (!class_exists("DeviceDetector")) {
throw new \Exception("DeviceDetector could not be found, maybe you are using Piwik from git and need to have update Composer. <br>php composer.phar update");
}
$ua = new \DeviceDetector($_SERVER['HTTP_USER_AGENT']);
$ua->parse();
$os = $ua->getOs('short_name');
if ($os && in_array($os, array('AND', 'IOS'))) {
MenuTop::addEntry('Piwik Mobile App', array('module' => 'Proxy', 'action' => 'redirect', 'url' => 'http://piwik.org/mobile/'), true, 4);
}
}
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/API/stylesheets/listAllAPI.less";

View file

@ -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
@ -14,6 +14,8 @@ use Piwik\API\Request;
use Piwik\Common;
use Piwik\Config;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Url;
use Piwik\View;
/**
@ -23,12 +25,22 @@ class Controller extends \Piwik\Plugin\Controller
{
function index()
{
$token = 'token_auth=' . Common::getRequestVar('token_auth', 'anonymous', 'string');
// when calling the API through http, we limit the number of returned results
if (!isset($_GET['filter_limit'])) {
$_GET['filter_limit'] = Config::getInstance()->General['API_datatable_default_limit'];
$token .= '&api_datatable_default_limit=' . $_GET['filter_limit'];
}
$request = new Request('token_auth=' . Common::getRequestVar('token_auth', 'anonymous', 'string'));
return $request->process();
$request = new Request($token);
$response = $request->process();
if (is_array($response)) {
$response = var_export($response, true);
}
return $response;
}
public function listAllMethods()
@ -58,7 +70,7 @@ class Controller extends \Piwik\Plugin\Controller
foreach ($segments as $segment) {
// Eg. Event Value is a metric, not in the Visit metric category,
// we make sure it is displayed along with the Events dimensions
if($segment['type'] == 'metric' && $segment['category'] != Piwik::translate('General_Visit')) {
if ($segment['type'] == 'metric' && $segment['category'] != Piwik::translate('General_Visit')) {
$segment['type'] = 'dimension';
}
@ -125,4 +137,14 @@ class Controller extends \Piwik\Plugin\Controller
</table>
";
}
public function glossary()
{
Piwik::checkUserHasSomeViewAccess();
return $this->renderTemplate('glossary', array(
'reports' => Request::processRequest('API', array('method' => 'API.getGlossaryReports')),
'metrics' => Request::processRequest('API', array('method' => 'API.getGlossaryMetrics')),
));
}
}

View file

@ -0,0 +1,55 @@
<?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\API\DataTable;
use Piwik\DataTable\Row;
use Piwik\DataTable;
class MergeDataTables
{
/**
* Merge the columns of two data tables.
* Manipulates the first table.
*
* @param DataTable|DataTable\Map $table1 The table to eventually filter.
* @param DataTable|DataTable\Map $table2 Whether to delete rows with no visits or not.
*/
public function mergeDataTables($table1, $table2)
{
// handle table arrays
if ($table1 instanceof DataTable\Map && $table2 instanceof DataTable\Map) {
$subTables2 = $table2->getDataTables();
foreach ($table1->getDataTables() as $index => $subTable1) {
if (!array_key_exists($index, $subTables2)) {
// occurs when archiving starts on dayN and continues into dayN+1, see https://github.com/piwik/piwik/issues/5168#issuecomment-50959925
continue;
}
$subTable2 = $subTables2[$index];
$this->mergeDataTables($subTable1, $subTable2);
}
return;
}
$firstRow2 = $table2->getFirstRow();
if (!($firstRow2 instanceof Row)) {
return;
}
$firstRow1 = $table1->getFirstRow();
if (empty($firstRow1)) {
$firstRow1 = $table1->addRow(new Row());
}
foreach ($firstRow2->getColumns() as $metric => $value) {
$firstRow1->setColumn($metric, $value);
}
}
}

View file

@ -0,0 +1,108 @@
<?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\API;
class Glossary
{
protected $metadata = array();
public function __construct(API $api)
{
$this->api = $api;
}
public function reportsGlossary($idSite)
{
$metadata = $this->api->getReportMetadata($idSite);
$reports = array();
foreach ($metadata as $report) {
if (isset($report['documentation'])) {
$reports[] = array(
'name' => sprintf("%s (%s)", $report['name'], $report['category']),
'documentation' => $report['documentation']
);
}
}
usort($reports, function ($a, $b) {
return strcmp($a['name'], $b['name']);
});
return $reports;
}
public function metricsGlossary($idSite)
{
$metadata = $this->api->getReportMetadata($idSite);
$metrics = array();
foreach ($metadata as $report) {
if (!isset($report['metricsDocumentation'])) {
continue;
}
foreach ($report['metricsDocumentation'] as $metricId => $metricDocumentation) {
$metricKey = $metricId;
if(empty($report['metrics'][$metricId])
&& empty($report['processedMetrics'][$metricId])) {
continue;
}
$metricName = isset($report['metrics'][$metricId]) ? $report['metrics'][$metricId] : $report['processedMetrics'][$metricId];
// Already one metric with same name, but different documentation...
if (isset($metrics[$metricKey])
&& $metrics[$metricKey]['documentation'] !== $metricDocumentation) {
// Don't show nb_hits in glossary since it duplicates others, eg. nb_downloads,
if($metricKey == 'nb_hits') {
continue;
}
$metricName = sprintf("%s (%s)", $metricName, $report['category']);
$metricKey = $metricName;
if (isset($metrics[$metricKey]) && $metrics[$metricKey]['documentation'] !== $metricDocumentation) {
throw new \Exception(sprintf("Metric %s has two different documentations: \n(1) %s \n(2) %s",
$metricKey,
$metrics[$metricKey]['documentation'],
$metricDocumentation)
);
}
} else {
if (!isset($report['metrics'][$metricId])
&& !isset($report['processedMetrics'][$metricId])
) {
// $metricId metric name not found in $report['dimension'] report
// it will be set in another one
continue;
}
}
$metrics[$metricKey] = array(
'name' => $metricName,
'id' => $metricId,
'documentation' => $metricDocumentation
);
}
}
usort($metrics, function ($a, $b) {
return strcmp($a['name'], $b['name']);
});
return $metrics;
}
}

View file

@ -0,0 +1,67 @@
<?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\API;
use Piwik\DeviceDetectorCache;
use Piwik\Menu\MenuTop;
use Piwik\Menu\MenuUser;
use Piwik\Piwik;
use DeviceDetector\Parser\OperatingSystem;
class Menu extends \Piwik\Plugin\Menu
{
const DD_SHORT_NAME_ANDROID = 'AND';
const DD_SHORT_NAME_IOS = 'IOS';
public function configureTopMenu(MenuTop $menu)
{
$this->addTopMenuMobileApp($menu);
}
public function configureUserMenu(MenuUser $menu)
{
$menu->addPlatformItem('General_API',
$this->urlForAction('listAllAPI', array('segment' => false)),
6,
Piwik::translate('API_TopLinkTooltip')
);
if(Piwik::isUserIsAnonymous()) {
$menu->addPlatformItem('API_Glossary',
$this->urlForAction('glossary', array('segment' => false)),
50
);
}
}
private function addTopMenuMobileApp(MenuTop $menu)
{
if (empty($_SERVER['HTTP_USER_AGENT'])) {
return;
}
if (!class_exists("DeviceDetector\\DeviceDetector")) {
throw new \Exception("DeviceDetector could not be found, maybe you are using Piwik from git and need to update Composer. Execute this command: php composer.phar update");
}
$ua = new OperatingSystem($_SERVER['HTTP_USER_AGENT']);
$ua->setCache(new DeviceDetectorCache(86400));
$parsedOS = $ua->parse();
if (!empty($parsedOS['short_name']) && in_array($parsedOS['short_name'], array(self::DD_SHORT_NAME_ANDROID, self::DD_SHORT_NAME_IOS))) {
$url = $this->urlForModuleAction('Proxy', 'redirect', array('url' => 'http://piwik.org/mobile/'));
if ($url) {
$menu->addItem('Piwik Mobile App', null, $url, 4);
}
}
}
}

View file

@ -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,24 @@ namespace Piwik\Plugins\API;
use Exception;
use Piwik\API\Request;
use Piwik\Archive\DataTableFactory;
use Piwik\CacheId;
use Piwik\Cache as PiwikCache;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\DataTable\Simple;
use Piwik\DataTable;
use Piwik\Date;
use Piwik\Metrics;
use Piwik\MetricsFormatter;
use Piwik\Metrics\Formatter;
use Piwik\Period;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Site;
use Piwik\Timer;
use Piwik\Url;
class ProcessedReport
{
/**
* Loads reports metadata, then return the requested one,
* matching optional API parameters.
@ -38,8 +40,10 @@ class ProcessedReport
foreach ($reportsMetadata as $report) {
// See ArchiveProcessor/Aggregator.php - unique visitors are not processed for period != day
// todo: should use SettingsPiwik::isUniqueVisitorsEnabled instead
if (($period && $period != 'day') && !($apiModule == 'VisitsSummary' && $apiAction == 'get')) {
unset($report['metrics']['nb_uniq_visitors']);
unset($report['metrics']['nb_users']);
}
if ($report['module'] == $apiModule
&& $report['action'] == $apiAction
@ -148,31 +152,45 @@ class ProcessedReport
Piwik::checkUserHasViewAccess($idSites);
}
// as they cache key contains a lot of information there would be an even better cache result by caching parts of
// this huge method separately but that makes it also more complicated. leaving it like this for now.
$key = $this->buildReportMetadataCacheKey($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports);
$key = CacheId::pluginAware($key);
$cache = PiwikCache::getTransientCache();
if ($cache->contains($key)) {
return $cache->fetch($key);
}
$parameters = array('idSites' => $idSites, 'period' => $period, 'date' => $date);
$availableReports = array();
foreach (Report::getAllReports() as $report) {
$report->configureReportMetadata($availableReports, $parameters);
}
/**
* Triggered when gathering metadata for all available reports.
*
*
* Plugins that define new reports should use this event to make them available in via
* the metadata API. By doing so, the report will become available in scheduled reports
* as well as in the Piwik Mobile App. In fact, any third party app that uses the metadata
* API will automatically have access to the new report.
*
*
* @param string &$availableReports The list of available reports. Append to this list
* to make a report available.
*
*
* Every element of this array must contain the following
* information:
*
*
* - **category**: A translated string describing the report's category.
* - **name**: The translated display title of the report.
* - **module**: The plugin of the report.
* - **action**: The API method that serves the report.
*
*
* The following information is optional:
*
*
* - **dimension**: The report's [dimension](/guides/all-about-analytics-data#dimensions) if any.
* - **metrics**: An array mapping metric names with their display names.
* - **metricsDocumentation**: An array mapping metric names with their
@ -183,25 +201,31 @@ class ProcessedReport
* metrics.
* - **order**: The order of the report in the list of reports
* with the same category.
*
*
* @param array $parameters Contains the values of the sites and period we are
* getting reports for. Some reports depend on this data.
* For example, Goals reports depend on the site IDs being
* requested. Contains the following information:
*
*
* - **idSites**: The array of site IDs we are getting reports for.
* - **period**: The period type, eg, `'day'`, `'week'`, `'month'`,
* `'year'`, `'range'`.
* - **date**: A string date within the period or a date range, eg,
* `'2013-01-01'` or `'2012-01-01,2013-01-01'`.
*
*
* TODO: put dimensions section in all about analytics data
* @deprecated since 2.5.0 Use Report Classes instead.
* @ignore
*/
Piwik::postEvent('API.getReportMetadata', array(&$availableReports, $parameters));
// TODO we can remove this one once we remove API.getReportMetadata event (except hideMetricsDoc)
foreach ($availableReports as &$availableReport) {
// can be removed once we remove hook API.getReportMetadata
if (!isset($availableReport['metrics'])) {
$availableReport['metrics'] = Metrics::getDefaultMetrics();
}
// can be removed once we remove hook API.getReportMetadata
if (!isset($availableReport['processedMetrics'])) {
$availableReport['processedMetrics'] = Metrics::getDefaultProcessedMetrics();
}
@ -211,24 +235,25 @@ class ProcessedReport
unset($availableReport['metricsDocumentation']);
} else if (!isset($availableReport['metricsDocumentation'])) {
// set metric documentation to default if it's not set
// can be removed once we remove hook API.getReportMetadata
$availableReport['metricsDocumentation'] = Metrics::getDefaultMetricsDocumentation();
}
}
/**
* Triggered after all available reports are collected.
*
*
* This event can be used to modify the report metadata of reports in other plugins. You
* could, for example, add custom metrics to every report or remove reports from the list
* of available reports.
*
*
* @param array &$availableReports List of all report metadata. Read the {@hook API.getReportMetadata}
* docs to see what this array contains.
* @param array $parameters Contains the values of the sites and period we are
* getting reports for. Some report depend on this data.
* For example, Goals reports depend on the site IDs being
* request. Contains the following information:
*
*
* - **idSites**: The array of site IDs we are getting reports for.
* - **period**: The period type, eg, `'day'`, `'week'`, `'month'`,
* `'year'`, `'range'`.
@ -238,16 +263,17 @@ class ProcessedReport
Piwik::postEvent('API.getReportMetadata.end', array(&$availableReports, $parameters));
// Sort results to ensure consistent order
usort($availableReports, array($this, 'sort'));
// Add the magic API.get report metadata aggregating all plugins API.get API calls automatically
$this->addApiGetMetdata($availableReports);
usort($availableReports, array('self', 'sortReports'));
$knownMetrics = array_merge(Metrics::getDefaultMetrics(), Metrics::getDefaultProcessedMetrics());
$columnsToKeep = $this->getColumnsToKeep();
$columnsToRemove = $this->getColumnsToRemove();
foreach ($availableReports as &$availableReport) {
// Ensure all metrics have a translation
$metrics = $availableReport['metrics'];
$cleanedMetrics = array();
// TODO we can remove this once we remove the getReportMetadata event, leaving it here for backwards compatibility
foreach ($metrics as $metricId => $metricTranslation) {
// When simply the column name was given, ie 'metric' => array( 'nb_visits' )
// $metricTranslation is in this case nb_visits. We look for a known translation.
@ -262,13 +288,13 @@ class ProcessedReport
$availableReport['metrics'] = $cleanedMetrics;
// if hide/show columns specified, hide/show metrics & docs
$availableReport['metrics'] = $this->hideShowMetrics($availableReport['metrics']);
$availableReport['metrics'] = $this->hideShowMetricsWithParams($availableReport['metrics'], $columnsToRemove, $columnsToKeep);
if (isset($availableReport['processedMetrics'])) {
$availableReport['processedMetrics'] = $this->hideShowMetrics($availableReport['processedMetrics']);
$availableReport['processedMetrics'] = $this->hideShowMetricsWithParams($availableReport['processedMetrics'], $columnsToRemove, $columnsToKeep);
}
if (isset($availableReport['metricsDocumentation'])) {
$availableReport['metricsDocumentation'] =
$this->hideShowMetrics($availableReport['metricsDocumentation']);
$this->hideShowMetricsWithParams($availableReport['metricsDocumentation'], $columnsToRemove, $columnsToKeep);
}
// Remove array elements that are false (to clean up API output)
@ -278,6 +304,7 @@ class ProcessedReport
}
}
// when there are per goal metrics, don't display conversion_rate since it can differ from per goal sum
// TODO we should remove this once we remove the getReportMetadata event, leaving it here for backwards compatibility
if (isset($availableReport['metricsGoal'])) {
unset($availableReport['processedMetrics']['conversion_rate']);
unset($availableReport['metricsGoal']['conversion_rate']);
@ -306,93 +333,79 @@ class ProcessedReport
}
}
return array_values($availableReports); // make sure array has contiguous key values
$actualReports = array_values($availableReports);
$cache->save($key, $actualReports);
return $actualReports; // make sure array has contiguous key values
}
/**
* API metadata are sorted by category/name,
* with a little tweak to replicate the standard Piwik category ordering
*
* @param string $a
* @param string $b
* @param array $a
* @param array $b
* @return int
*/
private function sort($a, $b)
private static function sortReports($a, $b)
{
static $order = null;
if (is_null($order)) {
$order = array(
Piwik::translate('General_MultiSitesSummary'),
Piwik::translate('VisitsSummary_VisitsSummary'),
Piwik::translate('Goals_Ecommerce'),
Piwik::translate('General_Actions'),
Piwik::translate('Events_Events'),
Piwik::translate('Actions_SubmenuSitesearch'),
Piwik::translate('Referrers_Referrers'),
Piwik::translate('Goals_Goals'),
Piwik::translate('General_Visitors'),
Piwik::translate('DevicesDetection_DevicesDetection'),
Piwik::translate('UserSettings_VisitorSettings'),
);
}
return ($category = strcmp(array_search($a['category'], $order), array_search($b['category'], $order))) == 0
? (@$a['order'] < @$b['order'] ? -1 : 1)
: $category;
}
/**
* Add the metadata for the API.get report
* In other plugins, this would hook on 'API.getReportMetadata'
*/
private function addApiGetMetdata(&$availableReports)
{
$metadata = array(
'category' => Piwik::translate('General_API'),
'name' => Piwik::translate('General_MainMetrics'),
'module' => 'API',
'action' => 'get',
'metrics' => array(),
'processedMetrics' => array(),
'metricsDocumentation' => array(),
'order' => 1
);
$indexesToMerge = array('metrics', 'processedMetrics', 'metricsDocumentation');
foreach ($availableReports as $report) {
if ($report['action'] == 'get') {
foreach ($indexesToMerge as $index) {
if (isset($report[$index])
&& is_array($report[$index])
) {
$metadata[$index] = array_merge($metadata[$index], $report[$index]);
}
}
$order = array();
foreach (Report::$orderOfReports as $category) {
$order[] = Piwik::translate($category);
}
}
$availableReports[] = $metadata;
$posA = array_search($a['category'], $order);
$posB = array_search($b['category'], $order);
if ($posA === false && $posB === false) {
return strcmp($a['category'], $b['category']);
} elseif ($posA === false) {
return 1;
} elseif ($posB === false) {
return -1;
}
$category = strcmp($posA, $posB);
if ($category == 0) {
return (@$a['order'] < @$b['order'] ? -1 : 1);
}
return $category;
}
public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false,
$apiParameters = false, $idGoal = false, $language = false,
$showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false)
$showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false,
$formatMetrics = null, $idDimension = false)
{
$timer = new Timer();
if (empty($apiParameters)) {
$apiParameters = array();
}
if (!empty($idGoal)
&& empty($apiParameters['idGoal'])
) {
$apiParameters['idGoal'] = $idGoal;
}
if (!empty($idDimension)
&& empty($apiParameters['idDimension'])
) {
$apiParameters['idDimension'] = (int) $idDimension;
}
// Is this report found in the Metadata available reports?
$reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language,
$period, $date, $hideMetricsDoc, $showSubtableReports = true);
if (empty($reportMetadata)) {
throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite not found in the list of available reports. \n");
}
$reportMetadata = reset($reportMetadata);
// Generate Api call URL passing custom parameters
@ -406,8 +419,18 @@ class ProcessedReport
'language' => $language,
'idSubtable' => $idSubtable,
));
if (!empty($segment)) $parameters['segment'] = $segment;
if (!empty($reportMetadata['processedMetrics'])
&& !empty($reportMetadata['metrics']['nb_visits'])
&& @$reportMetadata['category'] != Piwik::translate('Goals_Ecommerce')
&& $apiModule !== 'MultiSites'
) {
$deleteRowsWithNoVisits = empty($reportMetadata['constantRowsCount']) ? '1' : '0';
$parameters['filter_add_columns_when_show_all_columns'] = $deleteRowsWithNoVisits;
}
$url = Url::getQueryStringFromParameters($parameters);
$request = new Request($url);
try {
@ -417,13 +440,14 @@ class ProcessedReport
throw new Exception("API returned an error: " . $e->getMessage() . " at " . basename($e->getFile()) . ":" . $e->getLine() . "\n");
}
list($newReport, $columns, $rowsMetadata, $totals) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, $showRawMetrics);
foreach ($columns as $columnId => &$name) {
list($newReport, $columns, $rowsMetadata, $totals) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, $showRawMetrics, $formatMetrics);
foreach ($columns as &$name) {
$name = ucfirst($name);
}
$website = new Site($idSite);
$period = Period::factory($period, $date);
$period = Period\Factory::build($period, $date);
$period = $period->getLocalizedLongString();
$return = array(
@ -454,44 +478,44 @@ class ProcessedReport
* @param \Piwik\DataTable\Map|\Piwik\DataTable\Simple $dataTable
* @param array $reportMetadata
* @param bool $showRawMetrics
* @param bool|null $formatMetrics
* @return array Simple|Set $newReport with human readable format & array $columns list of translated column names & Simple|Set $rowsMetadata
*/
private function handleTableReport($idSite, $dataTable, &$reportMetadata, $showRawMetrics = false)
private function handleTableReport($idSite, $dataTable, &$reportMetadata, $showRawMetrics = false, $formatMetrics = null)
{
$hasDimension = isset($reportMetadata['dimension']);
$columns = $reportMetadata['metrics'];
$columns = @$reportMetadata['metrics'] ?: array();
if ($hasDimension) {
$columns = array_merge(
array('label' => $reportMetadata['dimension']),
$columns
);
}
if (isset($reportMetadata['processedMetrics'])) {
$processedMetricsAdded = Metrics::getDefaultProcessedMetrics();
foreach ($processedMetricsAdded as $processedMetricId => $processedMetricTranslation) {
// this processed metric can be displayed for this report
if (isset($reportMetadata['processedMetrics'][$processedMetricId])) {
$columns[$processedMetricId] = $processedMetricTranslation;
}
if (isset($reportMetadata['processedMetrics']) && is_array($reportMetadata['processedMetrics'])) {
$processedMetricsAdded = Metrics::getDefaultProcessedMetrics();
foreach ($reportMetadata['processedMetrics'] as $processedMetricId => $processedMetricTranslation) {
// this processed metric can be displayed for this report
if ($processedMetricTranslation && $processedMetricId !== $processedMetricTranslation) {
$columns[$processedMetricId] = $processedMetricTranslation;
} elseif (isset($processedMetricsAdded[$processedMetricId])) {
// for instance in case 'nb_visits' => 'nb_visits' we will translate it
$columns[$processedMetricId] = $processedMetricsAdded[$processedMetricId];
}
}
}
// Display the global Goal metrics
if (isset($reportMetadata['metricsGoal'])) {
$metricsGoalDisplay = array('revenue');
// Add processed metrics to be displayed for this report
foreach ($metricsGoalDisplay as $goalMetricId) {
if (isset($reportMetadata['metricsGoal'][$goalMetricId])) {
$columns[$goalMetricId] = $reportMetadata['metricsGoal'][$goalMetricId];
}
// Display the global Goal metrics
if (isset($reportMetadata['metricsGoal'])) {
$metricsGoalDisplay = array('revenue');
// Add processed metrics to be displayed for this report
foreach ($metricsGoalDisplay as $goalMetricId) {
if (isset($reportMetadata['metricsGoal'][$goalMetricId])) {
$columns[$goalMetricId] = $reportMetadata['metricsGoal'][$goalMetricId];
}
}
if (isset($reportMetadata['processedMetrics'])) {
// Add processed metrics
$dataTable->filter('AddColumnsProcessedMetrics', array($deleteRowsWithNoVisit = false));
}
}
$columns = $this->hideShowMetrics($columns);
@ -508,10 +532,10 @@ class ProcessedReport
$rowsMetadata->setKeyName("prettyDate");
// Process each Simple entry
foreach ($dataTable->getDataTables() as $label => $simpleDataTable) {
foreach ($dataTable->getDataTables() as $simpleDataTable) {
$this->removeEmptyColumns($columns, $reportMetadata, $simpleDataTable);
list($enhancedSimpleDataTable, $rowMetadata) = $this->handleSimpleDataTable($idSite, $simpleDataTable, $columns, $hasDimension, $showRawMetrics);
list($enhancedSimpleDataTable, $rowMetadata) = $this->handleSimpleDataTable($idSite, $simpleDataTable, $columns, $hasDimension, $showRawMetrics, $formatMetrics);
$enhancedSimpleDataTable->setAllTableMetadata($simpleDataTable->getAllTableMetadata());
$period = $simpleDataTable->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLocalizedLongString();
@ -522,7 +546,7 @@ class ProcessedReport
}
} else {
$this->removeEmptyColumns($columns, $reportMetadata, $dataTable);
list($newReport, $rowsMetadata) = $this->handleSimpleDataTable($idSite, $dataTable, $columns, $hasDimension, $showRawMetrics);
list($newReport, $rowsMetadata) = $this->handleSimpleDataTable($idSite, $dataTable, $columns, $hasDimension, $showRawMetrics, $formatMetrics);
$totals = $this->aggregateReportTotalValues($dataTable, $totals);
}
@ -547,14 +571,17 @@ class ProcessedReport
return;
}
$columns = $this->hideShowMetrics($columns, $emptyColumns);
$columnsToRemove = $this->getColumnsToRemove();
$columnsToKeep = $this->getColumnsToKeep();
$columns = $this->hideShowMetricsWithParams($columns, $columnsToRemove, $columnsToKeep, $emptyColumns);
if (isset($reportMetadata['metrics'])) {
$reportMetadata['metrics'] = $this->hideShowMetrics($reportMetadata['metrics'], $emptyColumns);
$reportMetadata['metrics'] = $this->hideShowMetricsWithParams($reportMetadata['metrics'], $columnsToRemove, $columnsToKeep, $emptyColumns);
}
if (isset($reportMetadata['metricsDocumentation'])) {
$reportMetadata['metricsDocumentation'] = $this->hideShowMetrics($reportMetadata['metricsDocumentation'], $emptyColumns);
$reportMetadata['metricsDocumentation'] = $this->hideShowMetricsWithParams($reportMetadata['metricsDocumentation'], $columnsToRemove, $columnsToKeep, $emptyColumns);
}
}
@ -574,9 +601,21 @@ class ProcessedReport
}
// remove columns if hideColumns query parameters exist
$columnsToRemove = Common::getRequestVar('hideColumns', '');
if ($columnsToRemove != '') {
$columnsToRemove = explode(',', $columnsToRemove);
$columnsToRemove = $this->getColumnsToRemove();
// remove columns if showColumns query parameters exist
$columnsToKeep = $this->getColumnsToKeep();
return $this->hideShowMetricsWithParams($columns, $columnsToRemove, $columnsToKeep, $emptyColumns);
}
private function hideShowMetricsWithParams($columns, $columnsToRemove, $columnsToKeep, $emptyColumns = array())
{
if (!is_array($columns)) {
return $columns;
}
if (null !== $columnsToRemove) {
foreach ($columnsToRemove as $name) {
// if a column to remove is in the column list, remove it
if (isset($columns[$name])) {
@ -585,12 +624,7 @@ class ProcessedReport
}
}
// remove columns if showColumns query parameters exist
$columnsToKeep = Common::getRequestVar('showColumns', '');
if ($columnsToKeep != '') {
$columnsToKeep = explode(',', $columnsToKeep);
$columnsToKeep[] = 'label';
if (null !== $columnsToKeep) {
foreach ($columns as $name => $ignore) {
// if the current column should not be kept, remove it
$idx = array_search($name, $columnsToKeep);
@ -626,10 +660,10 @@ class ProcessedReport
* @param array $metadataColumns
* @param boolean $hasDimension
* @param bool $returnRawMetrics If set to true, the original metrics will be returned
*
* @param bool|null $formatMetrics
* @return array DataTable $enhancedDataTable filtered metrics with human readable format & Simple $rowsMetadata
*/
private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension, $returnRawMetrics = false)
private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension, $returnRawMetrics = false, $formatMetrics = null)
{
// new DataTable to store metadata
$rowsMetadata = new DataTable();
@ -641,28 +675,50 @@ class ProcessedReport
$enhancedDataTable = new Simple();
}
// add missing metrics
foreach ($simpleDataTable->getRows() as $row) {
$rowMetrics = $row->getColumns();
foreach ($metadataColumns as $id => $name) {
if (!isset($rowMetrics[$id])) {
$row->addColumn($id, 0);
}
}
}
$formatter = new Formatter();
foreach ($simpleDataTable->getRows() as $row) {
$rowMetrics = $row->getColumns();
// add missing metrics
foreach ($metadataColumns as $id => $name) {
if (!isset($rowMetrics[$id])) {
$row->setColumn($id, 0);
$rowMetrics[$id] = 0;
}
}
$enhancedRow = new Row();
$enhancedDataTable->addRow($enhancedRow);
$rowMetrics = $row->getColumns();
foreach ($rowMetrics as $columnName => $columnValue) {
// filter metrics according to metadata definition
if (isset($metadataColumns[$columnName])) {
// generate 'human readable' metric values
$prettyValue = MetricsFormatter::getPrettyValue($idSite, $columnName, $columnValue, $htmlAllowed = false);
// if we handle MultiSites.getAll we do not always have the same idSite but different ones for
// each site, see https://github.com/piwik/piwik/issues/5006
$idSiteForRow = $idSite;
$idSiteMetadata = $row->getMetadata('idsite');
if ($idSiteMetadata && is_numeric($idSiteMetadata)) {
$idSiteForRow = (int) $idSiteMetadata;
}
// format metrics manually here to maintain API.getProcessedReport BC if format_metrics query parameter is
// not supplied. TODO: should be removed for 3.0. should only rely on format_metrics query parameter.
if ($formatMetrics === null
|| $formatMetrics == 'bc'
) {
$prettyValue = self::getPrettyValue($formatter, $idSiteForRow, $columnName, $columnValue, $htmlAllowed = false);
} else {
$prettyValue = $columnValue;
}
$enhancedRow->addColumn($columnName, $prettyValue);
} // For example the Maps Widget requires the raw metrics to do advanced datavis
elseif ($returnRawMetrics) {
else if ($returnRawMetrics) {
if (!isset($columnValue)) {
$columnValue = 0;
}
$enhancedRow->addColumn($columnName, $columnValue);
}
}
@ -715,4 +771,108 @@ class ProcessedReport
return $totals;
}
}
private function getColumnsToRemove()
{
$columnsToRemove = Common::getRequestVar('hideColumns', '');
if ($columnsToRemove != '') {
return explode(',', $columnsToRemove);
}
return null;
}
private function getColumnsToKeep()
{
$columnsToKeep = Common::getRequestVar('showColumns', '');
if ($columnsToKeep != '') {
$columnsToKeep = explode(',', $columnsToKeep);
$columnsToKeep[] = 'label';
return $columnsToKeep;
}
return null;
}
private function buildReportMetadataCacheKey($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports)
{
if (isset($_GET) && isset($_POST) && is_array($_GET) && is_array($_POST)) {
$request = $_GET + $_POST;
} elseif (isset($_GET) && is_array($_GET)) {
$request = $_GET;
} elseif (isset($_POST) && is_array($_POST)) {
$request = $_POST;
} else {
$request = array();
}
$key = '';
foreach ($request as $k => $v) {
if (is_array($v)) {
$key .= $k . $this->getImplodedArray($v) . ',';
} else {
$key .= $k . $v . ',';
}
}
$key .= implode(',', $idSites) . ($period === false ? 0 : $period) . ($date === false ? 0 : $date);
$key .= (int)$hideMetricsDoc . (int)$showSubtableReports . Piwik::getCurrentUserLogin();
return 'reportMetadata' . md5($key);
}
/**
* @param $v
* @return string
*/
private function getImplodedArray($v)
{
return implode(', ', array_map(function ($entry) {
if (is_array($entry)) {
return implode(":", $entry);
}
return $entry;
}, $v));
}
/**
* Prettifies a metric value based on the column name.
*
* @param int $idSite The ID of the site the metric is for (used if the column value is an amount of money).
* @param string $columnName The metric name.
* @param mixed $value The metric value.
* @param bool $isHtml If true, replaces all spaces with `'&nbsp;'`.
* @return string
*/
public static function getPrettyValue(Formatter $formatter, $idSite, $columnName, $value)
{
if (!is_numeric($value)) {
return $value;
}
// Display time in human readable
if (strpos($columnName, 'time_generation') !== false) {
return $formatter->getPrettyTimeFromSeconds($value, true);
}
if (strpos($columnName, 'time') !== false) {
return $formatter->getPrettyTimeFromSeconds($value);
}
// Add revenue symbol to revenues
$isMoneyMetric = strpos($columnName, 'revenue') !== false || strpos($columnName, 'price') !== false;
if ($isMoneyMetric && strpos($columnName, 'evolution') === false) {
return $formatter->getPrettyMoney($value, $idSite);
}
// Add % symbol to rates
if (strpos($columnName, '_rate') !== false) {
if (strpos($value, "%") === false) {
return $value . "%";
}
}
return $value;
}
}

View file

@ -0,0 +1,31 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
class Console extends ApiRenderer
{
public function renderException($message, \Exception $exception)
{
self::sendHeader();
return 'Error: ' . $message;
}
public function sendHeader()
{
Common::sendHeader('Content-Type: text/plain; charset=utf-8');
}
}

View file

@ -0,0 +1,60 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
use Piwik\ProxyHttp;
class Csv extends ApiRenderer
{
public function renderSuccess($message)
{
Common::sendHeader("Content-Disposition: attachment; filename=piwik-report-export.csv");
return "message\n" . $message;
}
public function renderException($message, \Exception $exception)
{
Common::sendHeader('Content-Type: text/html; charset=utf-8', true);
return 'Error: ' . $message;
}
public function renderDataTable($dataTable)
{
$convertToUnicode = Common::getRequestVar('convertToUnicode', true, 'int', $this->request);
$idSite = Common::getRequestVar('idSite', false, 'int', $this->request);
/** @var \Piwik\DataTable\Renderer\Csv $tableRenderer */
$tableRenderer = $this->buildDataTableRenderer($dataTable);
$tableRenderer->setConvertToUnicode($convertToUnicode);
$method = Common::getRequestVar('method', '', 'string', $this->request);
$tableRenderer->setApiMethod($method);
$tableRenderer->setIdSite($idSite);
$tableRenderer->setTranslateColumnNames(Common::getRequestVar('translateColumnNames', false, 'int', $this->request));
return $tableRenderer->render();
}
public function renderArray($array)
{
return $this->renderDataTable($array);
}
public function sendHeader()
{
Common::sendHeader("Content-Type: application/vnd.ms-excel", true);
ProxyHttp::overrideCacheControlHeaders();
}
}

View file

@ -0,0 +1,51 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
class Html extends ApiRenderer
{
public function renderException($message, \Exception $exception)
{
Common::sendHeader('Content-Type: text/plain; charset=utf-8', true);
return nl2br($message);
}
public function renderDataTable($dataTable)
{
/** @var \Piwik\DataTable\Renderer\Html $tableRenderer */
$tableRenderer = $this->buildDataTableRenderer($dataTable);
$tableRenderer->setTableId($this->request['method']);
$method = Common::getRequestVar('method', '', 'string', $this->request);
$tableRenderer->setApiMethod($method);
$tableRenderer->setIdSite(Common::getRequestVar('idSite', false, 'int', $this->request));
$tableRenderer->setTranslateColumnNames(Common::getRequestVar('translateColumnNames', false, 'int', $this->request));
return $tableRenderer->render();
}
public function renderArray($array)
{
return $this->renderDataTable($array);
}
public function sendHeader()
{
Common::sendHeader('Content-Type: text/html; charset=utf-8', true);
}
}

View file

@ -0,0 +1,108 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
use Piwik\Piwik;
use Piwik\ProxyHttp;
/**
* API output renderer for JSON.
*
* **NOTE: This is the old JSON format. It includes bugs that are fixed in the JSON2 API output
* format. Please use that format instead of this.**
*
* @deprecated
*/
class Json extends ApiRenderer
{
public function renderSuccess($message)
{
$result = json_encode(array('result' => 'success', 'message' => $message));
return $this->applyJsonpIfNeeded($result);
}
public function renderException($message, \Exception $exception)
{
$exceptionMessage = str_replace(array("\r\n", "\n"), "", $message);
$result = json_encode(array('result' => 'error', 'message' => $exceptionMessage));
return $this->applyJsonpIfNeeded($result);
}
public function renderDataTable($dataTable)
{
$result = parent::renderDataTable($dataTable);
return $this->applyJsonpIfNeeded($result);
}
public function renderArray($array)
{
if (Piwik::isMultiDimensionalArray($array)) {
$jsonRenderer = Renderer::factory('json');
$jsonRenderer->setTable($array);
$result = $jsonRenderer->render();
return $this->applyJsonpIfNeeded($result);
}
return $this->renderDataTable($array);
}
public function sendHeader()
{
if ($this->isJsonp()) {
Common::sendHeader('Content-Type: application/javascript; charset=utf-8');
} else {
Renderer\Json::sendHeaderJSON();
}
ProxyHttp::overrideCacheControlHeaders();
}
private function isJsonp()
{
$callback = $this->getJsonpCallback();
if (false === $callback) {
return false;
}
return preg_match('/^[0-9a-zA-Z_.]*$/D', $callback) > 0;
}
private function getJsonpCallback()
{
$jsonCallback = Common::getRequestVar('callback', false, null, $this->request);
if ($jsonCallback === false) {
$jsonCallback = Common::getRequestVar('jsoncallback', false, null, $this->request);
}
return $jsonCallback;
}
/**
* @param $str
* @return string
*/
private function applyJsonpIfNeeded($str)
{
if ($this->isJsonp()) {
$jsonCallback = $this->getJsonpCallback();
$str = $jsonCallback . "(" . $str . ")";
}
return $str;
}
}

View file

@ -0,0 +1,33 @@
<?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\API\Renderer;
use Piwik\Piwik;
/**
* Correct API output renderer for JSON. Includes bug fixes for bugs in the old JSON API
* format.
*/
class Json2 extends Json
{
public function renderArray($array)
{
$result = parent::renderArray($array);
// if $array is a simple associative array, remove the JSON root array that is added by renderDataTable
if (!empty($array)
&& Piwik::isAssociativeArray($array)
&& !Piwik::isMultiDimensionalArray($array)
) {
$result = substr($result, 1, strlen($result) - 2);
}
return $result;
}
}

View file

@ -0,0 +1,77 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
class Original extends ApiRenderer
{
public function renderSuccess($message)
{
return true;
}
public function renderException($message, \Exception $exception)
{
throw $exception;
}
public function renderDataTable($dataTable)
{
return $this->serializeIfNeeded($dataTable);
}
public function renderArray($array)
{
return $this->serializeIfNeeded($array);
}
public function renderScalar($scalar)
{
return $scalar;
}
public function renderObject($object)
{
return $object;
}
public function renderResource($resource)
{
return $resource;
}
public function sendHeader()
{
if ($this->shouldSerialize()) {
Common::sendHeader('Content-Type: text/plain; charset=utf-8');
}
}
/**
* Returns true if the user requested to serialize the output data (&serialize=1 in the request)
*
* @return bool
*/
private function shouldSerialize()
{
$serialize = Common::getRequestVar('serialize', 0, 'int', $this->request);
return !empty($serialize);
}
private function serializeIfNeeded($response)
{
if ($this->shouldSerialize()) {
return serialize($response);
}
return $response;
}
}

View file

@ -0,0 +1,82 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
use Piwik\Piwik;
class Php extends ApiRenderer
{
public function renderSuccess($message)
{
$success = array('result' => 'success', 'message' => $message);
return $this->serializeIfNeeded($success);
}
public function renderException($message, \Exception $exception)
{
$message = array('result' => 'error', 'message' => $message);
return $this->serializeIfNeeded($message);
}
public function renderDataTable($dataTable)
{
/** @var \Piwik\DataTable\Renderer\Php $tableRenderer */
$tableRenderer = $this->buildDataTableRenderer($dataTable);
$tableRenderer->setSerialize($this->shouldSerialize(1));
$tableRenderer->setPrettyDisplay(Common::getRequestVar('prettyDisplay', false, 'int', $this->request));
return $tableRenderer->render();
}
public function renderArray($array)
{
if (!Piwik::isMultiDimensionalArray($array)) {
return $this->renderDataTable($array);
}
if ($this->shouldSerialize(1)) {
return serialize($array);
}
return $array;
}
public function sendHeader()
{
Common::sendHeader('Content-Type: text/plain; charset=utf-8');
}
/**
* Returns true if the user requested to serialize the output data (&serialize=1 in the request)
*
* @param mixed $defaultSerializeValue Default value in case the user hasn't specified a value
* @return bool
*/
private function shouldSerialize($defaultSerializeValue)
{
$serialize = Common::getRequestVar('serialize', $defaultSerializeValue, 'int', $this->request);
return !empty($serialize);
}
private function serializeIfNeeded($response)
{
if ($this->shouldSerialize(1)) {
return serialize($response);
}
return $response;
}
}

View file

@ -0,0 +1,51 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
class Rss extends ApiRenderer
{
public function renderException($message, \Exception $exception)
{
self::sendHeader('plain');
return 'Error: ' . $message;
}
public function renderDataTable($dataTable)
{
/** @var \Piwik\DataTable\Renderer\Rss $tableRenderer */
$tableRenderer = $this->buildDataTableRenderer($dataTable);
$method = Common::getRequestVar('method', '', 'string', $this->request);
$tableRenderer->setApiMethod($method);
$tableRenderer->setIdSite(Common::getRequestVar('idSite', false, 'int', $this->request));
$tableRenderer->setTranslateColumnNames(Common::getRequestVar('translateColumnNames', false, 'int', $this->request));
return $tableRenderer->render();
}
public function renderArray($array)
{
return $this->renderDataTable($array);
}
public function sendHeader($type = "xml")
{
Common::sendHeader('Content-Type: text/' . $type . '; charset=utf-8');
}
}

View file

@ -0,0 +1,24 @@
<?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\API\Renderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
class Tsv extends Csv
{
public function renderSuccess($message)
{
Common::sendHeader("Content-Disposition: attachment; filename=piwik-report-export.csv");
return "message\t" . $message;
}
}

View file

@ -0,0 +1,40 @@
<?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\API\Renderer;
use Piwik\API\ApiRenderer;
use Piwik\Common;
use Piwik\DataTable\Renderer;
use Piwik\DataTable;
class Xml extends ApiRenderer
{
public function renderSuccess($message)
{
return "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" .
"<result>\n" .
"\t<success message=\"" . $message . "\" />\n" .
"</result>";
}
public function renderException($message, \Exception $exception)
{
return '<?xml version="1.0" encoding="utf-8" ?>' . "\n" .
"<result>\n" .
"\t<error message=\"" . $message . "\" />\n" .
"</result>";
}
public function sendHeader()
{
Common::sendHeader('Content-Type: text/xml; charset=utf-8');
}
}

View file

@ -0,0 +1,109 @@
<?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\API\Reports;
use Piwik\Piwik;
use Piwik\Plugin\Report;
class Get extends Report
{
/**
* List of Plugin.Get reports that are merged in this one.
*
* @var Report[]
*/
private $reportsToMerge = array();
protected function init()
{
parent::init();
$this->reportsToMerge = $this->getReportsToMerge();
$this->module = 'API';
$this->action = 'get';
$this->category = 'API';
$this->name = Piwik::translate('General_MainMetrics');
$this->documentation = '';
$this->processedMetrics = array();
foreach ($this->reportsToMerge as $report) {
if (!is_array($report->processedMetrics)) {
continue;
}
$this->processedMetrics = array_merge($this->processedMetrics, $report->processedMetrics);
}
$this->metrics = array();
foreach ($this->reportsToMerge as $report) {
if (!is_array($report->metrics)) {
continue;
}
$this->metrics = array_merge($this->metrics, $report->metrics);
}
$this->order = 6;
}
public function getMetrics()
{
$metrics = array();
foreach ($this->reportsToMerge as $report) {
$metrics = array_merge($metrics, $report->getMetrics());
}
return $metrics;
}
public function getProcessedMetrics()
{
$processedMetrics = array();
foreach ($this->reportsToMerge as $report) {
$reportMetrics = $report->getProcessedMetrics();
if (is_array($reportMetrics)) {
$processedMetrics = array_merge($processedMetrics, $reportMetrics);
}
}
return $processedMetrics;
}
/**
* @return Report[]
*/
private function getReportsToMerge()
{
$result = array();
foreach (Report::getAllReportClasses() as $reportClass) {
if ($reportClass == 'Piwik\\Plugins\\API\\Reports\\Get') {
continue;
}
/** @var Report $report */
$report = new $reportClass();
if ($report->getModule() == 'API'
|| $report->getAction() != 'get'
) {
continue;
}
$metrics = $report->getMetrics();
if (!empty($report->parameters)
|| empty($metrics)
) {
continue;
}
$result[] = $report;
}
return $result;
}
}

View file

@ -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
@ -10,17 +10,17 @@ namespace Piwik\Plugins\API;
use Exception;
use Piwik\API\DataTableManipulator\LabelFilter;
use Piwik\API\DataTablePostProcessor;
use Piwik\API\Request;
use Piwik\API\ResponseBuilder;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\DataTable\Filter\CalculateEvolutionFilter;
use Piwik\DataTable\Filter\SafeDecodeLabel;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\Period;
use Piwik\Piwik;
use Piwik\Url;
use Piwik\Site;
use Piwik\Url;
/**
* This class generates a Row evolution dataset, from input request
@ -36,7 +36,7 @@ class RowEvolution
'getPageUrl'
);
public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false)
{
// validation of requested $period & $date
if ($period == 'range') {
@ -48,12 +48,12 @@ class RowEvolution
throw new Exception("Row evolutions can not be processed with this combination of \'date\' and \'period\' parameters.");
}
$label = ResponseBuilder::unsanitizeLabelParameter($label);
$label = DataTablePostProcessor::unsanitizeLabelParameter($label);
$labels = Piwik::getArrayFromApiParameter($label);
$metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal);
$metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal, $idDimension);
$dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal);
$dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal, $idDimension);
if (empty($labels)) {
$labels = $this->getLabelsFromDataTable($dataTable, $labels);
@ -144,7 +144,7 @@ class RowEvolution
$logo = $actualLabel = false;
$urlFound = false;
foreach ($dataTable->getDataTables() as $date => $subTable) {
foreach ($dataTable->getDataTables() as $subTable) {
/** @var $subTable DataTable */
$subTable->applyQueuedFilters();
if ($subTable->getRowsCount() > 0) {
@ -204,10 +204,13 @@ class RowEvolution
$replaceRegex = "/\\s*" . preg_quote(LabelFilter::SEPARATOR_RECURSIVE_LABEL) . "\\s*/";
$cleanLabel = preg_replace($replaceRegex, '/', $label);
return $mainUrlHost . '/' . $cleanLabel . '/';
$result = $mainUrlHost . '/' . $cleanLabel . '/';
} else {
return str_replace(LabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label);
$result = str_replace(LabelFilter::SEPARATOR_RECURSIVE_LABEL, ' - ', $label);
}
// remove @ terminal operator occurances
return str_replace(LabelFilter::TERMINAL_OPERATOR, '', $result);
}
/**
@ -245,7 +248,7 @@ class RowEvolution
* @throws Exception
* @return DataTable\Map|DataTable
*/
private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false)
private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false, $idDimension = false)
{
if (!is_array($label)) {
$label = array($label);
@ -262,6 +265,7 @@ class RowEvolution
'serialize' => '0',
'segment' => $segment,
'idGoal' => $idGoal,
'idDimension' => $idDimension,
// data for row evolution should NOT be limited
'filter_limit' => -1,
@ -276,9 +280,8 @@ class RowEvolution
// note: some reports should not be filtered with AddColumnProcessedMetrics
// specifically, reports without the Metrics::INDEX_NB_VISITS metric such as Goals.getVisitsUntilConversion & Goal.getDaysToConversion
// this is because the AddColumnProcessedMetrics filter removes all datable rows lacking this metric
if( isset($metadata['metrics']['nb_visits'])
&& !empty($label)) {
$parameters['filter_add_columns_when_show_all_columns'] = '1';
if (isset($metadata['metrics']['nb_visits'])) {
$parameters['filter_add_columns_when_show_all_columns'] = '0';
}
$url = Url::getQueryStringFromParameters($parameters);
@ -307,12 +310,15 @@ class RowEvolution
* @throws Exception
* @return array
*/
private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false)
private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false, $idDimension = false)
{
$apiParameters = array();
if (!empty($idGoal) && $idGoal > 0) {
$apiParameters = array('idGoal' => $idGoal);
}
if (!empty($idDimension) && $idDimension > 0) {
$apiParameters = array('idDimension' => (int) $idDimension);
}
$reportMetadata = API::getInstance()->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language,
$period, $date, $hideMetricsDoc = false, $showSubtableReports = true);
@ -328,6 +334,10 @@ class RowEvolution
$metrics = $metrics + $reportMetadata['processedMetrics'];
}
if (empty($reportMetadata['dimension'])) {
throw new Exception(sprintf('Reports like %s.%s which do not have a dimension are not supported by row evolution', $apiModule, $apiAction));
}
$dimension = $reportMetadata['dimension'];
return compact('metrics', 'dimension');
@ -353,9 +363,16 @@ class RowEvolution
unset($metadata['logos']);
$subDataTables = $dataTable->getDataTables();
if (empty($subDataTables)) {
throw new \Exception("Unexpected state: row evolution API call returned empty DataTable\\Map.");
}
$firstDataTable = reset($subDataTables);
$this->checkDataTableInstance($firstDataTable);
$firstDataTableRow = $firstDataTable->getFirstRow();
$lastDataTable = end($subDataTables);
$this->checkDataTableInstance($lastDataTable);
$lastDataTableRow = $lastDataTable->getFirstRow();
// Process min/max values
@ -423,7 +440,7 @@ class RowEvolution
$labelRow, $apiModule, $apiAction, $labelUseAbsoluteUrl);
$prettyLabel = $labelRow->getColumn('label_html');
if($prettyLabel !== false) {
if ($prettyLabel !== false) {
$actualLabels[$labelIdx] = $prettyLabel;
}
@ -526,4 +543,11 @@ class RowEvolution
$label = SafeDecodeLabel::decodeLabelSafe($label);
return $label;
}
private function checkDataTableInstance($lastDataTable)
{
if (!($lastDataTable instanceof DataTable)) {
throw new \Exception("Unexpected state: row evolution returned DataTable\\Map w/ incorrect child table type: " . get_class($lastDataTable));
}
}
}

View file

@ -0,0 +1,5 @@
{
"API": {
"LoadedAPIs": "ማስጋባት ተሳክቷል %s ኤፒአይዎች"
}
}

View file

@ -0,0 +1,12 @@
{
"API": {
"GenerateVisits": "إذا كنت لا تملك بيانات لليوم الحالي، فيمكنك أن تنشئ بعض البيانات باستخدام تطبيق %1$s. يمكنك تفعيل تطبيق %2$s، ثم النقر على قائمة \"مولد الزوا\" في لوحة إدارة Piwik.",
"KeepTokenSecret": "مفتاح المصادقة هذا سري كما هو الحال في اسم المستخدم ولكلمة المرور، %1$s لا تعطه لأحد قط%2$s!",
"LoadedAPIs": "تم تحميل %s واجهة تطبيقات.",
"MoreInformation": "لمزيد من المعلومات حول واجهة التطبيقات لبرنامج Piwik، الرجاء مراجعة %1$s مقدمة إلى واجهة تطبيقات Piwik %2$s وكذلك %3$sدليل واجهة تطبيقات Piwik %4$s.",
"PluginDescription": "يمكن الحصول على جميع بيانات Piwik عبر واجهات برمجية بسيطة. وهذه الإضافة هي نقطة بداية خدمة وب يمكنك استدعاؤها للحصول على بيانات تحليلات وب بصيغ xml ، json ، php ، csv وغيرها",
"TopLinkTooltip": "الوصول إلى تحليلات ويب الخاصة بك برمجياً عبر واجهة تطبيقات بسيطة API على شكل json, xml وغيرها.",
"UserAuthentication": "مصادقة المستخدم",
"UsingTokenAuth": "إذا كنت ترغب في %1$s طلب بيانات من خلال نص برمجي أو Crontab، أو غيرها %2$s فستحتاج إلى إضافة باراميتر %3$s في روابط طلبات API والتي تتطلب المصادقة."
}
}

View file

@ -0,0 +1,10 @@
{
"API": {
"GenerateVisits": "Калі ў вас няма дадзеных на сённяшні дзень, вы можаце стварыць некаторыя дадзеныя, выкарыстоўваючы даданы модуль %1$s. Уключыце даданы модуль %2$s, затым націсніце на \"Генератар наведванняў\" у адміністрацыйнай частцы Piwik.",
"KeepTokenSecret": "Гэта ідэнтыфікацыйны токэн, ён такі жа сакрэтны, як ваш лагін і пароль, %1$s не дзеліцеся ім ня з кім%2$s!",
"LoadedAPIs": "%s API паспяхова загружаны",
"MoreInformation": "Дадатковыя звесткі аб Piwik API, калі ласка, звярніце ўвагу на %1$s Уводзіны ў Piwik API %2$s і %3$s Piwik API спасылкі %4$s.",
"UserAuthentication": "Аўтэнтыфікацыя карыстальніка",
"UsingTokenAuth": "Калі вы жадаеце %1$s запытаць дадзеныя ў рамках скрыпта, кронтаба і г.д. %2$s. Вам патрабуецца дадаць параметр %3$s да API каб выклікаць URL-адрасоў, якія патрабуюць праверкі сапраўднасці."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Ако не разполагате с данни за днес можете да генерирате такива с помощта на добавката %1$s. Вие можете да активирате добавката %2$s, след което натиснете на „Генератор на посещения“, намиращ се в менюто на администраторската среда на Piwik.",
"KeepTokenSecret": "Това token_auth е тайна, като Вашето потребителско име и парола, %1$s не го споделяйте%2$s!",
"LoadedAPIs": "Успешно заредени %s API-та",
"MoreInformation": "За повече информация за Piwik API-тата, моля погледнете %1$s Въведение в Piwik API%2$s и %3$s Piwik API Референт%4$s.",
"TopLinkTooltip": "Информацията за уеб анализите може да бъде достъпена чрез прост приложно-програмен интерфейс в json, xml и др. формат.",
"UserAuthentication": "Удостоверяване на потребителя",
"UsingTokenAuth": "Ако искате да %1$s вмъкнете данните със скрипт, crontab, др. %2$s трябва да добавите параметър %3$s на API кода за повиквания на URL адреси, които изискват удостоверяване."
}
}

View file

@ -0,0 +1,7 @@
{
"API": {
"GenerateVisits": "Ako nemate podataka za danas onda možete prvo generisati neke podatke sa dodatkom %1$s. Ovo možete uraditi ako uključite dodatak %2$s i zatim kliknete na 'Proizvođač posjetilaca' u meniju koja se nalazi u Piwik adminskom prostoru.",
"KeepTokenSecret": "Ovaj token_auth je povjerljiv podatak poput vašeg korisničkog imena i lozine. %1$s Nemojte ga dijeliti sa drugima%2$s!",
"LoadedAPIs": "Uspješno učitani %s API(-ovi)"
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Si no disposeu d'informació d'avui podeu generar informació utilitzant l'extensió: %1$s. Heu d'activar l'extensió %2$s i desprès anar al menú 'Generador de visites' de l'espai d'administració del Piwik.",
"KeepTokenSecret": "El token_auth es tan secret com el vostre usuari i la vostra contrasenya, %1$s no compartiu el seu %2$s!",
"LoadedAPIs": "S'ha carregat correctament un total de %s API",
"MoreInformation": "Per mes informació sobre les APIs de Piwik, siusplau reviseu %1$s Introducció a l'API de Piwik %2$s i %3$s la Referència de l'API de Piwik %4$s.",
"TopLinkTooltip": "Accediu a la vostra informació de l'anàlisis Web d'una forma programada a través d'una API simple en json, xml, etc.",
"UserAuthentication": "Autentificació de l'usuari",
"UsingTokenAuth": "Si voleu %1$s obtenir informació a través d'un script, un crontab, etc %2$s heu d'afegir el paràmetre %3$s a les crides a la APU per les URLs que requereixen autentificació."
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "Pokud pro dnešek nemáte data, můžete je nejprve vygenerovat pomocí zásuvného modulu %1$s. Můžete povolit zásuvný modul %2$s a poté kliknout na 'Generátor návštěv' v menu v administrační části Piwiku",
"KeepTokenSecret": "Tento token_auth je tajný jako vaše uživatelské jméno a heslo, %1$s neříkejte jej nikomu jinému %2$s!",
"LoadedAPIs": "Úspěšně načteno %s API",
"MoreInformation": "Pro více informací o API Piwiku se podívejte na %1$s Úvod do API Piwiku %2$s a %3$s Referenci API Piwiku %4$s",
"PluginDescription": "Všechna data v Piwiku jsou dostupná pomocí jednoduchých API. Tento zásuvný modul je vstupním bodem těchto webových služeb, který vám umožňuje získat vaše analytická data jako XML, JSON, CSV, PHP atd.",
"ReportingApiReference": "Reference API hlášení",
"TopLinkTooltip": "Zpřístupněte svoje Webové analýzy programově skrze jednoduché API pomocí json, xml a dalších.",
"UserAuthentication": "Autentifikace uživatele",
"UsingTokenAuth": "Pokud chcete %1$s načíst data ze skriptu, cronu, atd. %2$s Potřebujete přidat parametr %3$s k voláním API, které vyžadují přihlášení",
"Glossary": "Glosář",
"LearnAboutCommonlyUsedTerms": "Zjistěte víc o nejčastějších termínech, abyste mohli využít Piwik naplno %1$s a %2$s."
}
}

View file

@ -0,0 +1,12 @@
{
"API": {
"GenerateVisits": "Hvis du ikke har data for i dag ,kan du generere nogle data ved hjælp af %1$s programudvidelsen. Aktiver %2$s programudvidelsen, og klik derefter på 'Besøgsgenerator' i menuen under indstillinger.",
"KeepTokenSecret": "Token_auth er ligeså hemmeligt som brugernavn og adgangskode, %1$sdel det ikke ud%2$s!",
"LoadedAPIs": "%s APIs indlæst",
"MoreInformation": "Mere information om Piwik API'er, findes på %1$sIntroduktion til Piwik API%2$s og %3$sPiwik API Reference%4$s.",
"PluginDescription": "Alle data i Piwik er tilgængelig gennem enkle API'er. Denne udvidelse er en webservice indgang, som kaldes for at få Web Analytics-data i xml, JSON, php, csv etc.",
"TopLinkTooltip": "Få adgang til dine webanalyse data programmeringsmæssigt gennem en simpel API i JSON, XML, etc.",
"UserAuthentication": "Brugergodkendelse",
"UsingTokenAuth": "Hvis du ønsker at %1$s hente data i et script, med crontab, m.m. %2$s skal du tilføje parameteren %3$s til API-kaldes netadresse, som kræver godkendelse."
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "Sollten für heute keine Daten verfügbar sein, so können Sie mit dem %1$s Plugin Testdaten erzeugen. Aktivieren Sie das %2$s Plugin und klicken dann auf das 'Besuchergenerator' Menu in der Piwik-Administration.",
"KeepTokenSecret": "Der token_auth ist so geheim wie Ihr Login und Passwort, %1$s teilen Sie es niemandem mit%2$s!",
"LoadedAPIs": "%s APIs erfolgreich geladen",
"MoreInformation": "Für weitere Informationen über die Piwik-APIs lesen Sie bitte %1$s Einführung in die Piwik-API %2$s und die %3$s Piwik-API-Referenz %4$s.",
"PluginDescription": "Alle Daten in Piwik sind über einfache APIs verfügbar. Dieses Plugin ist der Web Service Eingangspunkt, welchen Sie nutzen können um Daten der Webanalyse in XML, JSON, PHP, CSV usw. abzufragen.",
"ReportingApiReference": "Reporting-API-Referenz",
"TopLinkTooltip": "Greife auf die Webanalytikdaten über eine einfache API mit json, xml, usw. zu.",
"UserAuthentication": "Benutzerauthentifizierung",
"UsingTokenAuth": "Wenn Sie %1$s Daten mit einem Script, einem Crontab, etc. abrufen wollen, %2$s müssen Sie den Parameter %3$s an die URLs anhängen, deren API-Aufrufe eine Authentifizierung benötigen.",
"Glossary": "Glossar",
"LearnAboutCommonlyUsedTerms": "Lernen Sie mehr über die häufig verwendeten Begriffe um das Beste aus Ihren Piwik Analysen zu machen: %1$s und %2$s"
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "Αν δεν έχετε δεδομένα για σήμερα μπορείτε να δημιουργήσετε χρησιμοποιώντας το πρόσθετο %1$s. Μπορείτε να ενεργοποιήσετε το πρόσθετο %2$s, μετά να επιλέξετε το μενού «Παραγωγή Επισκεπτών» στην περιοχή Διαχείρισης του Piwik.",
"KeepTokenSecret": "Αυτό το token_auth είναι μυστικό όπως και το όνομα χρήστη και συνθηματικό σας, %1$sγια το λόγο αυτό, μην το δίνετε πουθενά%2$s!",
"LoadedAPIs": "Φορτώθηκαν επιτυχώς %s APIs",
"MoreInformation": "Για περισσότερες πληροφορίες σχετικά με τα APIs του Piwik, δείτε στην %1$s Εισαγωγή για το Piwik API %2$s και την %3$s Παραπομπή για το Piwik API %4$s.",
"PluginDescription": "Όλα τα δεδομένα στο Piwik είναι διαθέσιμα μέσω απλών API. Το πρόσθετο αυτό είναι ένα σημείο εισόδου μιας υπηρεσίας ιστού, που μπορείτε να καλείτε για να λαμβάνετε τα δεδομένα αναλυτικών σας σε μορφή xml, json, php, csv, κτλ.",
"ReportingApiReference": "Παραπομπή API Αναφορών",
"TopLinkTooltip": "Προσπελάστε τα δεδομένα Στατιστικών Ιστού προγραμματιστικά μέσω μιας απλής εφαρμογής σε json, xml, κλπ.",
"UserAuthentication": "Πιστοποίηση χρήστη",
"UsingTokenAuth": "Αν θέλετε να %1$s ζητήσετε δεδομένα από ένα σενάριο, μια εργασία crontab, κτλ. %2$s χρειάζεται να προσθέσετε την παράμετρο %3$s στα URLs των κλήσεων API που απαιτούν αυθεντικοποίηση.",
"Glossary": "Γλωσσάρι",
"LearnAboutCommonlyUsedTerms": "Δείτε περισσότερα σχετικά με συχνά χρησιμοποιούμενους όρους για να εκμεταλλευτείτε στο έπακρο το Piwik Analytics: %1$s και %2$s."
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "If you don't have data for today you can first generate some data using the %1$s plugin. You can enable the %2$s plugin, then click on the 'Visitor Generator' menu in the Piwik Admin area.",
"KeepTokenSecret": "This token_auth is as secret as your login and password, %1$s do not share it%2$s!",
"LoadedAPIs": "Successfully loaded %s APIs",
"MoreInformation": "For more information about the Piwik APIs, please have a look at the %1$s Introduction to Piwik API %2$s and the %3$s Piwik API Reference %4$s.",
"PluginDescription": "All the data in Piwik is available through simple APIs. This plugin is the web service entry point, that you can call to get your Web Analytics data in xml, json, php, csv, etc.",
"ReportingApiReference": "Reporting API Reference",
"TopLinkTooltip": "Access your Web Analytics data programmatically through a simple API in json, xml, etc.",
"UserAuthentication": "User authentication",
"UsingTokenAuth": "If you want to %1$s request data within a script, a crontab, etc. %2$s you need to add the parameter %3$s to the API calls URLs that require authentication.",
"Glossary": "Glossary",
"LearnAboutCommonlyUsedTerms": "Learn about the commonly used terms to make the most of Piwik Analytics: %1$s and %2$s."
}
}

View file

@ -0,0 +1,13 @@
{
"API": {
"GenerateVisits": "Si no tiene los datos de hoy puede generar primero algunos datos mediante el complemento %1$s. Puede habilitar el complemento %2$s, a continuación, haga clic en el menú 'Generador de Visitas' en la area Administrativa de Piwik.",
"KeepTokenSecret": "Este token_auth es tan secreto como su nombre de usuario y contraseña, %1$s no lo comparta %2$s!",
"LoadedAPIs": "Se han cargado %s APIs exitosamente",
"MoreInformation": "Para mayor información acerca de las APIs de Piwik, por favor lea la %1$s Introducción a las API Piwik %2$s y la %3$s Referencia de las API Piwik %4$s.",
"PluginDescription": "Toda la información en Piwik está disponible a través de simples APIs. Este complemento es el punto de entrada del servicio de internet, al que puede convocar para obtener la información analítica de internet en formato xml, json, php, csv, etc.",
"ReportingApiReference": "Informando Referencia API",
"TopLinkTooltip": "Acceda a sus datos de análisis de internet programáticamente, a través de una sencilla API en json, xml, etc.",
"UserAuthentication": "Autenticación de usuario",
"UsingTokenAuth": "Si quieres %1$s solicitar datos dentro de un script, un crontab, etc. %2$s tiene que agregar el parámetro %3$s a las llamadas a la URL de la API que requieren autenticación."
}
}

View file

@ -0,0 +1,7 @@
{
"API": {
"LoadedAPIs": "Edukalt laetud %s API-id",
"MoreInformation": "Et saada rohkem infot Piwiku API-st, vaata %1$sPiwiku API tutvustus%2$s ja %3$sPiwiku API juhend%4$s.",
"UserAuthentication": "Kasutaja autentimine"
}
}

View file

@ -0,0 +1,5 @@
{
"API": {
"LoadedAPIs": "%s API ondo kargatu dira"
}
}

View file

@ -0,0 +1,8 @@
{
"API": {
"KeepTokenSecret": "این token_auth برای رمزعبور و ورود شما مثل یک راز است , %1$s آن را به کسی نگویید %2$s!",
"LoadedAPIs": "API های %s با موفقیت بارگزاری شدند",
"TopLinkTooltip": "با استفاده از یک API ساده به اطلاعات آماری وب خود از طریق کدنویسی در فرمت های json و xml و غیره دسترسی پیدا کنید.",
"UserAuthentication": "تأیید هویت کاربر"
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Jos sinulla ei ole yhtään tietoja tälle päivälle, voit generoida satunnaisia tietoja käyttämällä lisäosaa %1$s. Voit ottaa käyttöön %2$s-lisäosan ja painaa 'Vierailijoiden luominen'-linkkiä Piwikin ylläpitosivulla.",
"KeepTokenSecret": "Tämä token_auth on yhtä salainen kuin salasanasi, %1$s älä jaa sitä%2$s!",
"LoadedAPIs": "Ladattiin %s APIa",
"MoreInformation": "Lisätietoa Piwikin APIsta löytyy sivulta %1$sJohdatus Piwikin APIin%2$s ja %3$sPiwikin API%4$s.",
"TopLinkTooltip": "Hae analytiikkatietoja automaattisesti yksinkertaisella API:lla JSON:lla, XML:llä jne.",
"UserAuthentication": "Käyttäjän autentikointi",
"UsingTokenAuth": "Jos haluat %1$s hakea tietoja skriptillä, crontabista jne. %2$s sinun täytyy lisätä parametri %3$s API-kutsujen osoitteisiin, jos kutsu vaatii autentikoinnin."
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "Si vous n'avez pas de données pour aujourd'hui vous pouvez en générer en utilisant le plugin %1$s. Vous pouvez activer le plugin %2$s puis cliquer sur le menu 'Générateur de visites' dans la zone d'administration de Piwik.",
"KeepTokenSecret": "Cette clef partagée (tocken_auth) est aussi secrète que votre login et mot de passe, %1$s ne la partagez pas%2$s !",
"LoadedAPIs": "%s API chargées avec succès",
"MoreInformation": "Pour plus d'informations à propos de l'API de Piwik, merci de visiter %1$s l'introduction à Piwik %2$s et %3$s la référence de l'API Piwik %4$s.",
"PluginDescription": "Toutes les données de Piwik sont disponibles via des API simples. Ce composant est le service web que vous pouvez appeler afin d'obtenir vos données d'analyse web en xml, json, php, csv, etc",
"ReportingApiReference": "Référence de l'API de rapports",
"TopLinkTooltip": "Accédez à vos données de statistiques web depuis votre code via une API simple en JSON, XML, Etc.",
"UserAuthentication": "Authentification de l'utilisateur",
"UsingTokenAuth": "Si vous souhaitez %1$s récupérer des données depuis un script, un crontab, etc %2$s vous devez ajouter le paramètre %3$s aux appels d'URLs de l'API qui requièrent une authentification.",
"Glossary": "Glossaire",
"LearnAboutCommonlyUsedTerms": "En savoir plus sur les termes les plus couramment utilisés composant principalement Piwik Analytics : %1$s et %2$s."
}
}

View file

@ -0,0 +1,6 @@
{
"API": {
"LoadedAPIs": "%s APIs foro cargadas con éxito",
"UserAuthentication": "Autenticación do usuario"
}
}

View file

@ -0,0 +1,10 @@
{
"API": {
"GenerateVisits": "אם אין לך מידע להיום ניתן קודם כל לחולל מידע בעזרת התוסף %1$s. ניתן להפעיל את התוסף %2$s, ולאחר מכן לחולל מידע על מבקרים דרך פאנל הניהול של Piwik.",
"KeepTokenSecret": "הtoken_auth סודי ביותר כמו שם המשתמש והסיסמה, %1$s אין לשתף אף אחד בפרטים אלו%2$s!",
"LoadedAPIs": "%s APIים נטענו בהצלחה",
"MoreInformation": "למידע נוסף עבור הAPIים של Piwik, מומלץ להציץ ב%1$sהיכרות עם הAPI של Piwik%2$s וגם ב%3$sהעמקה אודות הAPI של Piwik%4$s.",
"UserAuthentication": "אימות משתמש",
"UsingTokenAuth": "אם ברצונך %1$s לדרוש מידע מתוך סקריטפ, עבודה כרונית וכד' %2$s יש צורך להוסיף את הפרמטר %3$s לכל קריאת API מהURLים שדורשים אימות."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "आप आज के लिए डेटा नहीं है, तो आप पहले %1$s प्लगइन का उपयोग कर कुछ डेटा उत्पन्न कर सकते हैं. आप %2$s प्लगइन सक्षम कर सकते हैं, तो Piwik व्यवस्थापक क्षेत्र में 'आगंतुक जेनरेटर' मेनू पर क्लिक करें.",
"KeepTokenSecret": "इस टोकन प्रमाणन अपने लॉगिन और पासवर्ड के रूप में गुप्त रूप है, %1$s %2$s यह साझा नहीं करते हैं!",
"LoadedAPIs": "सफलतापूर्वक लोड %s एपीआई",
"MoreInformation": "Piwik एपीआई के बारे में अधिक जानकारी के लिए,कृपया %1$s Piwik एपीआई %2$s और %3$s Piwik एपीआई संदर्भ %4$s के परिचय पर एक नजर डाले",
"TopLinkTooltip": "Json, xml, आदि में एक सरल एपीआई के माध्यम से प्रोग्राम के रूप में अपने वेब विश्लेषिकी डेटा का उपयोग करे",
"UserAuthentication": "प्रयोगकर्ता का प्रामाणीकरण",
"UsingTokenAuth": "आप एक स्क्रिप्ट के भीतर %1$s डेटा, एक crontab, आदि %2$s के लिए अनुरोध करना चाहते हैं तो प्रमाणीकरण की आवश्यकता है कि यूआरएल कॉल आप एपीआई के लिए पैरामीटर %3$s जोड़ने की जरूरत है."
}
}

View file

@ -0,0 +1,6 @@
{
"API": {
"GenerateVisits": "Ako ne postoje podaci za danas, možete ih kreirati pomoću %1$s plugina. Prvo uključite %2$s plugin, zatim koristite opciju 'Kreiraj posjetitelje' u izborniku Piwik administracije.",
"LoadedAPIs": "%s API je uspješno učitan"
}
}

View file

@ -0,0 +1,10 @@
{
"API": {
"GenerateVisits": "Ha nem volt még ma látogató, automatikusan generálhatsz mintaadatokat a %1$s kiegészítő segítségével. A %2$s kiegészítőt a Piwik adminisztrációs felületén megjelenő Látogatógenerálás menüpontra kattintva engedélyezheted.",
"KeepTokenSecret": "Ez a token_auth nevű kód pontosan annyira érzékeny adat mint a felhasználói neved és jelszavad, ezért %1$s ne oszd meg mindenkivel%2$s!",
"LoadedAPIs": "A(z) %s API sikeresen betöltődött.",
"MoreInformation": "További információkért a Piwik API-kkal kapcsolatban kérjük, tekintse meg a %1$s Introduction to Piwik API %2$s és a %3$s Piwik API Reference %4$s című leírásokat.",
"UserAuthentication": "Felhasználó autentikációja",
"UsingTokenAuth": "Ha azt szeretné, hogy a %1$s külső alkalmazás adatot kérjen a szkripten belül, a crontab segítségével, stb. %2$s hozzá kell adnia a %3$s paramétert az API hívásoknál használt URL-ekhez, ha autentikáció szükséges számukra."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Bila Anda tidak memiliki data untuk hari ini, Anda dapat membuat beberapa menggunakan pengaya %1$s. Anda dapat mengaktifkan pengaya %2$s, lalu klik menu 'Pembangkit Kunjungan' di halaman Pengurus Piwik.",
"KeepTokenSecret": "token_auth ini merupakan hal yang rahasia sebagaimana nama-id dan sandi Anda, %1$s sehingga jangan memberitahukannya%2$s!",
"LoadedAPIs": "Berhasil memuat API %s",
"MoreInformation": "Untuk informasi selengkapnya tentang API Piwik, silakan melihat %1$s Pengenalan API Piwik%2$s dan %3$sReferensi API Piwik%4$s.",
"TopLinkTooltip": "Akses data Analisis Ramatraya terprogram melalui API sederhana dalam json, xml, dan lain lain.",
"UserAuthentication": "Otentikasi pengguna",
"UsingTokenAuth": "Jika Anda ingin melakukan permintaan data %1$s menggunakan skrip, crontab, dll. %2$s Anda harus menambah parameter %3$s agar API memanggil URL yang membutuhkan otentikasi."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Ef þú hefur ekki gögn dagsins í dag verður þú fyrst að búa til gögn með að nota %s íbótina. Virkjaðu %s íbótina og smelltu síðan á valmyndinni 'Gesta framleiðni' í Piwik umsjónarmanns svæðinu.",
"KeepTokenSecret": "Þessi tókar auðkenning er eins leynd og þitt notendanafn og lykilorð %s deilið því ekki%s!",
"LoadedAPIs": "Tókst að hlaða %s API",
"MoreInformation": "Fyrir frekari upplýsingar um Piwik API, Vinsamlegast skoðið %s Inngangur að Piwik API %s og %s Piwik API Tilvísunir %s.",
"QuickDocumentationTitle": "API hraðskjöl.",
"UserAuthentication": "Notenda auðkenning",
"UsingTokenAuth": "Ef þú vilt að %s biðji um gögn inn í skriftu eða crontab færslu osfv. %s verður þú að bæta við færibreytu %s til API kalls vefslóðir sem krefst auðkenningar."
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "Se oggi non hai ricevuto visite, le puoi generare usando il plugin %1$s. Abilita il plugin %2$s, quindi clicca su \"Generatore di Visite\" nell'area amministrazione Piwik.",
"KeepTokenSecret": "Questo token_auth è segreto come il tuo login e la tua password, %1$s non condividerlo %2$s!",
"LoadedAPIs": "Sono state caricate con successo %s API",
"MoreInformation": "Per ulteriori informazioni sulle API di Piwik, si prega di dare un'occhiata all'introduzione delle API di Pwik %1$s e a %2$s %3$s Piwik API di riferimento API %4$s.",
"PluginDescription": "Tutti i dati di Piwik sono disponibili tramite semplici API. Questo plugin è la porta d'accesso al servizio web che puoi utilizzare per avere i dati delle tue statistiche web in xml, json, php, csv, ecc.",
"ReportingApiReference": "Segnalazione Riferimenti API",
"TopLinkTooltip": "Accedi ai tuoi dati di Web Analytics tramite le semplici API in JSON, XML, ecc",
"UserAuthentication": "Autenticazione utente",
"UsingTokenAuth": "Se si vogliono i dati di richiesta %1$s all'interno di uno script, un crontab, ecc %2$s è necessario aggiungere il parametro %3$s per le chiamate API URL che richiedono l'autenticazione.",
"Glossary": "Glossario",
"LearnAboutCommonlyUsedTerms": "Impara i termini comunemente usati per sfruttare al meglio Piwik Analytics: %1$s e %2$s."
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "今日のデータがない場合は、%1$s プラグインを使用して、はじめに若干のデータを生成することができます。 これを行うには、%2$s プラグインを有効にし、管理エリアで 'ビジタージェネレータ' メニューをクリックします。",
"KeepTokenSecret": "token_auth は、ログイン名とパスワードのように秘密にし、%1$s絶対に共有しないでください%2$s",
"LoadedAPIs": "%s API が正常に読み込まれました",
"MoreInformation": "Piwik API の詳細については、%1$sIntroduction to Piwik API%2$s や %3$sPiwik API Reference%4$s を参照してください。",
"PluginDescription": "Pwik の全てのデータは簡単な API を通して利用することができます。このプラグインはウェブサービスの入り口で、xml , json , php , csv などのあなたのウェブ分析データを入手するために呼び出すことができます。",
"ReportingApiReference": "API リファレンスを報告",
"TopLinkTooltip": "jsopn、xml等シンプルなAPIを介して、プログラムで分析データにアクセスできます",
"UserAuthentication": "ユーザー認証",
"UsingTokenAuth": "%1$sスクリプトcrontab 等)でリクエストデータを得たい場合%2$sは、API をコールする URL認証が必要にパラメータ %3$s を付加する必要があります。",
"Glossary": "用語集",
"LearnAboutCommonlyUsedTerms": "Piwik Analytics の最もよく使われる用語について: %1$s と %2$s"
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "თუ დღეისთვის არ გაქვთ მონაცემები, შეგიძლიათ ჯერ %s პლაგინის გამოყენებით დააგენერიროთ რამდენიმე მონაცემი. შეგიძლიათ ჩართოთ პლაგინი %s, შემდეგ Piwik ადმინისტრირების ზონაში დააწკაპუნოთ \"ვიზიტორების გენერატორი\" მენიუზე.",
"KeepTokenSecret": "ეს token_auth ისევე საიდუმლოა, როგორც თქვენი მომხმარებლის სახელი და პაროლი, %s არავის გაუზიაროთ ის%s!",
"LoadedAPIs": "%s API ფუნქციები წარმატებით ჩაიტვირთა",
"MoreInformation": "Piwik API ფუნქციების შესახებ დამატებითი ინფორმაციისთვის გთხოვთ, გადახედოთ მასალებს %s Piwik API ფუნქციბის გამოყენების ინსტრუქცია %s და %s Piwik API ფუნქციების ცნობარი %s.",
"QuickDocumentationTitle": "API ფუნქციების მოკლე დოკუმენტაცია",
"UserAuthentication": "მომხმარებლის აუტენთიფიკაცია",
"UsingTokenAuth": "თუ გსურთ %s გააკეთოთ მონაცემების მოთხოვნა სკრიპტიდან, crontab ფაილიდან და სხვ. %s თქვენ უნდა დაამატოთ %s პარამეტრი API ფუნქციის გამოძახების URLებს, რაც მოითხოვს აუტენთიფიკაციას."
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "오늘에 해당하는 데이터가 없는 경우는 %1$s 플러그인을 사용하여 처음 약간의 데이터를 생성할 수 있습니다. %2$s 플러그인을 활성화하고 관리 영역에서 '방문자 생성기'메뉴를 클릭하세요.",
"KeepTokenSecret": "token_auth는 외부 로그인을 위한 비밀키입니다. %1$s 절대로 공유하지 마세요 %2$s!",
"LoadedAPIs": "성공적으로 %s API를 불러옴",
"MoreInformation": "Piwik API에 대한 자세한 내용은 %1$sIntroduction to Piwik API %2$s 문서와 %3$sPiwik API Reference%4$s 문서를 참조하세요.",
"PluginDescription": "Piwik 내 모든 데이터는 간단한 API를 통해서 접근할 수 있습니다. 이 플러그인은 웹 분석 데이터를 xml, json, php, csv 등의 형태로 받을 수 있는 웹 서비스 진입로입니다.",
"ReportingApiReference": "API 레퍼런스 보고",
"TopLinkTooltip": "JSON, XML 등의 간단한 API를 통해 프로그래밍 방식으로 웹 로그 분석 데이터에 접근할 수 있습니다.",
"UserAuthentication": "사용자 인증",
"UsingTokenAuth": "%1$s 스크립트 (crontab 등)에서 요청 데이터를 얻고 싶다면 %2$s는 API를 호출하는 URL (인증 필요)에 매개 변수 %3$s를 추가해야합니다.",
"Glossary": "용어",
"LearnAboutCommonlyUsedTerms": "Piwik 분석에 있어 자주 사용되는 용어 %1$s 와 %2$s 배우기"
}
}

View file

@ -0,0 +1,10 @@
{
"API": {
"GenerateVisits": "Jei neturite duomenų šiandienai , galite sugeneruoti jų pasinaudoję %1$s papildiniu. Įjunkite šį %2$s papildinį, tada spauskite ant 'Apsilankymų generatorius' meniu Piwik administratoriaus srityje.",
"KeepTokenSecret": "Šis token_auth yra slaptas kaip ir naudotojo vardas bei slaptažodis, %1$s neviešinkite jo%2$s!",
"LoadedAPIs": "Sėkmingai įkrautos API sąsajos (%s).",
"MoreInformation": "Norinčius gauti daugiau informacijos apie Piwik API sąsajas, prašome žvilgtelėti į %1$s Įvadas į Piwik API %2$s ir %3$s Piwik API informacija %4$s.",
"UserAuthentication": "Naudotojo autentifikavimas",
"UsingTokenAuth": "Jei norite %1$s gauti duomenis scenarijų, crontab ir pan. įrankių pagalba, %2$s turite pridėti parametrus %3$s į API užklausų URLs, kurios reikalauja autentifikacijos."
}
}

View file

@ -0,0 +1,5 @@
{
"API": {
"UserAuthentication": "Lietotāja autentifikācija"
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "Hvis du ikke har data for i dag kan du først generere noe data med utvidelsen %1$s. Du kan aktivere %2$s, så klikker du på «Besøksgenerator»-menyen i Piwik admin.",
"KeepTokenSecret": "Denne token_auth er like hemmelig som ditt brukernavn og passord. %1$sIkke del den!%2$s",
"LoadedAPIs": "Lastet %s API-er",
"MoreInformation": "For mer informasjon om Piwik API-er, vennligst se på %1$s Introduction to Piwik API %2$s og %3$s Piwik API Reference %4$s.",
"PluginDescription": "Alle data i Piwik er tilgjengelig gjennom enkle API-er. Denne utvidelsen er web-tjenestens inngangspunkt, som du kan kalle opp for å få ut dine data i xml, json, php, csv, etc.",
"ReportingApiReference": "Referanse for rapporterings-API",
"TopLinkTooltip": "Få tilgang til dine analysedata programmatisk gjennom en enkel API i json, xml, etc.",
"UserAuthentication": "Brukerautentisering",
"UsingTokenAuth": "Hvis du vil %1$s spørre etter data i et script, en crontab, etc. %2$s må du legge til parameteret %3$s til API-spørringer som krever autentisering.",
"Glossary": "Ordliste",
"LearnAboutCommonlyUsedTerms": "Lær mer om vanlige termer for å få mest mulig ut av Piwik Analytics: %1$s og %2$s."
}
}

View file

@ -0,0 +1,13 @@
{
"API": {
"GenerateVisits": "Als u geen data hebt voor vandaag, kunt u eerst wat data genereren met de %1$s plugin. U kunt de %2$s plugin inschakelen, en dan klikken op het Bezoekers generator menu in het Piwik beheer gedeelte.",
"KeepTokenSecret": "Deze token_auth is even geheim als uw gebruikersnaam en wachtwoord, %1$sdeel het met niemand%2$s!",
"LoadedAPIs": "%s API's succesvol geladen",
"MoreInformation": "Voor meer informatie over de Piwik API's, lees even de %1$s introductie van de Piwik API %2$s en de %3$s Piwik API referenties %4$s.",
"PluginDescription": "Alle data in Piwik is beschikbaar via eenvoudige API's. Deze plugin is het web service contactpunt, waarmee je je Web Analytics data in xml, json, php, csv, etc. kunt verkrijgen.",
"ReportingApiReference": "Rapportage API verwijzing",
"TopLinkTooltip": "Benader je Web Analyse data via een simpele API in josn, xml, enz.",
"UserAuthentication": "Gebruikers authenticatie.",
"UsingTokenAuth": "Als u data wil %1$s opvragen via een script, een cronjob, enz. waarvoor authenticatie vereist is, %2$s moet u de parameter %3$s aan de API aanvraag URL's toevoegen"
}
}

View file

@ -0,0 +1,5 @@
{
"API": {
"LoadedAPIs": "Lasta vellykka %s APIar"
}
}

View file

@ -0,0 +1,13 @@
{
"API": {
"GenerateVisits": "Jeżeli nie posiadasz żadnych danych do dnia dzisiejszego, możesz na początek wygenerować trochę symulowanych danych testowych używając wtyczki o nazwie %1$s. Możesz aktywować i uruchomić wtyczkę %2$s, a następnie kliknąć w menu przycisk Potwierdź w 'generatorze odwiedzin', w strefie menu Konfiguracja administratora systemu Piwik.",
"KeepTokenSecret": "Ten token_auth jest tak samo tajny jak twój login i hasło, %1$s nie upowszechniaj go%2$s!",
"LoadedAPIs": "Środowisko API załadowano %s pomyślnie",
"MoreInformation": "Aby uzyskać więcej informacji o interfejsie API statystyk Piwik, prosimy przeczytać %1$s Wprowadzenie do interfejsu API w Piwik%2$s, a także na stronie opracowania %3$s Piwik API Reference%4$s.",
"PluginDescription": "Wszystkie dane w Piwiku są dostępne poprzez proste API. Ten plugin jest punktem końcowym serwisu web,",
"TopLinkTooltip": "Dostęp do twoich danych analitycznych programistycznie poprzez proste API w formatach json, xml itp.",
"UserAuthentication": "Uwierzytelnianie użytkownika",
"UsingTokenAuth": "Jeżeli wysuniesz żądanie %1$s ujęcia pewnych danych w statystyce za pomocą crontab, %2$s musisz dodać poniższy parametr %3$s do interfejsu API, wywołując URL-e które wymagają uwierzytelniania.",
"Glossary": "Słownik"
}
}

View file

@ -0,0 +1,15 @@
{
"API": {
"GenerateVisits": "Se você não tem nenhum dado hoje, primeiro você pode gerar alguns dados usando o plugin %1$s. Basta ativar o plugin %2$s e clicar no menu \"Gerador de Visitantes\" na área de Admin do Piwik.",
"KeepTokenSecret": "Esse token_auth é tão secreto quanto seu login e sua senha, %1$s não compartilhe isso %2$s!",
"LoadedAPIs": "%s APIs carregadas com sucesso",
"MoreInformation": "Para mais informações sobre as APIs Piwik, por favor, dê uma olhada no %1$s Introdução para Piwik API %2$s e a %3$s API Piwik de Referência %4$s.",
"PluginDescription": "Todos os dados em Piwik estão disponíveis através da APIs simples. Este plugin é o ponto de entrada do serviço web, que você pode chamar para obter seus dados de Web Analytics em XML, JSON, php, CSV, etc.",
"ReportingApiReference": "Reportando Referência do API",
"TopLinkTooltip": "Acesse seus dados de Análise Web de forma programática através de uma API simples em json, xml, etc.",
"UserAuthentication": "Autenticação de usuário",
"UsingTokenAuth": "Se você quer %1$s requisitar dados dentro de um script, um crontab, etc. %2$s você precisa adicionar o parâmetro %3$s para a API chamar URLS que requerem autenticação.",
"Glossary": "Glossário",
"LearnAboutCommonlyUsedTerms": "Saiba mais sobre os termos comumente utilizados para tirar o máximo do Piwik Analytics: %1$s e %2$s."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Se não tiver dados para hoje pode gerar alguns através do plugin %1$s. Pode activar o plugin %2$s, e depois clicar no menu 'Gerador de Visitantes' na área de Administração do Piwik.",
"KeepTokenSecret": "Este token_auth é tão secreto como o seu nome de utilizador e palavra-passe. %1$s não o partilhe%2$s!",
"LoadedAPIs": "%s APIs carregadas com sucesso",
"MoreInformation": "Para mais informação sobre os APIs de Piwik, por favor dê uma vista de olhos na %1$s Introdução ao API Piwik %2$s e a %3$s Referência do API Piwik %4$s.",
"TopLinkTooltip": "Aceda programaticamente à sua informação através de uma simples API em json, xml, etc.",
"UserAuthentication": "Autenticação do utilizador",
"UsingTokenAuth": "Se quer %1$s pedir dados através de um script, crontab, etc. %2$s tem que adicionar o parâmetro %3$s aos URLs das chamadas do API que necessitam de autenticação."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Daca nu ai date pentru astazi, prima data poti genera date folosind pluginul %1$s. Poti activa pluginul %2$s, apoi apasa pe meniul 'Visitor Generator' in aria de administrare Piwik Admin.",
"KeepTokenSecret": "Acest token_auth este secret cum sunt login-ul si parola, %1$s nu le fa publice%2$s!",
"LoadedAPIs": "%s API-uri încarcate cu succes",
"MoreInformation": "Pentru mai multe informatii despre API-urile Piwik, va rugam sa va uitati la %1$s Introducere la Piwik API %2$s si la %3$s Referinte Piwik API %4$s.",
"TopLinkTooltip": "Acceseaza datele tale de Web Analytics in mod organizat prin intermediul unui simplu API in json, xml, etc.",
"UserAuthentication": "Identificare utilizator",
"UsingTokenAuth": "Daca vrei sa %1$s preiei date printr-un script, crontab etc. %2$s este necesar sa adaugi parametrul %3$s la URL-urile API care cer autentificare."
}
}

View file

@ -0,0 +1,12 @@
{
"API": {
"GenerateVisits": "Если у вас нет данных на сегодня, вы можете для начала сгенерировать немного статистики, используя плагин: %1$s. Включите плагин %2$s, кликните на Visitor Generator в панели администрирования Piwik.",
"KeepTokenSecret": "Этот token_auth является таким же секретным, как ваш логин и пароль. %1$s НЕ СООБЩАЙТЕ ЕГО НИКОМУ%2$s!",
"LoadedAPIs": "%s API успешно загружен",
"MoreInformation": "Чтобы узнать больше информации о Piwik API, пожалуйста, посмотрите раздел %1$s Introduction to Piwik API %2$s в %3$s Piwik API Reference %4$s.",
"PluginDescription": "Все данные из Piwik доступны через простые API. Этот плагин является входной точкой для веб сервиса, который можно вызвать чтобы получить данные веб аналитики в xml, json, php, csv и др.",
"TopLinkTooltip": "Получайте доступ к вашей веб-аналитике с помощью простого API и использования json, xml и др.",
"UserAuthentication": "Аутентификация пользователя",
"UsingTokenAuth": "Если вам необходимо %1$s запрашивать данные в ваших скриптах, cron-задачах, или другого источника, то вам %2$s необходимо добавить следующий ключ %3$s к URL API-вызова, который требует аутентификации."
}
}

View file

@ -0,0 +1,6 @@
{
"API": {
"LoadedAPIs": "Úspešne načítané %s API",
"UserAuthentication": "Overenie užívateľa"
}
}

View file

@ -0,0 +1,10 @@
{
"API": {
"GenerateVisits": "Če nimate današnjih podatkov, potem lahko najprej ustvarite nekaj podatkov s %1$s vtičnikom. Vtičnik %2$s lahko omogočite in nato kliknete na 'Visitor Generator' meni v Piwik administraciji.",
"KeepTokenSecret": "Ta žeton je skriven, kot vaše uporabniško ime in geslo, %1$s ne delite ga z drugimi %2$s!",
"LoadedAPIs": "Uspešno naloženih %s API-jev",
"MoreInformation": "Za več informacij o Piwik API-ju, si oglejte %1$sNavodila za uporabo Piwik API-ja %2$s in %3$s Piwik API Reference %4$s.",
"UserAuthentication": "Overitev uporabnika",
"UsingTokenAuth": "Če želite %1$s zahtevati podatke preko skripte, crontaba, itd. %2$s potem morate dodati parameter %3$s v URL API klica za overitev."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Nëse nuk keni të dhëna për ditën e sotme, mund të prodhonit ca të dhëna duke përdorur shtojcën %1$s. Shtojcën %2$s mund ta aktivizoni, mandej klikoni mbi menunë 'Gjenerator Vizitorësh' te pjesa Përgjegjës Piwik-u.",
"KeepTokenSecret": "Ky token_auth është po aq i fshehtë sa të dhënat tuaja për hyrjen dhe fjalëkalimi juaj, %1$s mos ia jepni kujt%2$s!",
"LoadedAPIs": "U ngarkua me sukses API %s",
"MoreInformation": "Për më tepër të dhëna rreth API-ve të Piwik-ut, ju lutem, hidhini një sy %1$s Hyrje në API-t e Piwik-ut %2$s dhe %3$s Referencë API-sh Piwik-u %4$s.",
"TopLinkTooltip": "Hyni programatikisht te të dhënat tuaja të Analizave Web përmes një API-je të thjeshtë json, xml, etj.",
"UserAuthentication": "Mirëfilltësim përdoruesi",
"UsingTokenAuth": "Nëse doni të %1$s kërkoni të dhëna brenda një programthi, një crontab-i, etj. %2$s lypset të shtoni parametrin %3$s te URL-të për thirrje API që lypin mirëfilltësim."
}
}

View file

@ -0,0 +1,12 @@
{
"API": {
"GenerateVisits": "Ukoliko nema podataka za danas, onda ih prvo generišite pomoću dodatka %1$s. Aktivirajte dodatak %2$s a onda kliknite na 'Generator posetilaca' na stranici za Piwik administraciju.",
"KeepTokenSecret": "token_auth je poverljivi podatak poput vašeg korisničkog imena i lozinke, stoga ga %1$s nemojte nikome pokazivati%2$s!",
"LoadedAPIs": "Uspešno učitano API-ja: %s",
"MoreInformation": "Za više informacija o Piwik API-ju molimo vas da pogledate %1$s uvod u Piwik API %2$s i %3$s Piwik API referenc listu %4$s.",
"PluginDescription": "Svi podaci su u Piwik-u dostupni preko jednostavnih API-ja. Ovaj dodatak je ulazna tačka za veb servis koji možete pozivati kako biste dobili vaše analitičke podatke u XML, JSON, PHP, CSV i drugim formatima.",
"TopLinkTooltip": "Pristupite analitičkim podacima iz vašeg programa pomoću jednostavnog API-ja u json-u, xml-u itd.",
"UserAuthentication": "Autentifikacija korisnika",
"UsingTokenAuth": "Ukoliko želite da %1$s dođete do podataka putem skripta, crontaba i slično %2$s potrebno je da dodate parametar %3$s API pozivima."
}
}

View file

@ -0,0 +1,13 @@
{
"API": {
"GenerateVisits": "Om du inte har data för idag så kan du först skapa några data med hjälp av %1$s plugin. Du kan aktivera %2$s plugin, klicka sedan på 'Besöksgeneratorn'-menyn i Piwik administrationsområdet.",
"KeepTokenSecret": "Detta token_auth är lika hemligt som ditt användarnamn och lösenord, %1$s dela inte med dig av detta! %2$s!",
"LoadedAPIs": "Laddade in %s API'er utan problem",
"MoreInformation": "För mer information om Piwiks API'er, ta en titt i %1$s Introduction to Piwik API %2$s och %3$s Piwik API Reference %4$s.",
"PluginDescription": "Allt data i Piwik finns tillgängligt genom enkla API:er. Denna plugin är en web service entry point, vilken du kan använda för att hämta ditt Piwik-data i formaten xml, json, php, csv, etc.",
"TopLinkTooltip": "Få åtkomst till webbanalysdata programmatiskt genom ett enkelt API i t.ex. json, xml etc.",
"UserAuthentication": "Användarautentisering",
"UsingTokenAuth": "Om du vill %1$s begära uppgifter inom ett skript, ett crontab-jobb, etc. %2$s måste du lägga till parametern %3$s till API-anrop webbadresser som kräver autentisering.",
"Glossary": "Ordbok"
}
}

View file

@ -0,0 +1,8 @@
{
"API": {
"KeepTokenSecret": "இந்த token_auth ஆனது உங்கள் கடவுச்சொல்லை போன்று இரகசியமானது. %1$s பகிர வேண்டாம்%2$s!",
"LoadedAPIs": "%s ஏபிஐ-கள் வெற்றிகரமாக ஏற்றப்பட்டன",
"TopLinkTooltip": "JSON, XML முதலிய எளிய பயன்பாட்டு நிரலாக்க இடைமுகம் மூலம் நிரலாக்கத்தின்படி உங்கள் இணைய பகுப்பாய்வு தரவு அணுக",
"UserAuthentication": "பயனர் உறுதிப்பாடு"
}
}

View file

@ -0,0 +1,5 @@
{
"API": {
"UserAuthentication": "వాడుకరి అధీకరణ"
}
}

View file

@ -0,0 +1,10 @@
{
"API": {
"GenerateVisits": "ถ้าคุณไม่มีข้อมูลสำหรับวันนี้ คุณสามารถสร้างข้อมูลบางอย่างใช้ปลั๊กอินการ %1$s เป็นครั้งแรก คุณสามารถเปิดใช้งานปลั๊กอิน %2$s แล้วคลิกที่เมนู 'ตัวสร้างผู้เข้าชม' ในพื้นที่จัดการ Piwik",
"KeepTokenSecret": "token_auth นี้จะเป็นความลับในการเข้าสู่ระบบและรหัสผ่านของคุณ %1$s ไม่แชร์ %2$s ได้",
"LoadedAPIs": "โหลด API %s สำเร็จแล้ว",
"MoreInformation": "สำหรับข้อมูลเพิ่มเติมเกี่ยวกับ Piwik APIs โปรดให้ดูที่ %1$s บทนำสู่การใช้ Piwik API %2$s และ %3$s แหล่งที่มาของ Piwik API %4$s.",
"UserAuthentication": "การรับรองความถูกต้องของผู้ใช้",
"UsingTokenAuth": "ถ้าคุณต้องการ ข้อมูลที่ร้องขอ %1$s ภายในสคริปต์ crontab ฯลฯ %2$s คุณต้องการเพิ่มพารามิเตอร์ %3$s API เรียก url ที่ต้องการการรับรองความถูกต้อง"
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Kung wala kang data para sa araw na ito maaari ka munang bumuo ng ilang data gamit ang %1$s na plugin. Maaari mong i-enable ang %2$s na plugin, at pagkatapos ay i-click ang 'Tagabuo ng Bisita' na menu sa Piwik Admin area.",
"KeepTokenSecret": "Ang token_auth na ito ay isang lihim gaya ng iyong login at password, %1$s huwag itong ibahagi sa iba %2$s!",
"LoadedAPIs": "Matagumpay na nai-load ang %s APIs",
"MoreInformation": "Para sa karagdagang impormasyon tungkol sa Piwik API, mangyaring tumingin sa %1$s Panimula sa Piwik API %2$s at ang %3$s Piwik API Reference %4$s.",
"TopLinkTooltip": "I-access ang iyong data ng Web Analytics programmatically sa pamamagitan ng isang simpleng API sa json, xml, atbp.",
"UserAuthentication": "Pagpapatunay sa User",
"UsingTokenAuth": "Kung nais mong %1$s mag-request ng data sa loob ng isang script, crontab, atbp. %2$s kailangan mong idagdag ang parameter na %3$s sa mga tumatawag ng API na URL na nangangailangan ng pagpapatunay."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "Eğer bugün için bir veri yoksa öncelikle %1$s eklentisiyle biraz veri oluşturabilirsiniz. %2$s eklentisini aktifleştirin, sonrasında Piwik Yönetim alanından 'Ziyaretçi Oluşturucu' menüsüne tıklayınız.",
"KeepTokenSecret": "token_auth kullanıcı adı ve şifreniz kadar gizlidir, %1$s kimseyle paylaşmayınız%2$s!",
"LoadedAPIs": "%s API başarılı şekilde yüklendi",
"MoreInformation": "Piwik APIsi hakkında daha fazla bilgi için lütfen %1$s Piwik API Kullanımı %2$s ve %3$s Piwik API Referansına %4$s bakınız.",
"TopLinkTooltip": "Json, xml veya diğerleri ile Api kullanarak erişin.",
"UserAuthentication": "Kullanıcı kimlik doğrulaması",
"UsingTokenAuth": "Bir script, cron vb içerisinden %1$s verisi çağırmak isterseniz, %2$s kimlik doğrulama gerektiren API bağlantılarına %3$s parametresini eklemelisiniz."
}
}

View file

@ -0,0 +1,10 @@
{
"API": {
"GenerateVisits": "Якщо на сьогодні ще немає даних то можна власноручно згенерувати дані використовуючи плагін %1$s . Щоб увімкнути плагін %2$s , клацніть \"Visitor Generator\" меню в Панелі Адміністратора Piwik.",
"KeepTokenSecret": "token_auth є секретним, на рівні з логіном та паролем, тож %1$s тримайте цю інформацію секреті%2$s!",
"LoadedAPIs": "%s API успішно завантажено",
"MoreInformation": "Для отримання детальнішої інформації про API Piwik, перегляньте %1$s Вступ до Piwik API %2$s та %3$s Довідковий матеріал по Piwik API %4$s.",
"UserAuthentication": "Аутентифікація користоувача",
"UsingTokenAuth": "Для %1$sдоступу до інформації скриптом, програмою і т.д.%2$s треба додавати параметр %3$s до кожного виклику API якищо використовуваний URL вимагає аутентифікації."
}
}

View file

@ -0,0 +1,13 @@
{
"API": {
"GenerateVisits": "Nếu bạn không có dữ liệu cho ngày hôm nay, bạn có thể sinh một số dữ liệu (về người thăm website) bằng cách sử dụng plugin %1$s. Bạn có thể khởi động plugin %2$s, sau đó click vào menu \"Visitor Generator\" trên khu vực quản trị Piwik.",
"KeepTokenSecret": "token_auth này chặt chẽ như mật khẩu đăng nhập của bạn, %1$s không thể chia sẻ nó %2$s!",
"LoadedAPIs": "Nạp thành công API %s",
"MoreInformation": "Để biết thêm thông tin về các API của Piwik, vui lòng xem ở Giới thiệu %1$s về %2$s API Piwik và API %3$s Piwik Reference %4$s",
"PluginDescription": "Tất cả dữ liệu của Piwik đều tủy cập được thông qua API. Plugin này là 1 web service, từ đó bạn có thể gọi để truy cập dữ liệu dưới dạng xml, json, csv,...",
"ReportingApiReference": "Tham chiếu API báo cáo",
"TopLinkTooltip": "Truy cập dữ liệu lập trình Web Analytics của bạn thông qua một API đơn giản trong JSON, xml, vv",
"UserAuthentication": "Xác thực người dùng",
"UsingTokenAuth": "Nếu bạn muốn %1$s truy vấn dữ liệu trong script hoặc crontab, ... %2$s bạn cần phải thêm tham số %3$s vào các API call URL yêu cầu xác thực."
}
}

View file

@ -0,0 +1,11 @@
{
"API": {
"GenerateVisits": "如果您今天还沒有任何资料,可以先用 %1$s 插件来产生一些数据。您可以启用 %2$s 插件,然后点击在 Piwik 管理菜单下的'生成访客'选项。",
"KeepTokenSecret": "授权号 token_auth 与您的帐号和密码一样重要,%1$s请不要公开%2$s",
"LoadedAPIs": "已成功载入 %s 个 APIs",
"MoreInformation": "了解更多关于 Piwik APIs 的资讯,请访问 %1$sPiwik API 介绍%2$s 和 %3$s Piwik API 参考资料%4$s。",
"TopLinkTooltip": "通过一个简单的 API让您可以以 xml, json, 及其它格式取得网站统计数据。",
"UserAuthentication": "身份验证",
"UsingTokenAuth": "如果您想在代码、定时任务中 %1$s 请求资料,%2$s 要在 API 调用需要认证的网址时增加参数 %3$s。"
}
}

View file

@ -0,0 +1,12 @@
{
"API": {
"GenerateVisits": "如果你今天還沒有任何資料,你可以先使用 %1$s 外掛來產生一些數據。你可以啟用 %2$s 外掛,然後點擊在 Piwik 管理員區域裡的'產生訪客'選單。",
"KeepTokenSecret": "token_auth 如你的帳號和密碼般重要,%1$s請不要公開它%2$s",
"LoadedAPIs": "已成功載入 %s 個 APIs",
"MoreInformation": "取得更多關於 Piwik APIs 的資訊,請前往 %1$sPiwik API 指引%2$s 與 %3$s Piwik API 參考資料%4$s。",
"PluginDescription": "所有在Piwik內透過簡單的API所取得資料都是可用的這個外掛是網站服務的入口點因此你可以透過下列網路分析的資料xml、json、php、csv諸如此類的方式來加以呼叫。",
"TopLinkTooltip": "透過API可以取得網站流量統計分析json與xml格式的數據。",
"UserAuthentication": "使用者驗證",
"UsingTokenAuth": "如果你想從程式、工作排程在 %1$s 請求資料。%2$s 你需要新增一個 %3$s 字串至 API 呼叫網址來進行驗證。"
}
}

View file

@ -0,0 +1,43 @@
{% extends isWidget ? 'empty.twig' : 'user.twig' %}
{% set title %}{{ 'API_Glossary'|translate }}{% endset %}
{% block content %}
<h2 piwik-enriched-headline>{{ title }}</h2>
{{ 'API_LearnAboutCommonlyUsedTerms'|translate(
'<a href="#metrics">'~ 'General_Metrics'|translate ~ '</a>',
'<a href="#reports">' ~ 'General_Reports'|translate ~ '</a>')|raw
}}
<!-- {{ metrics|length }} metrics, {{ reports|length }} reports -->
<a id="metrics"></a>
<h2>{{ 'General_Metrics'|translate }}</h2>
<table>
{% for metric in metrics %}
<tr>
<td>
<h3>{{ metric.name }}</h3>
</td>
<td>
{{ metric.documentation|raw }}
<br/><span style="color: #bbb;">{{ metric.id }} (API)</span>
</td>
</tr>
{% endfor %}
</table>
<a id="reports"></a>
<h2>{{ 'General_Reports'|translate }}</h2>
{% for report in reports %}
<h3>{{ report.name }}</h3>
<p>{{ report.documentation|raw }}</p>
{% endfor %}
{% endblock %}

View file

@ -1,29 +1,28 @@
{% extends 'dashboard.twig' %}
{% set showMenu=false %}
{% extends 'user.twig' %}
{% set title %}{{ 'API_ReportingApiReference'|translate }}{% endset %}
{% block topcontrols %}
{% include "@CoreHome/_siteSelectHeader.twig" %}
{% include "@CoreHome/_periodSelect.twig" %}
{% endblock %}
{% block content %}
{% include "@CoreHome/_siteSelectHeader.twig" %}
<div class="api-list">
<div class="page_api pageWrap">
<div class="top_controls">
{% include "@CoreHome/_periodSelect.twig" %}
</div>
<h2>{{ 'API_QuickDocumentationTitle'|translate }}</h2>
<h2>{{ title }}</h2>
<p>{{ 'API_PluginDescription'|translate }}</p>
<p>
<strong>{{ 'API_MoreInformation'|translate("<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api'>","</a>","<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api/reference'>","</a>")|raw }}</strong>
{{ 'API_MoreInformation'|translate("<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api'>","</a>","<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/analytics-api/reference'>","</a>")|raw }}
</p>
<h2>{{ 'API_UserAuthentication'|translate }}</h2>
<p>
{{ 'API_UsingTokenAuth'|translate('<b>','</b>',"")|raw }}<br/>
{{ 'API_UsingTokenAuth'|translate('','',"")|raw }}<br/>
<span id='token_auth'>&amp;token_auth=<strong>{{ token_auth }}</strong></span><br/>
{{ 'API_KeepTokenSecret'|translate('<b>','</b>')|raw }}
{{ list_api_methods_with_links|raw }}

View file

@ -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
@ -9,23 +9,27 @@
namespace Piwik\Plugins\Actions;
use Exception;
use Piwik\API\Request;
use Piwik\Archive;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Date;
use Piwik\Metrics;
use Piwik\Metrics as PiwikMetrics;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugins\Actions\Columns\Metrics\AveragePageGenerationTime;
use Piwik\Plugins\Actions\Columns\Metrics\AverageTimeOnPage;
use Piwik\Plugins\Actions\Columns\Metrics\BounceRate;
use Piwik\Plugins\Actions\Columns\Metrics\ExitRate;
use Piwik\Plugins\CustomVariables\API as APICustomVariables;
use Piwik\Plugins\Actions\Actions\ActionSiteSearch;
use Piwik\Tracker\Action;
use Piwik\Tracker\ActionSiteSearch;
use Piwik\Tracker\PageUrl;
/**
* The Actions API lets you request reports for all your Visitor Actions: Page URLs, Page titles (Piwik Events),
* File Downloads and Clicks on external websites.
*
* For example, "getPageTitles" will return all your page titles along with standard <a href='http://piwik.org/docs/analytics-api/reference/#toc-metric-definitions' target='_blank'>Actions metrics</a> for each row.
* For example, "getPageTitles" will return all your page titles along with standard <a href='http://piwik.org/docs/analytics-api/reference/#toc-metric-definitions' rel='noreferrer' target='_blank'>Actions metrics</a> for each row.
*
* It is also possible to request data for a specific Page Title with "getPageTitle"
* and setting the parameter pageName to the page title you wish to request.
@ -50,58 +54,25 @@ class API extends \Piwik\Plugin\API
public function get($idSite, $period, $date, $segment = false, $columns = false)
{
Piwik::checkUserHasViewAccess($idSite);
$report = Report::factory("Actions", "get");
$archive = Archive::build($idSite, $period, $date, $segment);
$metrics = Archiver::$actionsAggregateMetrics;
$metrics['Actions_avg_time_generation'] = 'avg_time_generation';
$requestedColumns = Piwik::getArrayFromApiParameter($columns);
$columns = $report->getMetricsRequiredForReport($allColumns = null, $requestedColumns);
// get requested columns
$columns = Piwik::getArrayFromApiParameter($columns);
if (!empty($columns)) {
// get the columns that are available and requested
$columns = array_intersect($columns, array_values($metrics));
$columns = array_values($columns); // make sure indexes are right
$nameReplace = array();
foreach ($columns as $i => $column) {
$fullColumn = array_search($column, $metrics);
$columns[$i] = $fullColumn;
$nameReplace[$fullColumn] = $column;
}
$inDbColumnNames = array_map(function ($value) { return 'Actions_' . $value; }, $columns);
$dataTable = $archive->getDataTableFromNumeric($inDbColumnNames);
if (false !== ($avgGenerationTimeRequested = array_search('Actions_avg_time_generation', $columns))) {
unset($columns[$avgGenerationTimeRequested]);
$avgGenerationTimeRequested = true;
}
} else {
// get all columns
unset($metrics['Actions_avg_time_generation']);
$columns = array_keys($metrics);
$nameReplace = & $metrics;
$avgGenerationTimeRequested = true;
}
$dataTable->deleteColumns(array_diff($requestedColumns, $columns));
if ($avgGenerationTimeRequested) {
$tempColumns[] = Archiver::METRIC_SUM_TIME_RECORD_NAME;
$tempColumns[] = Archiver::METRIC_HITS_TIMED_RECORD_NAME;
$columns = array_merge($columns, $tempColumns);
$columns = array_unique($columns);
$newNameMapping = array_combine($inDbColumnNames, $columns);
$dataTable->filter('ReplaceColumnNames', array($newNameMapping));
$nameReplace[Archiver::METRIC_SUM_TIME_RECORD_NAME] = 'sum_time_generation';
$nameReplace[Archiver::METRIC_HITS_TIMED_RECORD_NAME] = 'nb_hits_with_time_generation';
}
$columnsToShow = $requestedColumns ?: $report->getAllMetrics();
$dataTable->queueFilter('ColumnDelete', array($columnsToRemove = array(), $columnsToShow));
$table = $archive->getDataTableFromNumeric($columns);
// replace labels (remove Actions_)
$table->filter('ReplaceColumnNames', array($nameReplace));
// compute avg generation time
if ($avgGenerationTimeRequested) {
$table->filter('ColumnCallbackAddColumnQuotient', array('avg_time_generation', 'sum_time_generation', 'nb_hits_with_time_generation', 3));
$table->deleteColumns(array('sum_time_generation', 'nb_hits_with_time_generation'));
}
return $table;
return $dataTable;
}
/**
@ -112,15 +83,17 @@ class API extends \Piwik\Plugin\API
* @param bool $expanded
* @param bool|int $idSubtable
* @param bool|int $depth
* @param bool|int $flat
*
* @return DataTable|DataTable\Map
*/
public function getPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false,
$depth = false)
$depth = false, $flat = false)
{
$dataTable = $this->getDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable, $depth);
$this->filterPageDatatable($dataTable);
$this->filterActionsDataTable($dataTable, $expanded);
$dataTable = Archive::createDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable, $depth);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
@ -165,7 +138,7 @@ class API extends \Piwik\Plugin\API
{
// Keep only pages which are following site search
$dataTable->filter('ColumnCallbackDeleteRow', array(
'nb_hits_following_search',
PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS,
function ($value) {
return $value <= 0;
}
@ -196,18 +169,19 @@ class API extends \Piwik\Plugin\API
public function getPageUrl($pageUrl, $idSite, $period, $date, $segment = false)
{
$callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
$callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageUrl, Action::TYPE_PAGE_URL);
$this->filterPageDatatable($dataTable);
$this->addPageProcessedMetrics($dataTable);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
public function getPageTitles($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
public function getPageTitles($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false, $flat = false)
{
$dataTable = $this->getDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $idSubtable);
$this->filterPageDatatable($dataTable);
$this->filterActionsDataTable($dataTable, $expanded);
$dataTable = Archive::createDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
@ -237,38 +211,38 @@ class API extends \Piwik\Plugin\API
public function getPageTitle($pageName, $idSite, $period, $date, $segment = false)
{
$callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
$callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageName, Action::TYPE_PAGE_TITLE);
$this->filterPageDatatable($dataTable);
$this->addPageProcessedMetrics($dataTable);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
public function getDownloads($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
public function getDownloads($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false, $flat = false)
{
$dataTable = $this->getDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $idSubtable);
$dataTable = Archive::createDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
$this->filterActionsDataTable($dataTable, $expanded);
return $dataTable;
}
public function getDownload($downloadUrl, $idSite, $period, $date, $segment = false)
{
$callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
$callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $downloadUrl, Action::TYPE_DOWNLOAD);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
public function getOutlinks($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
public function getOutlinks($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false, $flat = false)
{
$dataTable = $this->getDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $idSubtable);
$dataTable = Archive::createDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
$this->filterActionsDataTable($dataTable, $expanded);
return $dataTable;
}
public function getOutlink($outlinkUrl, $idSite, $period, $date, $segment = false)
{
$callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
$callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $outlinkUrl, Action::TYPE_OUTLINK);
$this->filterActionsDataTable($dataTable);
return $dataTable;
@ -277,9 +251,9 @@ class API extends \Piwik\Plugin\API
public function getSiteSearchKeywords($idSite, $period, $date, $segment = false)
{
$dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
$dataTable->deleteColumn(Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterPageDatatable($dataTable);
$dataTable->deleteColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterActionsDataTable($dataTable);
$dataTable->filter('ReplaceColumnNames');
$this->addPagesPerSearchColumn($dataTable);
return $dataTable;
}
@ -297,7 +271,7 @@ class API extends \Piwik\Plugin\API
protected function getSiteSearchKeywordsRaw($idSite, $period, $date, $segment)
{
$dataTable = $this->getDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false);
$dataTable = Archive::createDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false);
return $dataTable;
}
@ -307,15 +281,15 @@ class API extends \Piwik\Plugin\API
// Delete all rows that have some results
$dataTable->filter('ColumnCallbackDeleteRow',
array(
Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT,
PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT,
function ($value) {
return $value < 1;
}
));
$dataTable->deleteRow(DataTable::ID_SUMMARY_ROW);
$dataTable->deleteColumn(Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterPageDatatable($dataTable);
$dataTable->deleteColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterActionsDataTable($dataTable);
$dataTable->filter('ReplaceColumnNames');
$this->addPagesPerSearchColumn($dataTable);
return $dataTable;
}
@ -342,7 +316,6 @@ class API extends \Piwik\Plugin\API
$dataTable = $customVariables->getEmptyClone();
$customVariableDatatables = $customVariables->getDataTables();
$dataTables = $dataTable->getDataTables();
foreach ($customVariableDatatables as $key => $customVariableTableForDate) {
// we do not enter the IF, in the case idSite=1,3 AND period=day&date=datefrom,dateto,
if ($customVariableTableForDate instanceof DataTable
@ -365,6 +338,7 @@ class API extends \Piwik\Plugin\API
}
}
$this->filterActionsDataTable($dataTable);
$dataTable->filter('ReplaceColumnNames');
$this->addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_actions');
return $dataTable;
}
@ -394,7 +368,7 @@ class API extends \Piwik\Plugin\API
if ($table === false) {
// fetch the data table
$table = call_user_func_array(array($this, 'getDataTableFromArchive'), $callBackParameters);
$table = call_user_func_array('\Piwik\Archive::createDataTableFromArchive', $callBackParameters);
if ($table instanceof DataTable\Map) {
// search an array of tables, e.g. when using date=last30
@ -449,7 +423,7 @@ class API extends \Piwik\Plugin\API
// end of tree search reached
if (count($searchTree) == 0) {
$result = new DataTable();
$result = $table->getEmptyClone();
$result->addRow($row);
$result->setAllTableMetadata($table->getAllTableMetadata());
return $result;
@ -457,97 +431,36 @@ class API extends \Piwik\Plugin\API
// match found on this level and more levels remaining: go deeper
$idSubTable = $row->getIdSubDataTable();
$callBackParameters[6] = $idSubTable;
$table = call_user_func_array(array($this, 'getDataTableFromArchive'), $callBackParameters);
$callBackParameters[7] = $idSubTable;
/**
* @var \Piwik\Period $period
*/
$period = $table->getMetadata('period');
if (!empty($period)) {
$callBackParameters[3] = $period->getDateStart() . ',' . $period->getDateEnd();
}
$table = call_user_func_array('\Piwik\Archive::createDataTableFromArchive', $callBackParameters);
return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
}
throw new Exception("For this API function, DataTable " . get_class($table) . " is not supported");
}
/**
* Common filters for Page URLs and Page Titles
*
* @param DataTable|DataTable\Simple|DataTable\Map $dataTable
*/
protected function filterPageDatatable($dataTable)
{
$columnsToRemove = array('bounce_rate');
$dataTable->queueFilter('ColumnDelete', array($columnsToRemove));
// Average time on page = total time on page / number visits on that page
$dataTable->queueFilter('ColumnCallbackAddColumnQuotient',
array('avg_time_on_page',
'sum_time_spent',
'nb_visits',
0)
);
// Bounce rate = single page visits on this page / visits started on this page
$dataTable->queueFilter('ColumnCallbackAddColumnPercentage',
array('bounce_rate',
'entry_bounce_count',
'entry_nb_visits',
0));
// % Exit = Number of visits that finished on this page / visits on this page
$dataTable->queueFilter('ColumnCallbackAddColumnPercentage',
array('exit_rate',
'exit_nb_visits',
'nb_visits',
0)
);
// Handle performance analytics
$hasTimeGeneration = (array_sum($dataTable->getColumn(Metrics::INDEX_PAGE_SUM_TIME_GENERATION)) > 0);
if ($hasTimeGeneration) {
// Average generation time = total generation time / number of pageviews
$precisionAvgTimeGeneration = 3;
$dataTable->queueFilter('ColumnCallbackAddColumnQuotient',
array('avg_time_generation',
'sum_time_generation',
'nb_hits_with_time_generation',
$precisionAvgTimeGeneration)
);
$dataTable->queueFilter('ColumnDelete', array(array('sum_time_generation')));
} else {
// No generation time: remove it from the API output and add it to empty_columns metadata, so that
// the columns can also be removed from the view
$dataTable->filter('ColumnDelete', array(array(
Metrics::INDEX_PAGE_SUM_TIME_GENERATION,
Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION,
Metrics::INDEX_PAGE_MIN_TIME_GENERATION,
Metrics::INDEX_PAGE_MAX_TIME_GENERATION
)));
if ($dataTable instanceof DataTable) {
$emptyColumns = $dataTable->getMetadata(DataTable::EMPTY_COLUMNS_METADATA_NAME);
if (!is_array($emptyColumns)) {
$emptyColumns = array();
}
$emptyColumns[] = 'sum_time_generation';
$emptyColumns[] = 'avg_time_generation';
$emptyColumns[] = 'min_time_generation';
$emptyColumns[] = 'max_time_generation';
$dataTable->setMetadata(DataTable::EMPTY_COLUMNS_METADATA_NAME, $emptyColumns);
}
}
}
/**
* Common filters for all Actions API
*
* @param DataTable|DataTable\Simple|DataTable\Map $dataTable
* @param bool $expanded
*/
protected function filterActionsDataTable($dataTable, $expanded = false)
private function filterActionsDataTable($dataTable)
{
// Must be applied before Sort in this case, since the DataTable can contain both int and strings indexes
// (in the transition period between pre 1.2 and post 1.2 datatable structure)
$dataTable->filter('ReplaceColumnNames');
$dataTable->filter('Sort', array('nb_visits', 'desc', $naturalSort = false, $expanded));
$dataTable->queueFilter('ReplaceSummaryRowLabel');
$dataTable->filter('Piwik\Plugins\Actions\DataTable\Filter\Actions');
return $dataTable;
}
/**
@ -558,7 +471,7 @@ class API extends \Piwik\Plugin\API
private function filterNonEntryActions($dataTable)
{
$dataTable->filter('ColumnCallbackDeleteRow',
array('entry_nb_visits',
array(PiwikMetrics::INDEX_PAGE_ENTRY_NB_VISITS,
function ($visits) {
return !strlen($visits);
}
@ -574,23 +487,22 @@ class API extends \Piwik\Plugin\API
private function filterNonExitActions($dataTable)
{
$dataTable->filter('ColumnCallbackDeleteRow',
array('exit_nb_visits',
array(PiwikMetrics::INDEX_PAGE_EXIT_NB_VISITS,
function ($visits) {
return !strlen($visits);
})
);
}
protected function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null, $depth = null)
private function addPageProcessedMetrics(DataTable\DataTableInterface $dataTable)
{
$skipAggregationOfSubTables = false;
if ($period == 'range'
&& empty($idSubtable)
&& empty($expanded)
&& !Request::shouldLoadFlatten()
) {
$skipAggregationOfSubTables = false;
}
return Archive::getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable, $skipAggregationOfSubTables, $depth);
$dataTable->filter(function (DataTable $table) {
$extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
$extraProcessedMetrics[] = new AverageTimeOnPage();
$extraProcessedMetrics[] = new BounceRate();
$extraProcessedMetrics[] = new ExitRate();
$extraProcessedMetrics[] = new AveragePageGenerationTime();
$table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics);
});
}
}

View file

@ -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
@ -8,17 +8,11 @@
*/
namespace Piwik\Plugins\Actions;
use Piwik\API\Request;
use Piwik\ArchiveProcessor;
use Piwik\Common;
use Piwik\Db;
use Piwik\Menu\MenuMain;
use Piwik\MetricsFormatter;
use Piwik\Piwik;
use Piwik\Site;
use Piwik\Plugin\ViewDataTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\Site;
use Piwik\WidgetsList;
/**
* Actions plugin
@ -31,21 +25,68 @@ class Actions extends \Piwik\Plugin
const ACTIONS_REPORT_ROWS_DISPLAY = 100;
/**
* @see Piwik\Plugin::getListHooksRegistered
* @see Piwik\Plugin::registerEvents
*/
public function getListHooksRegistered()
public function registerEvents()
{
$hooks = array(
'WidgetsList.addWidgets' => 'addWidgets',
'Menu.Reporting.addItems' => 'addMenus',
'API.getReportMetadata' => 'getReportMetadata',
'API.getSegmentDimensionMetadata' => 'getSegmentsMetadata',
return array(
'ViewDataTable.configure' => 'configureViewDataTable',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'Insights.addReportToOverview' => 'addReportToInsightsOverview'
'Insights.addReportToOverview' => 'addReportToInsightsOverview',
'Live.getAllVisitorDetails' => 'extendVisitorDetails',
'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations',
'Metrics.getDefaultMetricDocumentationTranslations' => 'addMetricDocumentationTranslations',
);
return $hooks;
}
public function extendVisitorDetails(&$visitor, $details)
{
$visitor['searches'] = $details['visit_total_searches'];
$visitor['actions'] = $details['visit_total_actions'];
}
public function addMetricTranslations(&$translations)
{
$metrics = array(
'nb_pageviews' => 'General_ColumnPageviews',
'nb_uniq_pageviews' => 'General_ColumnUniquePageviews',
'nb_downloads' => 'General_Downloads',
'nb_uniq_downloads' => 'Actions_ColumnUniqueDownloads',
'nb_outlinks' => 'General_Outlinks',
'nb_uniq_outlinks' => 'Actions_ColumnUniqueOutlinks',
'nb_searches' => 'Actions_ColumnSearches',
'nb_keywords' => 'Actions_ColumnSiteSearchKeywords',
'avg_time_generation' => 'General_ColumnAverageGenerationTime',
'exit_rate' => 'General_ColumnExitRate',
'entry_nb_visits' => 'General_ColumnEntrances',
'entry_bounce_count' => 'General_ColumnBounces',
'exit_nb_visits' => 'General_ColumnExits',
'nb_pages_per_search' => 'Actions_ColumnPagesPerSearch',
'nb_hits_following_search' => 'General_ColumnViewedAfterSearch',
);
$translations = array_merge($translations, $metrics);
}
public function addMetricDocumentationTranslations(&$translations)
{
$metrics = array(
'nb_pageviews' => 'General_ColumnPageviewsDocumentation',
'nb_uniq_pageviews' => 'General_ColumnUniquePageviewsDocumentation',
'nb_downloads' => 'Actions_ColumnClicksDocumentation',
'nb_uniq_downloads' => 'Actions_ColumnUniqueClicksDocumentation',
'nb_outlinks' => 'Actions_ColumnClicksDocumentation',
'nb_uniq_outlinks' => 'Actions_ColumnUniqueClicksDocumentation',
'nb_searches' => 'Actions_ColumnSearchesDocumentation',
'avg_time_generation' => 'General_ColumnAverageGenerationTimeDocumentation',
'entry_nb_visits' => 'General_ColumnEntrancesDocumentation',
'entry_bounce_count' => 'General_ColumnBouncesDocumentation',
'exit_nb_visits' => 'General_ColumnExitsDocumentation',
'exit_rate' => 'General_ColumnExitRateDocumentation'
);
$translations = array_merge($translations, $metrics);
}
public function addReportToInsightsOverview(&$reports)
@ -63,447 +104,11 @@ class Actions extends \Piwik\Plugin
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/Actions/javascripts/actionsDataTable.js";
$jsFiles[] = "plugins/Actions/javascripts/rowactions.js";
}
public function getSegmentsMetadata(&$segments)
public function isSiteSearchEnabled($idSites, $idSite)
{
$sqlFilter = '\\Piwik\\Tracker\\TableLogAction::getIdActionFromSegment';
// entry and exit pages of visit
$segments[] = array(
'type' => 'dimension',
'category' => 'General_Actions',
'name' => 'Actions_ColumnEntryPageURL',
'segment' => 'entryPageUrl',
'sqlSegment' => 'log_visit.visit_entry_idaction_url',
'sqlFilter' => $sqlFilter,
);
$segments[] = array(
'type' => 'dimension',
'category' => 'General_Actions',
'name' => 'Actions_ColumnEntryPageTitle',
'segment' => 'entryPageTitle',
'sqlSegment' => 'log_visit.visit_entry_idaction_name',
'sqlFilter' => $sqlFilter,
);
$segments[] = array(
'type' => 'dimension',
'category' => 'General_Actions',
'name' => 'Actions_ColumnExitPageURL',
'segment' => 'exitPageUrl',
'sqlSegment' => 'log_visit.visit_exit_idaction_url',
'sqlFilter' => $sqlFilter,
);
$segments[] = array(
'type' => 'dimension',
'category' => 'General_Actions',
'name' => 'Actions_ColumnExitPageTitle',
'segment' => 'exitPageTitle',
'sqlSegment' => 'log_visit.visit_exit_idaction_name',
'sqlFilter' => $sqlFilter,
);
// single pages
$segments[] = array(
'type' => 'dimension',
'category' => 'General_Actions',
'name' => 'Actions_ColumnPageURL',
'segment' => 'pageUrl',
'sqlSegment' => 'log_link_visit_action.idaction_url',
'sqlFilter' => $sqlFilter,
'acceptedValues' => "All these segments must be URL encoded, for example: " . urlencode('http://example.com/path/page?query'),
);
$segments[] = array(
'type' => 'dimension',
'category' => 'General_Actions',
'name' => 'Actions_ColumnPageName',
'segment' => 'pageTitle',
'sqlSegment' => 'log_link_visit_action.idaction_name',
'sqlFilter' => $sqlFilter,
);
$segments[] = array(
'type' => 'dimension',
'category' => 'General_Actions',
'name' => 'Actions_SiteSearchKeyword',
'segment' => 'siteSearchKeyword',
'sqlSegment' => 'log_link_visit_action.idaction_name',
'sqlFilter' => $sqlFilter,
);
}
public function getReportMetadata(&$reports)
{
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('General_Actions') . ' - ' . Piwik::translate('General_MainMetrics'),
'module' => 'Actions',
'action' => 'get',
'metrics' => array(
'nb_pageviews' => Piwik::translate('General_ColumnPageviews'),
'nb_uniq_pageviews' => Piwik::translate('General_ColumnUniquePageviews'),
'nb_downloads' => Piwik::translate('General_Downloads'),
'nb_uniq_downloads' => Piwik::translate('Actions_ColumnUniqueDownloads'),
'nb_outlinks' => Piwik::translate('General_Outlinks'),
'nb_uniq_outlinks' => Piwik::translate('Actions_ColumnUniqueOutlinks'),
'nb_searches' => Piwik::translate('Actions_ColumnSearches'),
'nb_keywords' => Piwik::translate('Actions_ColumnSiteSearchKeywords'),
'avg_time_generation' => Piwik::translate('General_ColumnAverageGenerationTime'),
),
'metricsDocumentation' => array(
'nb_pageviews' => Piwik::translate('General_ColumnPageviewsDocumentation'),
'nb_uniq_pageviews' => Piwik::translate('General_ColumnUniquePageviewsDocumentation'),
'nb_downloads' => Piwik::translate('Actions_ColumnClicksDocumentation'),
'nb_uniq_downloads' => Piwik::translate('Actions_ColumnUniqueClicksDocumentation'),
'nb_outlinks' => Piwik::translate('Actions_ColumnClicksDocumentation'),
'nb_uniq_outlinks' => Piwik::translate('Actions_ColumnUniqueClicksDocumentation'),
'nb_searches' => Piwik::translate('Actions_ColumnSearchesDocumentation'),
'avg_time_generation' => Piwik::translate('General_ColumnAverageGenerationTimeDocumentation'),
// 'nb_keywords' => Piwik::translate('Actions_ColumnSiteSearchKeywords'),
),
'processedMetrics' => false,
'order' => 1
);
$metrics = array(
'nb_hits' => Piwik::translate('General_ColumnPageviews'),
'nb_visits' => Piwik::translate('General_ColumnUniquePageviews'),
'bounce_rate' => Piwik::translate('General_ColumnBounceRate'),
'avg_time_on_page' => Piwik::translate('General_ColumnAverageTimeOnPage'),
'exit_rate' => Piwik::translate('General_ColumnExitRate'),
'avg_time_generation' => Piwik::translate('General_ColumnAverageGenerationTime')
);
$documentation = array(
'nb_hits' => Piwik::translate('General_ColumnPageviewsDocumentation'),
'nb_visits' => Piwik::translate('General_ColumnUniquePageviewsDocumentation'),
'bounce_rate' => Piwik::translate('General_ColumnPageBounceRateDocumentation'),
'avg_time_on_page' => Piwik::translate('General_ColumnAverageTimeOnPageDocumentation'),
'exit_rate' => Piwik::translate('General_ColumnExitRateDocumentation'),
'avg_time_generation' => Piwik::translate('General_ColumnAverageGenerationTimeDocumentation'),
);
// pages report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('Actions_PageUrls'),
'module' => 'Actions',
'action' => 'getPageUrls',
'dimension' => Piwik::translate('Actions_ColumnPageURL'),
'metrics' => $metrics,
'metricsDocumentation' => $documentation,
'documentation' => Piwik::translate('Actions_PagesReportDocumentation', '<br />')
. '<br />' . Piwik::translate('General_UsePlusMinusIconsDocumentation'),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getPageUrls',
'order' => 2
);
// entry pages report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('Actions_SubmenuPagesEntry'),
'module' => 'Actions',
'action' => 'getEntryPageUrls',
'dimension' => Piwik::translate('Actions_ColumnPageURL'),
'metrics' => array(
'entry_nb_visits' => Piwik::translate('General_ColumnEntrances'),
'entry_bounce_count' => Piwik::translate('General_ColumnBounces'),
'bounce_rate' => Piwik::translate('General_ColumnBounceRate'),
),
'metricsDocumentation' => array(
'entry_nb_visits' => Piwik::translate('General_ColumnEntrancesDocumentation'),
'entry_bounce_count' => Piwik::translate('General_ColumnBouncesDocumentation'),
'bounce_rate' => Piwik::translate('General_ColumnBounceRateForPageDocumentation')
),
'documentation' => Piwik::translate('Actions_EntryPagesReportDocumentation', '<br />')
. ' ' . Piwik::translate('General_UsePlusMinusIconsDocumentation'),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getEntryPageUrls',
'order' => 3
);
// exit pages report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('Actions_SubmenuPagesExit'),
'module' => 'Actions',
'action' => 'getExitPageUrls',
'dimension' => Piwik::translate('Actions_ColumnPageURL'),
'metrics' => array(
'exit_nb_visits' => Piwik::translate('General_ColumnExits'),
'nb_visits' => Piwik::translate('General_ColumnUniquePageviews'),
'exit_rate' => Piwik::translate('General_ColumnExitRate')
),
'metricsDocumentation' => array(
'exit_nb_visits' => Piwik::translate('General_ColumnExitsDocumentation'),
'nb_visits' => Piwik::translate('General_ColumnUniquePageviewsDocumentation'),
'exit_rate' => Piwik::translate('General_ColumnExitRateDocumentation')
),
'documentation' => Piwik::translate('Actions_ExitPagesReportDocumentation', '<br />')
. ' ' . Piwik::translate('General_UsePlusMinusIconsDocumentation'),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getExitPageUrls',
'order' => 4
);
// page titles report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('Actions_SubmenuPageTitles'),
'module' => 'Actions',
'action' => 'getPageTitles',
'dimension' => Piwik::translate('Actions_ColumnPageName'),
'metrics' => $metrics,
'metricsDocumentation' => $documentation,
'documentation' => Piwik::translate('Actions_PageTitlesReportDocumentation', array('<br />', htmlentities('<title>'))),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getPageTitles',
'order' => 5,
);
// entry page titles report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('Actions_EntryPageTitles'),
'module' => 'Actions',
'action' => 'getEntryPageTitles',
'dimension' => Piwik::translate('Actions_ColumnPageName'),
'metrics' => array(
'entry_nb_visits' => Piwik::translate('General_ColumnEntrances'),
'entry_bounce_count' => Piwik::translate('General_ColumnBounces'),
'bounce_rate' => Piwik::translate('General_ColumnBounceRate'),
),
'metricsDocumentation' => array(
'entry_nb_visits' => Piwik::translate('General_ColumnEntrancesDocumentation'),
'entry_bounce_count' => Piwik::translate('General_ColumnBouncesDocumentation'),
'bounce_rate' => Piwik::translate('General_ColumnBounceRateForPageDocumentation')
),
'documentation' => Piwik::translate('Actions_ExitPageTitlesReportDocumentation', '<br />')
. ' ' . Piwik::translate('General_UsePlusMinusIconsDocumentation'),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getEntryPageTitles',
'order' => 6
);
// exit page titles report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('Actions_ExitPageTitles'),
'module' => 'Actions',
'action' => 'getExitPageTitles',
'dimension' => Piwik::translate('Actions_ColumnPageName'),
'metrics' => array(
'exit_nb_visits' => Piwik::translate('General_ColumnExits'),
'nb_visits' => Piwik::translate('General_ColumnUniquePageviews'),
'exit_rate' => Piwik::translate('General_ColumnExitRate')
),
'metricsDocumentation' => array(
'exit_nb_visits' => Piwik::translate('General_ColumnExitsDocumentation'),
'nb_visits' => Piwik::translate('General_ColumnUniquePageviewsDocumentation'),
'exit_rate' => Piwik::translate('General_ColumnExitRateDocumentation')
),
'documentation' => Piwik::translate('Actions_EntryPageTitlesReportDocumentation', '<br />')
. ' ' . Piwik::translate('General_UsePlusMinusIconsDocumentation'),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getExitPageTitles',
'order' => 7
);
$documentation = array(
'nb_visits' => Piwik::translate('Actions_ColumnUniqueClicksDocumentation'),
'nb_hits' => Piwik::translate('Actions_ColumnClicksDocumentation')
);
// outlinks report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('General_Outlinks'),
'module' => 'Actions',
'action' => 'getOutlinks',
'dimension' => Piwik::translate('Actions_ColumnClickedURL'),
'metrics' => array(
'nb_visits' => Piwik::translate('Actions_ColumnUniqueClicks'),
'nb_hits' => Piwik::translate('Actions_ColumnClicks')
),
'metricsDocumentation' => $documentation,
'documentation' => Piwik::translate('Actions_OutlinksReportDocumentation') . ' '
. Piwik::translate('Actions_OutlinkDocumentation') . '<br />'
. Piwik::translate('General_UsePlusMinusIconsDocumentation'),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getOutlinks',
'order' => 8,
);
// downloads report
$reports[] = array(
'category' => Piwik::translate('General_Actions'),
'name' => Piwik::translate('General_Downloads'),
'module' => 'Actions',
'action' => 'getDownloads',
'dimension' => Piwik::translate('Actions_ColumnDownloadURL'),
'metrics' => array(
'nb_visits' => Piwik::translate('Actions_ColumnUniqueDownloads'),
'nb_hits' => Piwik::translate('General_Downloads')
),
'metricsDocumentation' => $documentation,
'documentation' => Piwik::translate('Actions_DownloadsReportDocumentation', '<br />'),
'processedMetrics' => false,
'actionToLoadSubTables' => 'getDownloads',
'order' => 9,
);
if ($this->isSiteSearchEnabled()) {
// Search Keywords
$reports[] = array(
'category' => Piwik::translate('Actions_SubmenuSitesearch'),
'name' => Piwik::translate('Actions_WidgetSearchKeywords'),
'module' => 'Actions',
'action' => 'getSiteSearchKeywords',
'dimension' => Piwik::translate('General_ColumnKeyword'),
'metrics' => array(
'nb_visits' => Piwik::translate('Actions_ColumnSearches'),
'nb_pages_per_search' => Piwik::translate('Actions_ColumnPagesPerSearch'),
'exit_rate' => Piwik::translate('Actions_ColumnSearchExits'),
),
'metricsDocumentation' => array(
'nb_visits' => Piwik::translate('Actions_ColumnSearchesDocumentation'),
'nb_pages_per_search' => Piwik::translate('Actions_ColumnPagesPerSearchDocumentation'),
'exit_rate' => Piwik::translate('Actions_ColumnSearchExitsDocumentation'),
),
'documentation' => Piwik::translate('Actions_SiteSearchKeywordsDocumentation') . '<br/><br/>' . Piwik::translate('Actions_SiteSearchIntro') . '<br/><br/>'
. '<a href="http://piwik.org/docs/site-search/" target="_blank">' . Piwik::translate('Actions_LearnMoreAboutSiteSearchLink') . '</a>',
'processedMetrics' => false,
'order' => 15
);
// No Result Search Keywords
$reports[] = array(
'category' => Piwik::translate('Actions_SubmenuSitesearch'),
'name' => Piwik::translate('Actions_WidgetSearchNoResultKeywords'),
'module' => 'Actions',
'action' => 'getSiteSearchNoResultKeywords',
'dimension' => Piwik::translate('Actions_ColumnNoResultKeyword'),
'metrics' => array(
'nb_visits' => Piwik::translate('Actions_ColumnSearches'),
'exit_rate' => Piwik::translate('Actions_ColumnSearchExits'),
),
'metricsDocumentation' => array(
'nb_visits' => Piwik::translate('Actions_ColumnSearchesDocumentation'),
'exit_rate' => Piwik::translate('Actions_ColumnSearchExitsDocumentation'),
),
'documentation' => Piwik::translate('Actions_SiteSearchIntro') . '<br /><br />' . Piwik::translate('Actions_SiteSearchKeywordsNoResultDocumentation'),
'processedMetrics' => false,
'order' => 16
);
if (self::isCustomVariablesPluginsEnabled()) {
// Search Categories
$reports[] = array(
'category' => Piwik::translate('Actions_SubmenuSitesearch'),
'name' => Piwik::translate('Actions_WidgetSearchCategories'),
'module' => 'Actions',
'action' => 'getSiteSearchCategories',
'dimension' => Piwik::translate('Actions_ColumnSearchCategory'),
'metrics' => array(
'nb_visits' => Piwik::translate('Actions_ColumnSearches'),
'nb_pages_per_search' => Piwik::translate('Actions_ColumnPagesPerSearch'),
'exit_rate' => Piwik::translate('Actions_ColumnSearchExits'),
),
'metricsDocumentation' => array(
'nb_visits' => Piwik::translate('Actions_ColumnSearchesDocumentation'),
'nb_pages_per_search' => Piwik::translate('Actions_ColumnPagesPerSearchDocumentation'),
'exit_rate' => Piwik::translate('Actions_ColumnSearchExitsDocumentation'),
),
'documentation' => Piwik::translate('Actions_SiteSearchCategories1') . '<br/>' . Piwik::translate('Actions_SiteSearchCategories2'),
'processedMetrics' => false,
'order' => 17
);
}
$documentation = Piwik::translate('Actions_SiteSearchFollowingPagesDoc') . '<br/>' . Piwik::translate('General_UsePlusMinusIconsDocumentation');
// Pages URLs following Search
$reports[] = array(
'category' => Piwik::translate('Actions_SubmenuSitesearch'),
'name' => Piwik::translate('Actions_WidgetPageUrlsFollowingSearch'),
'module' => 'Actions',
'action' => 'getPageUrlsFollowingSiteSearch',
'dimension' => Piwik::translate('General_ColumnDestinationPage'),
'metrics' => array(
'nb_hits_following_search' => Piwik::translate('General_ColumnViewedAfterSearch'),
'nb_hits' => Piwik::translate('General_ColumnTotalPageviews'),
),
'metricsDocumentation' => array(
'nb_hits_following_search' => Piwik::translate('General_ColumnViewedAfterSearchDocumentation'),
'nb_hits' => Piwik::translate('General_ColumnPageviewsDocumentation'),
),
'documentation' => $documentation,
'processedMetrics' => false,
'order' => 18
);
// Pages Titles following Search
$reports[] = array(
'category' => Piwik::translate('Actions_SubmenuSitesearch'),
'name' => Piwik::translate('Actions_WidgetPageTitlesFollowingSearch'),
'module' => 'Actions',
'action' => 'getPageTitlesFollowingSiteSearch',
'dimension' => Piwik::translate('General_ColumnDestinationPage'),
'metrics' => array(
'nb_hits_following_search' => Piwik::translate('General_ColumnViewedAfterSearch'),
'nb_hits' => Piwik::translate('General_ColumnTotalPageviews'),
),
'metricsDocumentation' => array(
'nb_hits_following_search' => Piwik::translate('General_ColumnViewedAfterSearchDocumentation'),
'nb_hits' => Piwik::translate('General_ColumnPageviewsDocumentation'),
),
'documentation' => $documentation,
'processedMetrics' => false,
'order' => 19
);
}
}
function addWidgets()
{
WidgetsList::add('General_Actions', 'General_Pages', 'Actions', 'getPageUrls');
WidgetsList::add('General_Actions', 'Actions_WidgetPageTitles', 'Actions', 'getPageTitles');
WidgetsList::add('General_Actions', 'General_Outlinks', 'Actions', 'getOutlinks');
WidgetsList::add('General_Actions', 'General_Downloads', 'Actions', 'getDownloads');
WidgetsList::add('General_Actions', 'Actions_WidgetPagesEntry', 'Actions', 'getEntryPageUrls');
WidgetsList::add('General_Actions', 'Actions_WidgetPagesExit', 'Actions', 'getExitPageUrls');
WidgetsList::add('General_Actions', 'Actions_WidgetEntryPageTitles', 'Actions', 'getEntryPageTitles');
WidgetsList::add('General_Actions', 'Actions_WidgetExitPageTitles', 'Actions', 'getExitPageTitles');
if ($this->isSiteSearchEnabled()) {
WidgetsList::add('Actions_SubmenuSitesearch', 'Actions_WidgetSearchKeywords', 'Actions', 'getSiteSearchKeywords');
if (self::isCustomVariablesPluginsEnabled()) {
WidgetsList::add('Actions_SubmenuSitesearch', 'Actions_WidgetSearchCategories', 'Actions', 'getSiteSearchCategories');
}
WidgetsList::add('Actions_SubmenuSitesearch', 'Actions_WidgetSearchNoResultKeywords', 'Actions', 'getSiteSearchNoResultKeywords');
WidgetsList::add('Actions_SubmenuSitesearch', 'Actions_WidgetPageUrlsFollowingSearch', 'Actions', 'getPageUrlsFollowingSiteSearch');
WidgetsList::add('Actions_SubmenuSitesearch', 'Actions_WidgetPageTitlesFollowingSearch', 'Actions', 'getPageTitlesFollowingSiteSearch');
}
}
function addMenus()
{
MenuMain::getInstance()->add('General_Actions', '', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 15);
MenuMain::getInstance()->add('General_Actions', 'General_Pages', array('module' => 'Actions', 'action' => 'indexPageUrls'), true, 1);
MenuMain::getInstance()->add('General_Actions', 'Actions_SubmenuPagesEntry', array('module' => 'Actions', 'action' => 'indexEntryPageUrls'), true, 2);
MenuMain::getInstance()->add('General_Actions', 'Actions_SubmenuPagesExit', array('module' => 'Actions', 'action' => 'indexExitPageUrls'), true, 3);
MenuMain::getInstance()->add('General_Actions', 'Actions_SubmenuPageTitles', array('module' => 'Actions', 'action' => 'indexPageTitles'), true, 4);
MenuMain::getInstance()->add('General_Actions', 'General_Outlinks', array('module' => 'Actions', 'action' => 'indexOutlinks'), true, 6);
MenuMain::getInstance()->add('General_Actions', 'General_Downloads', array('module' => 'Actions', 'action' => 'indexDownloads'), true, 7);
if ($this->isSiteSearchEnabled()) {
MenuMain::getInstance()->add('General_Actions', 'Actions_SubmenuSitesearch', array('module' => 'Actions', 'action' => 'indexSiteSearch'), true, 5);
}
}
protected function isSiteSearchEnabled()
{
$idSite = Common::getRequestVar('idSite', 0, 'int');
$idSites = Common::getRequestVar('idSites', '', 'string');
$idSites = Site::getIdSitesFromIdSitesString($idSites, true);
if (!empty($idSite)) {
@ -523,63 +128,20 @@ class Actions extends \Piwik\Plugin
return true;
}
static public function checkCustomVariablesPluginEnabled()
public static function checkCustomVariablesPluginEnabled()
{
if (!self::isCustomVariablesPluginsEnabled()) {
throw new \Exception("To Track Site Search Categories, please ask the Piwik Administrator to enable the 'Custom Variables' plugin in Settings > Plugins.");
}
}
static protected function isCustomVariablesPluginsEnabled()
public static function isCustomVariablesPluginsEnabled()
{
return \Piwik\Plugin\Manager::getInstance()->isPluginActivated('CustomVariables');
}
public function configureViewDataTable(ViewDataTable $view)
{
switch ($view->requestConfig->apiMethodToRequestDataTable) {
case 'Actions.getPageUrls':
$this->configureViewForPageUrls($view);
break;
case 'Actions.getEntryPageUrls':
$this->configureViewForEntryPageUrls($view);
break;
case 'Actions.getExitPageUrls':
$this->configureViewForExitPageUrls($view);
break;
case 'Actions.getSiteSearchKeywords':
$this->configureViewForSiteSearchKeywords($view);
break;
case 'Actions.getSiteSearchNoResultKeywords':
$this->configureViewForSiteSearchNoResultKeywords($view);
break;
case 'Actions.getSiteSearchCategories':
$this->configureViewForSiteSearchCategories($view);
break;
case 'Actions.getPageUrlsFollowingSiteSearch':
$this->configureViewForGetPageUrlsOrTitlesFollowingSiteSearch($view, false);
break;
case 'Actions.getPageTitlesFollowingSiteSearch':
$this->configureViewForGetPageUrlsOrTitlesFollowingSiteSearch($view, true);
break;
case 'Actions.getPageTitles':
$this->configureViewForGetPageTitles($view);
break;
case 'Actions.getEntryPageTitles':
$this->configureViewForGetEntryPageTitles($view);
break;
case 'Actions.getExitPageTitles':
$this->configureViewForGetExitPageTitles($view);
break;
case 'Actions.getDownloads':
$this->configureViewForGetDownloads($view);
break;
case 'Actions.getOutlinks':
$this->configureViewForGetOutlinks($view);
break;
}
if ($this->pluginName == $view->requestConfig->getApiModuleToRequest()) {
if ($view->isRequestingSingleDataTable()) {
// make sure custom visualizations are shown on actions reports
@ -591,40 +153,6 @@ class Actions extends \Piwik\Plugin
}
}
private function addBaseDisplayProperties(ViewDataTable $view)
{
$view->config->datatable_js_type = 'ActionsDataTable';
$view->config->search_recursive = true;
$view->config->show_table_all_columns = false;
$view->requestConfig->filter_limit = self::ACTIONS_REPORT_ROWS_DISPLAY;
$view->config->show_all_views_icons = false;
if ($view->isViewDataTableId(HtmlTable::ID)) {
$view->config->show_embedded_subtable = true;
}
// if the flat parameter is not provided, make sure it is set to 0 in the URL,
// so users can see that they can set it to 1 (see #3365)
$view->config->custom_parameters = array('flat' => 0);
if (Request::shouldLoadExpanded()) {
if ($view->isViewDataTableId(HtmlTable::ID)) {
$view->config->show_expanded = true;
}
$view->config->filters[] = function ($dataTable) {
Actions::setDataTableRowLevels($dataTable);
};
}
$view->config->filters[] = function ($dataTable) use ($view) {
if ($view->isViewDataTableId(HtmlTable::ID)) {
$view->config->datatable_css_class = 'dataTableActions';
}
};
}
/**
* @param \Piwik\DataTable $dataTable
* @param int $level
@ -641,297 +169,5 @@ class Actions extends \Piwik\Plugin
}
}
private function addExcludeLowPopDisplayProperties(ViewDataTable $view)
{
if (Common::getRequestVar('enable_filter_excludelowpop', '0', 'string') != '0') {
$view->requestConfig->filter_excludelowpop = 'nb_hits';
$view->requestConfig->filter_excludelowpop_value = function () {
// computing minimum value to exclude (2 percent of the total number of actions)
$visitsInfo = \Piwik\Plugins\VisitsSummary\Controller::getVisitsSummary()->getFirstRow();
$nbActions = $visitsInfo->getColumn('nb_actions');
$nbActionsLowPopulationThreshold = floor(0.02 * $nbActions);
// we remove 1 to make sure some actions/downloads are displayed in the case we have a very few of them
// and each of them has 1 or 2 hits...
return min($visitsInfo->getColumn('max_actions') - 1, $nbActionsLowPopulationThreshold - 1);
};
}
}
private function addPageDisplayProperties(ViewDataTable $view)
{
$view->config->addTranslations(array(
'nb_hits' => Piwik::translate('General_ColumnPageviews'),
'nb_visits' => Piwik::translate('General_ColumnUniquePageviews'),
'avg_time_on_page' => Piwik::translate('General_ColumnAverageTimeOnPage'),
'bounce_rate' => Piwik::translate('General_ColumnBounceRate'),
'exit_rate' => Piwik::translate('General_ColumnExitRate'),
'avg_time_generation' => Piwik::translate('General_ColumnAverageGenerationTime'),
));
// prettify avg_time_on_page column
$getPrettyTimeFromSeconds = '\Piwik\MetricsFormatter::getPrettyTimeFromSeconds';
$view->config->filters[] = array('ColumnCallbackReplace', array('avg_time_on_page', $getPrettyTimeFromSeconds));
// prettify avg_time_generation column
$avgTimeCallback = function ($time) {
return $time ? MetricsFormatter::getPrettyTimeFromSeconds($time, true, true, false) : "-";
};
$view->config->filters[] = array('ColumnCallbackReplace', array('avg_time_generation', $avgTimeCallback));
// add avg_generation_time tooltip
$tooltipCallback = function ($hits, $min, $max) {
if (!$hits) {
return false;
}
return Piwik::translate("Actions_AvgGenerationTimeTooltip", array(
$hits,
"<br />",
MetricsFormatter::getPrettyTimeFromSeconds($min),
MetricsFormatter::getPrettyTimeFromSeconds($max)
));
};
$view->config->filters[] = array('ColumnCallbackAddMetadata',
array(
array('nb_hits_with_time_generation', 'min_time_generation', 'max_time_generation'),
'avg_time_generation_tooltip',
$tooltipCallback
)
);
$this->addExcludeLowPopDisplayProperties($view);
}
public function configureViewForPageUrls(ViewDataTable $view)
{
$view->config->addTranslation('label', Piwik::translate('Actions_ColumnPageURL'));
$view->config->columns_to_display = array('label', 'nb_hits', 'nb_visits', 'bounce_rate',
'avg_time_on_page', 'exit_rate', 'avg_time_generation');
$this->addPageDisplayProperties($view);
$this->addBaseDisplayProperties($view);
}
public function configureViewForEntryPageUrls(ViewDataTable $view)
{
// link to the page, not just the report, but only if not a widget
$widget = Common::getRequestVar('widget', false);
$view->config->self_url = Request::getCurrentUrlWithoutGenericFilters(array(
'module' => 'Actions',
'action' => $widget === false ? 'indexEntryPageUrls' : 'getEntryPageUrls'
));
$view->config->addTranslations(array(
'label' => Piwik::translate('Actions_ColumnEntryPageURL'),
'entry_bounce_count' => Piwik::translate('General_ColumnBounces'),
'entry_nb_visits' => Piwik::translate('General_ColumnEntrances'))
);
$view->config->title = Piwik::translate('Actions_SubmenuPagesEntry');
$view->config->addRelatedReport('Actions.getEntryPageTitles', Piwik::translate('Actions_EntryPageTitles'));
$view->config->columns_to_display = array('label', 'entry_nb_visits', 'entry_bounce_count', 'bounce_rate');
$view->requestConfig->filter_sort_column = 'entry_nb_visits';
$view->requestConfig->filter_sort_order = 'desc';
$this->addPageDisplayProperties($view);
$this->addBaseDisplayProperties($view);
}
public function configureViewForExitPageUrls(ViewDataTable $view)
{
// link to the page, not just the report, but only if not a widget
$widget = Common::getRequestVar('widget', false);
$view->config->self_url = Request::getCurrentUrlWithoutGenericFilters(array(
'module' => 'Actions',
'action' => $widget === false ? 'indexExitPageUrls' : 'getExitPageUrls'
));
$view->config->addTranslations(array(
'label' => Piwik::translate('Actions_ColumnExitPageURL'),
'exit_nb_visits' => Piwik::translate('General_ColumnExits'))
);
$view->config->title = Piwik::translate('Actions_SubmenuPagesExit');
$view->config->addRelatedReport('Actions.getExitPageTitles', Piwik::translate('Actions_ExitPageTitles'));
$view->config->columns_to_display = array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate');
$view->requestConfig->filter_sort_column = 'exit_nb_visits';
$view->requestConfig->filter_sort_order = 'desc';
$this->addPageDisplayProperties($view);
$this->addBaseDisplayProperties($view);
}
private function addSiteSearchDisplayProperties(ViewDataTable $view)
{
$view->config->addTranslations(array(
'nb_visits' => Piwik::translate('Actions_ColumnSearches'),
'exit_rate' => str_replace("% ", "%&nbsp;", Piwik::translate('Actions_ColumnSearchExits')),
'nb_pages_per_search' => Piwik::translate('Actions_ColumnPagesPerSearch')
));
$view->config->show_bar_chart = false;
$view->config->show_table_all_columns = false;
}
public function configureViewForSiteSearchKeywords(ViewDataTable $view)
{
$view->config->addTranslation('label', Piwik::translate('General_ColumnKeyword'));
$view->config->columns_to_display = array('label', 'nb_visits', 'nb_pages_per_search', 'exit_rate');
$this->addSiteSearchDisplayProperties($view);
}
public function configureViewForSiteSearchNoResultKeywords(ViewDataTable $view)
{
$view->config->addTranslation('label', Piwik::translate('Actions_ColumnNoResultKeyword'));
$view->config->columns_to_display = array('label', 'nb_visits', 'exit_rate');
$this->addSiteSearchDisplayProperties($view);
}
public function configureViewForSiteSearchCategories(ViewDataTable $view)
{
$view->config->addTranslations(array(
'label' => Piwik::translate('Actions_ColumnSearchCategory'),
'nb_visits' => Piwik::translate('Actions_ColumnSearches'),
'nb_pages_per_search' => Piwik::translate('Actions_ColumnPagesPerSearch')
));
$view->config->columns_to_display = array('label', 'nb_visits', 'nb_pages_per_search');
$view->config->show_table_all_columns = false;
$view->config->show_bar_chart = false;
if ($view->isViewDataTableId(HtmlTable::ID)) {
$view->config->disable_row_evolution = false;
}
}
public function configureViewForGetPageUrlsOrTitlesFollowingSiteSearch(ViewDataTable $view, $isTitle)
{
$title = $isTitle ? Piwik::translate('Actions_WidgetPageTitlesFollowingSearch')
: Piwik::translate('Actions_WidgetPageUrlsFollowingSearch');
$relatedReports = array(
'Actions.getPageTitlesFollowingSiteSearch' => Piwik::translate('Actions_WidgetPageTitlesFollowingSearch'),
'Actions.getPageUrlsFollowingSiteSearch' => Piwik::translate('Actions_WidgetPageUrlsFollowingSearch'),
);
$view->config->addRelatedReports($relatedReports);
$view->config->addTranslations(array(
'label' => Piwik::translate('General_ColumnDestinationPage'),
'nb_hits_following_search' => Piwik::translate('General_ColumnViewedAfterSearch'),
'nb_hits' => Piwik::translate('General_ColumnTotalPageviews')
));
$view->config->title = $title;
$view->config->columns_to_display = array('label', 'nb_hits_following_search', 'nb_hits');
$view->config->show_exclude_low_population = false;
$view->requestConfig->filter_sort_column = 'nb_hits_following_search';
$view->requestConfig->filter_sort_order = 'desc';
$this->addExcludeLowPopDisplayProperties($view);
$this->addBaseDisplayProperties($view);
}
public function configureViewForGetPageTitles(ViewDataTable $view)
{
// link to the page, not just the report, but only if not a widget
$widget = Common::getRequestVar('widget', false);
$view->config->self_url = Request::getCurrentUrlWithoutGenericFilters(array(
'module' => 'Actions',
'action' => $widget === false ? 'indexPageTitles' : 'getPageTitles'
));
$view->config->title = Piwik::translate('Actions_SubmenuPageTitles');
$view->config->addRelatedReports(array(
'Actions.getEntryPageTitles' => Piwik::translate('Actions_EntryPageTitles'),
'Actions.getExitPageTitles' => Piwik::translate('Actions_ExitPageTitles'),
));
$view->config->addTranslation('label', Piwik::translate('Actions_ColumnPageName'));
$view->config->columns_to_display = array('label', 'nb_hits', 'nb_visits', 'bounce_rate',
'avg_time_on_page', 'exit_rate', 'avg_time_generation');
$this->addPageDisplayProperties($view);
$this->addBaseDisplayProperties($view);
}
public function configureViewForGetEntryPageTitles(ViewDataTable $view)
{
$entryPageUrlAction =
Common::getRequestVar('widget', false) === false ? 'indexEntryPageUrls' : 'getEntryPageUrls';
$view->config->addTranslations(array(
'label' => Piwik::translate('Actions_ColumnEntryPageTitle'),
'entry_bounce_count' => Piwik::translate('General_ColumnBounces'),
'entry_nb_visits' => Piwik::translate('General_ColumnEntrances'),
));
$view->config->addRelatedReports(array(
'Actions.getPageTitles' => Piwik::translate('Actions_SubmenuPageTitles'),
"Actions.$entryPageUrlAction" => Piwik::translate('Actions_SubmenuPagesEntry')
));
$view->config->columns_to_display = array('label', 'entry_nb_visits', 'entry_bounce_count', 'bounce_rate');
$view->config->title = Piwik::translate('Actions_EntryPageTitles');
$view->requestConfig->filter_sort_column = 'entry_nb_visits';
$this->addPageDisplayProperties($view);
$this->addBaseDisplayProperties($view);
}
public function configureViewForGetExitPageTitles(ViewDataTable $view)
{
$exitPageUrlAction =
Common::getRequestVar('widget', false) === false ? 'indexExitPageUrls' : 'getExitPageUrls';
$view->config->addTranslations(array(
'label' => Piwik::translate('Actions_ColumnExitPageTitle'),
'exit_nb_visits' => Piwik::translate('General_ColumnExits'),
));
$view->config->addRelatedReports(array(
'Actions.getPageTitles' => Piwik::translate('Actions_SubmenuPageTitles'),
"Actions.$exitPageUrlAction" => Piwik::translate('Actions_SubmenuPagesExit'),
));
$view->config->title = Piwik::translate('Actions_ExitPageTitles');
$view->config->columns_to_display = array('label', 'exit_nb_visits', 'nb_visits', 'exit_rate');
$this->addPageDisplayProperties($view);
$this->addBaseDisplayProperties($view);
}
public function configureViewForGetDownloads(ViewDataTable $view)
{
$view->config->addTranslations(array(
'label' => Piwik::translate('Actions_ColumnDownloadURL'),
'nb_visits' => Piwik::translate('Actions_ColumnUniqueDownloads'),
'nb_hits' => Piwik::translate('General_Downloads'),
));
$view->config->columns_to_display = array('label', 'nb_visits', 'nb_hits');
$view->config->show_exclude_low_population = false;
$this->addBaseDisplayProperties($view);
}
public function configureViewForGetOutlinks(ViewDataTable $view)
{
$view->config->addTranslations(array(
'label' => Piwik::translate('Actions_ColumnClickedURL'),
'nb_visits' => Piwik::translate('Actions_ColumnUniqueClicks'),
'nb_hits' => Piwik::translate('Actions_ColumnClicks'),
));
$view->config->columns_to_display = array('label', 'nb_visits', 'nb_hits');
$view->config->show_exclude_low_population = false;
$this->addBaseDisplayProperties($view);
}
}

View file

@ -0,0 +1,71 @@
<?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\Actions\Actions;
use Piwik\Common;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visit;
/**
* This class represents an outlink.
* This is a particular type of Action: it has no 'name'
*
*/
class ActionClickUrl extends Action
{
public function __construct(Request $request)
{
parent::__construct(self::TYPE_OUTLINK, $request);
$this->setActionUrlWithoutExcludingParameters($request->getParam('link'));
}
public static function shouldHandle(Request $request)
{
$outlinkUrl = $request->getParam('link');
return !empty($outlinkUrl);
}
protected function getActionsToLookup()
{
return array(
// Note: we do not normalize outlink URL
'idaction_url' => array($this->getActionUrl(), $this->getActionType())
);
}
public function writeDebugInfo()
{
parent::writeDebugInfo();
if ($this->detectActionIsOutlinkOnAliasHost($this, $this->request->getIdSite())) {
Common::printDebug("INFO: The outlink URL host is one of the known host for this website. ");
}
}
/**
* Detect whether action is an outlink given host aliases
*
* @param Action $action
* @return bool true if the outlink the visitor clicked on points to one of the known hosts for this website
*/
protected function detectActionIsOutlinkOnAliasHost(Action $action, $idSite)
{
$decodedActionUrl = $action->getActionUrl();
$actionUrlParsed = @parse_url($decodedActionUrl);
if (!isset($actionUrlParsed['host'])) {
return false;
}
return Visit::isHostKnownAliasHost($actionUrlParsed['host'], $idSite);
}
}

View file

@ -0,0 +1,41 @@
<?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\Actions\Actions;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
/**
* This class represents a download.
* This is a particular type of Action: it has no 'name'
*/
class ActionDownloadUrl extends Action
{
public function __construct(Request $request)
{
parent::__construct(self::TYPE_DOWNLOAD, $request);
$this->setActionUrlWithoutExcludingParameters($request->getParam('download'));
}
public static function shouldHandle(Request $request)
{
$downloadUrl = $request->getParam('download');
return !empty($downloadUrl);
}
protected function getActionsToLookup()
{
return array(
// Note: we do not normalize download URL
'idaction_url' => array($this->getActionUrl(), $this->getActionType())
);
}
}

View file

@ -0,0 +1,270 @@
<?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\Actions\Actions;
use Piwik\Common;
use Piwik\Tracker\Action;
use Piwik\Tracker\PageUrl;
use Piwik\Tracker\Request;
use Piwik\Tracker\Cache;
use Piwik\UrlHelper;
/**
* This class represents a search on the site.
* - Its name is the search keyword
* - by default the URL is not recorded (since it's not used)
* - tracks site search result count and site search category as custom variables
*
*/
class ActionSiteSearch extends Action
{
private $searchCategory = false;
private $searchCount = false;
const CVAR_KEY_SEARCH_CATEGORY = '_pk_scat';
const CVAR_KEY_SEARCH_COUNT = '_pk_scount';
const CVAR_INDEX_SEARCH_CATEGORY = '4';
const CVAR_INDEX_SEARCH_COUNT = '5';
public function __construct(Request $request, $detect = true)
{
parent::__construct(Action::TYPE_SITE_SEARCH, $request);
$this->originalUrl = $request->getParam('url');
if ($detect) {
$this->isSearchDetected();
}
}
public static function shouldHandle(Request $request)
{
$search = new self($request, false);
return $search->detectSiteSearch($request->getParam('url'));
}
protected function getActionsToLookup()
{
return array(
'idaction_name' => array($this->getActionName(), Action::TYPE_SITE_SEARCH),
);
}
public function getIdActionUrl()
{
// Site Search, by default, will not track URL. We do not want URL to appear as "Page URL not defined"
// so we specifically set it to NULL in the table (the archiving query does IS NOT NULL)
return null;
}
public function getCustomFloatValue()
{
return $this->request->getPageGenerationTime();
}
protected function isSearchDetected()
{
$siteSearch = $this->detectSiteSearch($this->originalUrl);
if (empty($siteSearch)) {
return false;
}
list($actionName, $url, $category, $count) = $siteSearch;
if (!empty($category)) {
$this->searchCategory = trim($category);
}
if ($count !== false) {
$this->searchCount = $count;
}
$this->setActionName($actionName);
$this->setActionUrl($url);
return true;
}
public function getCustomVariables()
{
$customVariables = parent::getCustomVariables();
// Enrich Site Search actions with Custom Variables, overwriting existing values
if (!empty($this->searchCategory)) {
if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY])) {
Common::printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_CATEGORY . " for this page view");
}
$customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_CATEGORY] = self::CVAR_KEY_SEARCH_CATEGORY;
$customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_CATEGORY] = Request::truncateCustomVariable($this->searchCategory);
}
if ($this->searchCount !== false) {
if (!empty($customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT])) {
Common::printDebug("WARNING: Overwriting existing Custom Variable in slot " . self::CVAR_INDEX_SEARCH_COUNT . " for this page view");
}
$customVariables['custom_var_k' . self::CVAR_INDEX_SEARCH_COUNT] = self::CVAR_KEY_SEARCH_COUNT;
$customVariables['custom_var_v' . self::CVAR_INDEX_SEARCH_COUNT] = (int)$this->searchCount;
}
return $customVariables;
}
protected function detectSiteSearchFromUrl($website, $parsedUrl)
{
$doRemoveSearchParametersFromUrl = true;
$separator = '&';
$count = $actionName = $categoryName = false;
$keywordParameters = isset($website['sitesearch_keyword_parameters'])
? $website['sitesearch_keyword_parameters']
: array();
$queryString = (!empty($parsedUrl['query']) ? $parsedUrl['query'] : '') . (!empty($parsedUrl['fragment']) ? $separator . $parsedUrl['fragment'] : '');
$parametersRaw = UrlHelper::getArrayFromQueryString($queryString);
// strtolower the parameter names for smooth site search detection
$parameters = array();
foreach ($parametersRaw as $k => $v) {
$parameters[Common::mb_strtolower($k)] = $v;
}
// decode values if they were sent from a client using another charset
$pageEncoding = $this->request->getParam('cs');
PageUrl::reencodeParameters($parameters, $pageEncoding);
// Detect Site Search keyword
foreach ($keywordParameters as $keywordParameterRaw) {
$keywordParameter = Common::mb_strtolower($keywordParameterRaw);
if (!empty($parameters[$keywordParameter])) {
$actionName = $parameters[$keywordParameter];
break;
}
}
if (empty($actionName)) {
return false;
}
$categoryParameters = isset($website['sitesearch_category_parameters'])
? $website['sitesearch_category_parameters']
: array();
foreach ($categoryParameters as $categoryParameterRaw) {
$categoryParameter = Common::mb_strtolower($categoryParameterRaw);
if (!empty($parameters[$categoryParameter])) {
$categoryName = $parameters[$categoryParameter];
break;
}
}
if (isset($parameters['search_count'])
&& $this->isValidSearchCount($parameters['search_count'])
) {
$count = $parameters['search_count'];
}
// Remove search kwd from URL
if ($doRemoveSearchParametersFromUrl) {
// @see excludeQueryParametersFromUrl()
// Excluded the detected parameters from the URL
$parametersToExclude = array($categoryParameterRaw, $keywordParameterRaw);
if (isset($parsedUrl['query'])) {
$parsedUrl['query'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['query']), $parametersToExclude);
}
if (isset($parsedUrl['fragment'])) {
$parsedUrl['fragment'] = UrlHelper::getQueryStringWithExcludedParameters(UrlHelper::getArrayFromQueryString($parsedUrl['fragment']), $parametersToExclude);
}
}
$url = UrlHelper::getParseUrlReverse($parsedUrl);
if (is_array($actionName)) {
$actionName = reset($actionName);
}
$actionName = PageUrl::urldecodeValidUtf8($actionName);
$actionName = trim($actionName);
if (empty($actionName)) {
return false;
}
if (is_array($categoryName)) {
$categoryName = reset($categoryName);
}
$categoryName = PageUrl::urldecodeValidUtf8($categoryName);
$categoryName = trim($categoryName);
return array($url, $actionName, $categoryName, $count);
}
protected function isValidSearchCount($count)
{
return is_numeric($count) && $count >= 0;
}
public function detectSiteSearch($originalUrl)
{
$website = Cache::getCacheWebsiteAttributes($this->request->getIdSite());
if (empty($website['sitesearch'])) {
Common::printDebug("Internal 'Site Search' tracking is not enabled for this site. ");
return false;
}
$actionName = $url = $categoryName = $count = false;
$originalUrl = PageUrl::cleanupUrl($originalUrl);
// Detect Site search from Tracking API parameters rather than URL
$searchKwd = $this->request->getParam('search');
if (!empty($searchKwd)) {
$actionName = $searchKwd;
$isCategoryName = $this->request->getParam('search_cat');
if (!empty($isCategoryName)) {
$categoryName = $isCategoryName;
}
$isCount = $this->request->getParam('search_count');
if ($this->isValidSearchCount($isCount)) {
$count = $isCount;
}
}
if (empty($actionName)) {
$parsedUrl = @parse_url($originalUrl);
// Detect Site Search from URL query parameters
if (!empty($parsedUrl['query']) || !empty($parsedUrl['fragment'])) {
// array($url, $actionName, $categoryName, $count);
$searchInfo = $this->detectSiteSearchFromUrl($website, $parsedUrl);
if (!empty($searchInfo)) {
list ($url, $actionName, $categoryName, $count) = $searchInfo;
}
}
}
$actionName = trim($actionName);
$categoryName = trim($categoryName);
if (empty($actionName)) {
Common::printDebug("(this is not a Site Search request)");
return false;
}
Common::printDebug("Detected Site Search keyword '$actionName'. ");
if (!empty($categoryName)) {
Common::printDebug("- Detected Site Search Category '$categoryName'. ");
}
if ($count !== false) {
Common::printDebug("- Search Results Count was '$count'. ");
}
if ($url != $originalUrl) {
Common::printDebug("NOTE: The Page URL was changed / removed, during the Site Search detection, was '$originalUrl', now is '$url'");
}
return array(
$actionName,
$url,
$categoryName,
$count
);
}
}

View file

@ -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
@ -9,10 +9,10 @@
namespace Piwik\Plugins\Actions;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Metrics as PiwikMetrics;
use Piwik\RankingQuery;
use Piwik\Tracker\Action;
use Piwik\Tracker\ActionSiteSearch;
use Piwik\Plugins\Actions\Actions\ActionSiteSearch;
/**
* Class encapsulating logic to process Day/Period Archiving for the Actions reports
@ -37,39 +37,6 @@ class Archiver extends \Piwik\Plugin\Archiver
const METRIC_SEARCHES_RECORD_NAME = 'Actions_nb_searches';
const METRIC_KEYWORDS_RECORD_NAME = 'Actions_nb_keywords';
/* Metrics in use by the API Actions.get */
public static $actionsAggregateMetrics = array(
self::METRIC_PAGEVIEWS_RECORD_NAME => 'nb_pageviews',
self::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME => 'nb_uniq_pageviews',
self::METRIC_DOWNLOADS_RECORD_NAME => 'nb_downloads',
self::METRIC_UNIQ_DOWNLOADS_RECORD_NAME => 'nb_uniq_downloads',
self::METRIC_OUTLINKS_RECORD_NAME => 'nb_outlinks',
self::METRIC_UNIQ_OUTLINKS_RECORD_NAME => 'nb_uniq_outlinks',
self::METRIC_SEARCHES_RECORD_NAME => 'nb_searches',
self::METRIC_KEYWORDS_RECORD_NAME => 'nb_keywords',
);
public static $actionTypes = array(
Action::TYPE_PAGE_URL,
Action::TYPE_OUTLINK,
Action::TYPE_DOWNLOAD,
Action::TYPE_PAGE_TITLE,
Action::TYPE_SITE_SEARCH,
);
static protected $columnsToRenameAfterAggregation = array(
Metrics::INDEX_NB_UNIQ_VISITORS => Metrics::INDEX_SUM_DAILY_NB_UNIQ_VISITORS,
Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS => Metrics::INDEX_PAGE_ENTRY_SUM_DAILY_NB_UNIQ_VISITORS,
Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS => Metrics::INDEX_PAGE_EXIT_SUM_DAILY_NB_UNIQ_VISITORS,
);
static public $columnsToDeleteAfterAggregation = array(
Metrics::INDEX_NB_UNIQ_VISITORS,
Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
);
private static $columnsAggregationOperation = array(
Metrics::INDEX_PAGE_MAX_TIME_GENERATION => 'max',
Metrics::INDEX_PAGE_MIN_TIME_GENERATION => 'min'
);
protected $actionsTablesByType = null;
protected $isSiteSearchEnabled = false;
@ -121,7 +88,7 @@ class Archiver extends \Piwik\Plugin\Archiver
/**
* @return string
*/
static public function getWhereClauseActionIsNotEvent()
public static function getWhereClauseActionIsNotEvent()
{
return " AND log_link_visit_action.idaction_event_category IS NULL";
}
@ -133,10 +100,10 @@ class Archiver extends \Piwik\Plugin\Archiver
protected function updateQuerySelectFromForSiteSearch(&$select, &$from)
{
$selectFlagNoResultKeywords = ",
CASE WHEN (MAX(log_link_visit_action.custom_var_v" . ActionSiteSearch::CVAR_INDEX_SEARCH_COUNT . ") = 0
AND log_link_visit_action.custom_var_k" . ActionSiteSearch::CVAR_INDEX_SEARCH_COUNT . " = '" . ActionSiteSearch::CVAR_KEY_SEARCH_COUNT . "')
THEN 1 ELSE 0 END
AS `" . Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`";
CASE WHEN (MAX(log_link_visit_action.custom_var_v" . ActionSiteSearch::CVAR_INDEX_SEARCH_COUNT . ") = 0
AND log_link_visit_action.custom_var_k" . ActionSiteSearch::CVAR_INDEX_SEARCH_COUNT . " = '" . ActionSiteSearch::CVAR_KEY_SEARCH_COUNT . "')
THEN 1 ELSE 0 END
AS `" . PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT . "`";
//we need an extra JOIN to know whether the referrer "idaction_name_ref" was a Site Search request
$from[] = array(
@ -146,9 +113,9 @@ class Archiver extends \Piwik\Plugin\Archiver
);
$selectPageIsFollowingSiteSearch = ",
SUM( CASE WHEN log_action_name_ref.type = " . Action::TYPE_SITE_SEARCH . "
THEN 1 ELSE 0 END)
AS `" . Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS . "`";
SUM( CASE WHEN log_action_name_ref.type = " . Action::TYPE_SITE_SEARCH . "
THEN 1 ELSE 0 END)
AS `" . PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS . "`";
$select .= $selectFlagNoResultKeywords
. $selectPageIsFollowingSiteSearch;
@ -160,7 +127,7 @@ class Archiver extends \Piwik\Plugin\Archiver
private function initActionsTables()
{
$this->actionsTablesByType = array();
foreach (self::$actionTypes as $type) {
foreach (Metrics::$actionTypes as $type) {
$dataTable = new DataTable();
$dataTable->setMaximumAllowedRows(ArchivingHelper::$maximumRowsInDataTableLevelZero);
@ -168,7 +135,7 @@ class Archiver extends \Piwik\Plugin\Archiver
|| $type == Action::TYPE_PAGE_TITLE
) {
// for page urls and page titles, performance metrics exist and have to be aggregated correctly
$dataTable->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, self::$columnsAggregationOperation);
$dataTable->setMetadata(DataTable::COLUMN_AGGREGATION_OPS_METADATA_NAME, Metrics::$columnsAggregationOperation);
}
$this->actionsTablesByType[$type] = $dataTable;
@ -177,30 +144,14 @@ class Archiver extends \Piwik\Plugin\Archiver
protected function archiveDayActions($rankingQueryLimit)
{
$metricsConfig = Metrics::getActionMetrics();
$select = "log_action.name,
log_action.type,
log_action.idaction,
log_action.url_prefix,
count(distinct log_link_visit_action.idvisit) as `" . Metrics::INDEX_NB_VISITS . "`,
count(distinct log_link_visit_action.idvisitor) as `" . Metrics::INDEX_NB_UNIQ_VISITORS . "`,
count(*) as `" . Metrics::INDEX_PAGE_NB_HITS . "`,
sum(
case when " . Action::DB_COLUMN_CUSTOM_FLOAT . " is null
then 0
else " . Action::DB_COLUMN_CUSTOM_FLOAT . "
end
) / 1000 as `" . Metrics::INDEX_PAGE_SUM_TIME_GENERATION . "`,
sum(
case when " . Action::DB_COLUMN_CUSTOM_FLOAT . " is null
then 0
else 1
end
) as `" . Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION . "`,
min(" . Action::DB_COLUMN_CUSTOM_FLOAT . ") / 1000
as `" . Metrics::INDEX_PAGE_MIN_TIME_GENERATION . "`,
max(" . Action::DB_COLUMN_CUSTOM_FLOAT . ") / 1000
as `" . Metrics::INDEX_PAGE_MAX_TIME_GENERATION . "`
";
log_action.type,
log_action.idaction,
log_action.url_prefix";
$select = $this->addMetricsToSelect($select, $metricsConfig);
$from = array(
"log_link_visit_action",
@ -211,29 +162,28 @@ class Archiver extends \Piwik\Plugin\Archiver
);
$where = "log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?
AND log_link_visit_action.%s IS NOT NULL"
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?
AND log_link_visit_action.%s IS NOT NULL"
. $this->getWhereClauseActionIsNotEvent();
$groupBy = "log_action.idaction";
$orderBy = "`" . Metrics::INDEX_PAGE_NB_HITS . "` DESC, name ASC";
$orderBy = "`" . PiwikMetrics::INDEX_PAGE_NB_HITS . "` DESC, name ASC";
$rankingQuery = false;
if ($rankingQueryLimit > 0) {
$rankingQuery = new RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn(array('idaction', 'name'));
$rankingQuery->addColumn(array('url_prefix', Metrics::INDEX_NB_UNIQ_VISITORS));
$rankingQuery->addColumn(array(Metrics::INDEX_PAGE_NB_HITS, Metrics::INDEX_NB_VISITS), 'sum');
$rankingQuery->addColumn('url_prefix');
if ($this->isSiteSearchEnabled()) {
$rankingQuery->addColumn(Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min');
$rankingQuery->addColumn(Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum');
$rankingQuery->addColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min');
$rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS, 'sum');
}
$rankingQuery->addColumn(Metrics::INDEX_PAGE_SUM_TIME_GENERATION, 'sum');
$rankingQuery->addColumn(Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, 'sum');
$rankingQuery->addColumn(Metrics::INDEX_PAGE_MIN_TIME_GENERATION, 'min');
$rankingQuery->addColumn(Metrics::INDEX_PAGE_MAX_TIME_GENERATION, 'max');
$this->addMetricsToRankingQuery($rankingQuery, $metricsConfig);
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
}
@ -241,12 +191,33 @@ class Archiver extends \Piwik\Plugin\Archiver
// 1) No result Keywords
// 2) For each page view, count number of times the referrer page was a Site Search
if ($this->isSiteSearchEnabled()) {
$this->updateQuerySelectFromForSiteSearch($select, $from);
$this->updateQuerySelectFromForSiteSearch($select, $from);
}
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_name", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "idaction_name", $rankingQuery, $metricsConfig);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "idaction_url", $rankingQuery, $metricsConfig);
}
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_url", $rankingQuery);
private function addMetricsToSelect($select, $metricsConfig)
{
if (!empty($metricsConfig)) {
foreach ($metricsConfig as $metric => $config) {
$select .= ', ' . $config['query'] . " as `" . $metric . "`";
}
}
return $select;
}
private function addMetricsToRankingQuery(RankingQuery $rankingQuery, $metricsConfig)
{
foreach ($metricsConfig as $metric => $config) {
if (!empty($config['aggregation'])) {
$rankingQuery->addColumn($metric, $config['aggregation']);
} else {
$rankingQuery->addColumn($metric);
}
}
}
protected function isSiteSearchEnabled()
@ -254,7 +225,7 @@ class Archiver extends \Piwik\Plugin\Archiver
return $this->isSiteSearchEnabled;
}
protected function archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, $sprintfField, $rankingQuery = false)
protected function archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, $sprintfField, RankingQuery $rankingQuery = null, $metricsConfig = array())
{
$select = sprintf($select, $sprintfField);
@ -266,12 +237,12 @@ class Archiver extends \Piwik\Plugin\Archiver
// apply ranking query
if ($rankingQuery) {
$querySql = $rankingQuery->generateQuery($querySql);
$querySql = $rankingQuery->generateRankingQuery($querySql);
}
// get result
$resultSet = $this->getLogAggregator()->getDb()->query($querySql, $query['bind']);
$modified = ArchivingHelper::updateActionsTableWithRowQuery($resultSet, $sprintfField, $this->actionsTablesByType);
$modified = ArchivingHelper::updateActionsTableWithRowQuery($resultSet, $sprintfField, $this->actionsTablesByType, $metricsConfig);
return $modified;
}
@ -285,11 +256,11 @@ class Archiver extends \Piwik\Plugin\Archiver
$rankingQuery = new RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn('idaction');
$rankingQuery->addColumn(Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
$rankingQuery->addColumn(array(Metrics::INDEX_PAGE_ENTRY_NB_VISITS,
Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS,
Metrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
Metrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
$rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS);
$rankingQuery->addColumn(array(PiwikMetrics::INDEX_PAGE_ENTRY_NB_VISITS,
PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS,
PiwikMetrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
PiwikMetrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT), 'sum');
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
$extraSelects = 'log_action.type, log_action.name,';
@ -300,7 +271,7 @@ class Archiver extends \Piwik\Plugin\Archiver
"joinOn" => "log_visit.%s = log_action.idaction"
)
);
$orderBy = "`" . Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC";
$orderBy = "`" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "` DESC, log_action.name ASC";
} else {
$extraSelects = false;
$from = "log_visit";
@ -308,22 +279,22 @@ class Archiver extends \Piwik\Plugin\Archiver
}
$select = "log_visit.%s as idaction, $extraSelects
count(distinct log_visit.idvisitor) as `" . Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS . "`,
count(*) as `" . Metrics::INDEX_PAGE_ENTRY_NB_VISITS . "`,
sum(log_visit.visit_total_actions) as `" . Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "`,
sum(log_visit.visit_total_time) as `" . Metrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH . "`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . Metrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT . "`";
count(distinct log_visit.idvisitor) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS . "`,
count(*) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_VISITS . "`,
sum(log_visit.visit_total_actions) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS . "`,
sum(log_visit.visit_total_time) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH . "`,
sum(case log_visit.visit_total_actions when 1 then 1 when 0 then 1 else 0 end) as `" . PiwikMetrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT . "`";
$where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
AND log_visit.%s > 0";
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
AND log_visit.%s > 0";
$groupBy = "log_visit.%s, idaction";
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_entry_idaction_url", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "visit_entry_idaction_url", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_entry_idaction_name", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "visit_entry_idaction_name", $rankingQuery);
}
/**
@ -336,8 +307,8 @@ class Archiver extends \Piwik\Plugin\Archiver
$rankingQuery = new RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn('idaction');
$rankingQuery->addColumn(Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
$rankingQuery->addColumn(Metrics::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
$rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS);
$rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_EXIT_NB_VISITS, 'sum');
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
$extraSelects = 'log_action.type, log_action.name,';
@ -348,7 +319,7 @@ class Archiver extends \Piwik\Plugin\Archiver
"joinOn" => "log_visit.%s = log_action.idaction"
)
);
$orderBy = "`" . Metrics::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC";
$orderBy = "`" . PiwikMetrics::INDEX_PAGE_EXIT_NB_VISITS . "` DESC, log_action.name ASC";
} else {
$extraSelects = false;
$from = "log_visit";
@ -356,19 +327,19 @@ class Archiver extends \Piwik\Plugin\Archiver
}
$select = "log_visit.%s as idaction, $extraSelects
count(distinct log_visit.idvisitor) as `" . Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS . "`,
count(*) as `" . Metrics::INDEX_PAGE_EXIT_NB_VISITS . "`";
count(distinct log_visit.idvisitor) as `" . PiwikMetrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS . "`,
count(*) as `" . PiwikMetrics::INDEX_PAGE_EXIT_NB_VISITS . "`";
$where = "log_visit.visit_last_action_time >= ?
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
AND log_visit.%s > 0";
AND log_visit.visit_last_action_time <= ?
AND log_visit.idsite = ?
AND log_visit.%s > 0";
$groupBy = "log_visit.%s, idaction";
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_exit_idaction_url", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "visit_exit_idaction_url", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "visit_exit_idaction_name", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "visit_exit_idaction_name", $rankingQuery);
return array($rankingQuery, $extraSelects, $from, $orderBy, $select, $where, $groupBy);
}
@ -382,10 +353,10 @@ class Archiver extends \Piwik\Plugin\Archiver
$rankingQuery = new RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn('idaction');
$rankingQuery->addColumn(Metrics::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
$rankingQuery->addColumn(PiwikMetrics::INDEX_PAGE_SUM_TIME_SPENT, 'sum');
$rankingQuery->partitionResultIntoMultipleGroups('type', array_keys($this->actionsTablesByType));
$extraSelects = "log_action.type, log_action.name, count(*) as `" . Metrics::INDEX_PAGE_NB_HITS . "`,";
$extraSelects = "log_action.type, log_action.name, count(*) as `" . PiwikMetrics::INDEX_PAGE_NB_HITS . "`,";
$from = array(
"log_link_visit_action",
array(
@ -393,7 +364,7 @@ class Archiver extends \Piwik\Plugin\Archiver
"joinOn" => "log_link_visit_action.%s = log_action.idaction"
)
);
$orderBy = "`" . Metrics::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC";
$orderBy = "`" . PiwikMetrics::INDEX_PAGE_NB_HITS . "` DESC, log_action.name ASC";
} else {
$extraSelects = false;
$from = "log_link_visit_action";
@ -401,20 +372,20 @@ class Archiver extends \Piwik\Plugin\Archiver
}
$select = "log_link_visit_action.%s as idaction, $extraSelects
sum(log_link_visit_action.time_spent_ref_action) as `" . Metrics::INDEX_PAGE_SUM_TIME_SPENT . "`";
sum(log_link_visit_action.time_spent_ref_action) as `" . PiwikMetrics::INDEX_PAGE_SUM_TIME_SPENT . "`";
$where = "log_link_visit_action.server_time >= ?
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?
AND log_link_visit_action.time_spent_ref_action > 0
AND log_link_visit_action.%s > 0"
AND log_link_visit_action.server_time <= ?
AND log_link_visit_action.idsite = ?
AND log_link_visit_action.time_spent_ref_action > 0
AND log_link_visit_action.%s > 0"
. $this->getWhereClauseActionIsNotEvent();
$groupBy = "log_link_visit_action.%s, idaction";
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_url_ref", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "idaction_url_ref", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $orderBy, $groupBy, "idaction_name_ref", $rankingQuery);
$this->archiveDayQueryProcess($select, $from, $where, $groupBy, $orderBy, "idaction_name_ref", $rankingQuery);
}
/**
@ -437,10 +408,10 @@ class Archiver extends \Piwik\Plugin\Archiver
$this->insertTable($dataTable, self::PAGE_URLS_RECORD_NAME);
$records = array(
self::METRIC_PAGEVIEWS_RECORD_NAME => array_sum($dataTable->getColumn(Metrics::INDEX_PAGE_NB_HITS)),
self::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME => array_sum($dataTable->getColumn(Metrics::INDEX_NB_VISITS)),
self::METRIC_SUM_TIME_RECORD_NAME => array_sum($dataTable->getColumn(Metrics::INDEX_PAGE_SUM_TIME_GENERATION)),
self::METRIC_HITS_TIMED_RECORD_NAME => array_sum($dataTable->getColumn(Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION))
self::METRIC_PAGEVIEWS_RECORD_NAME => array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)),
self::METRIC_UNIQ_PAGEVIEWS_RECORD_NAME => array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)),
self::METRIC_SUM_TIME_RECORD_NAME => array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION)),
self::METRIC_HITS_TIMED_RECORD_NAME => array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION))
);
$this->getProcessor()->insertNumericRecords($records);
}
@ -461,14 +432,13 @@ class Archiver extends \Piwik\Plugin\Archiver
$this->getProcessor()->insertBlobRecord($recordName, $report);
}
protected function insertDownloadsReports()
{
$dataTable = $this->getDataTable(Action::TYPE_DOWNLOAD);
$this->insertTable($dataTable, self::DOWNLOADS_RECORD_NAME);
$this->getProcessor()->insertNumericRecord(self::METRIC_DOWNLOADS_RECORD_NAME, array_sum($dataTable->getColumn(Metrics::INDEX_PAGE_NB_HITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_UNIQ_DOWNLOADS_RECORD_NAME, array_sum($dataTable->getColumn(Metrics::INDEX_NB_VISITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_DOWNLOADS_RECORD_NAME, array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_UNIQ_DOWNLOADS_RECORD_NAME, array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)));
}
protected function insertOutlinksReports()
@ -476,8 +446,8 @@ class Archiver extends \Piwik\Plugin\Archiver
$dataTable = $this->getDataTable(Action::TYPE_OUTLINK);
$this->insertTable($dataTable, self::OUTLINKS_RECORD_NAME);
$this->getProcessor()->insertNumericRecord(self::METRIC_OUTLINKS_RECORD_NAME, array_sum($dataTable->getColumn(Metrics::INDEX_PAGE_NB_HITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_UNIQ_OUTLINKS_RECORD_NAME, array_sum($dataTable->getColumn(Metrics::INDEX_NB_VISITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_OUTLINKS_RECORD_NAME, array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_UNIQ_OUTLINKS_RECORD_NAME, array_sum($dataTable->getColumn(PiwikMetrics::INDEX_NB_VISITS)));
}
protected function insertPageTitlesReports()
@ -492,21 +462,21 @@ class Archiver extends \Piwik\Plugin\Archiver
$this->deleteUnusedColumnsFromKeywordsDataTable($dataTable);
$this->insertTable($dataTable, self::SITE_SEARCH_RECORD_NAME);
$this->getProcessor()->insertNumericRecord(self::METRIC_SEARCHES_RECORD_NAME, array_sum($dataTable->getColumn(Metrics::INDEX_NB_VISITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_SEARCHES_RECORD_NAME, array_sum($dataTable->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS)));
$this->getProcessor()->insertNumericRecord(self::METRIC_KEYWORDS_RECORD_NAME, $dataTable->getRowsCount());
}
protected function deleteUnusedColumnsFromKeywordsDataTable(DataTable $dataTable)
{
$columnsToDelete = array(
Metrics::INDEX_NB_UNIQ_VISITORS,
Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS,
Metrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
Metrics::INDEX_PAGE_ENTRY_NB_ACTIONS,
Metrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
Metrics::INDEX_PAGE_ENTRY_NB_VISITS,
Metrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT,
Metrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
PiwikMetrics::INDEX_NB_UNIQ_VISITORS,
PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS,
PiwikMetrics::INDEX_PAGE_ENTRY_NB_UNIQ_VISITORS,
PiwikMetrics::INDEX_PAGE_ENTRY_NB_ACTIONS,
PiwikMetrics::INDEX_PAGE_ENTRY_SUM_VISIT_LENGTH,
PiwikMetrics::INDEX_PAGE_ENTRY_NB_VISITS,
PiwikMetrics::INDEX_PAGE_ENTRY_BOUNCE_COUNT,
PiwikMetrics::INDEX_PAGE_EXIT_NB_UNIQ_VISITORS,
);
$dataTable->deleteColumns($columnsToDelete);
}
@ -522,8 +492,9 @@ class Archiver extends \Piwik\Plugin\Archiver
ArchivingHelper::$maximumRowsInDataTableLevelZero,
ArchivingHelper::$maximumRowsInSubDataTable,
ArchivingHelper::$columnToSortByBeforeTruncation,
self::$columnsAggregationOperation,
self::$columnsToRenameAfterAggregation
Metrics::$columnsAggregationOperation,
Metrics::$columnsToRenameAfterAggregation,
$countRowsRecursive = array()
);
$dataTableToSum = array(
@ -537,7 +508,8 @@ class Archiver extends \Piwik\Plugin\Archiver
ArchivingHelper::$maximumRowsInSubDataTable,
ArchivingHelper::$columnToSortByBeforeTruncation,
$aggregation,
self::$columnsToRenameAfterAggregation
Metrics::$columnsToRenameAfterAggregation,
$countRowsRecursive = array()
);
$this->getProcessor()->aggregateNumericMetrics($this->getMetricNames());

View file

@ -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
@ -10,11 +10,10 @@ namespace Piwik\Plugins\Actions;
use PDOStatement;
use Piwik\Config;
use Piwik\DataTable\Manager;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\DataTable\Row\DataTableSummaryRow;
use Piwik\Metrics;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics as PiwikMetrics;
use Piwik\Piwik;
use Piwik\Tracker\Action;
use Piwik\Tracker\PageUrl;
@ -38,7 +37,7 @@ class ArchivingHelper
* @param array $actionsTablesByType
* @return int
*/
static public function updateActionsTableWithRowQuery($query, $fieldQueried, & $actionsTablesByType)
public static function updateActionsTableWithRowQuery($query, $fieldQueried, & $actionsTablesByType, $metricsConfig)
{
$rowsProcessed = 0;
while ($row = $query->fetch()) {
@ -51,7 +50,11 @@ class ArchivingHelper
}
if ($row['type'] != Action::TYPE_SITE_SEARCH) {
unset($row[Metrics::INDEX_SITE_SEARCH_HAS_NO_RESULT]);
unset($row[PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT]);
}
if (in_array($row['type'], array(Action::TYPE_CONTENT, Action::TYPE_EVENT))) {
continue;
}
// This will appear as <url /> in the API, which is actually very important to keep
@ -107,15 +110,15 @@ class ArchivingHelper
&& !$actionRow->isSummaryRow()
) {
if (($existingUrl = $actionRow->getMetadata('url')) !== false) {
if (!empty($row[Metrics::INDEX_PAGE_NB_HITS])
&& $row[Metrics::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed
if (!empty($row[PiwikMetrics::INDEX_PAGE_NB_HITS])
&& $row[PiwikMetrics::INDEX_PAGE_NB_HITS] > $actionRow->maxVisitsSummed
) {
$actionRow->setMetadata('url', $url);
$actionRow->maxVisitsSummed = $row[Metrics::INDEX_PAGE_NB_HITS];
$actionRow->maxVisitsSummed = $row[PiwikMetrics::INDEX_PAGE_NB_HITS];
}
} else {
$actionRow->setMetadata('url', $url);
$actionRow->maxVisitsSummed = !empty($row[Metrics::INDEX_PAGE_NB_HITS]) ? $row[Metrics::INDEX_PAGE_NB_HITS] : 0;
$actionRow->maxVisitsSummed = !empty($row[PiwikMetrics::INDEX_PAGE_NB_HITS]) ? $row[PiwikMetrics::INDEX_PAGE_NB_HITS] : 0;
}
}
@ -123,17 +126,17 @@ class ArchivingHelper
&& $row['type'] != Action::TYPE_PAGE_TITLE
) {
// only keep performance metrics when they're used (i.e. for URLs and page titles)
if (array_key_exists(Metrics::INDEX_PAGE_SUM_TIME_GENERATION, $row)) {
unset($row[Metrics::INDEX_PAGE_SUM_TIME_GENERATION]);
if (array_key_exists(PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION, $row)) {
unset($row[PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION]);
}
if (array_key_exists(Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) {
unset($row[Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]);
if (array_key_exists(PiwikMetrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION, $row)) {
unset($row[PiwikMetrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION]);
}
if (array_key_exists(Metrics::INDEX_PAGE_MIN_TIME_GENERATION, $row)) {
unset($row[Metrics::INDEX_PAGE_MIN_TIME_GENERATION]);
if (array_key_exists(PiwikMetrics::INDEX_PAGE_MIN_TIME_GENERATION, $row)) {
unset($row[PiwikMetrics::INDEX_PAGE_MIN_TIME_GENERATION]);
}
if (array_key_exists(Metrics::INDEX_PAGE_MAX_TIME_GENERATION, $row)) {
unset($row[Metrics::INDEX_PAGE_MAX_TIME_GENERATION]);
if (array_key_exists(PiwikMetrics::INDEX_PAGE_MAX_TIME_GENERATION, $row)) {
unset($row[PiwikMetrics::INDEX_PAGE_MAX_TIME_GENERATION]);
}
}
@ -147,7 +150,7 @@ class ArchivingHelper
// - this happens when 2 visitors visit the same new page at the same time, and 2 actions get recorded for the same name
// - this could also happen when 2 URLs end up having the same label (eg. 2 subdomains get aggregated to the "/index" page name)
if (($alreadyValue = $actionRow->getColumn($name)) !== false) {
$newValue = self::getColumnValuesMerged($name, $alreadyValue, $value);
$newValue = self::getColumnValuesMerged($name, $alreadyValue, $value, $metricsConfig);
$actionRow->setColumn($name, $newValue);
} else {
$actionRow->addColumn($name, $value);
@ -157,7 +160,7 @@ class ArchivingHelper
// if the exit_action was not recorded properly in the log_link_visit_action
// there would be an error message when getting the nb_hits column
// we must fake the record and add the columns
if ($actionRow->getColumn(Metrics::INDEX_PAGE_NB_HITS) === false) {
if ($actionRow->getColumn(PiwikMetrics::INDEX_PAGE_NB_HITS) === false) {
// to test this code: delete the entries in log_link_action_visit for
// a given exit_idaction_url
foreach (self::getDefaultRow()->getColumns() as $name => $value) {
@ -176,7 +179,7 @@ class ArchivingHelper
{
// Delete all columns that have a value of zero
$dataTable->filter('ColumnDelete', array(
$columnsToRemove = array(Metrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS),
$columnsToRemove = array(PiwikMetrics::INDEX_PAGE_IS_FOLLOWING_SITE_SEARCH_NB_HITS),
$columnsToKeep = array(),
$deleteIfZeroOnly = true
));
@ -194,16 +197,16 @@ class ArchivingHelper
if (($idSubtable = $row->getIdSubDataTable()) !== null
|| $id === DataTable::ID_SUMMARY_ROW
) {
if ($idSubtable !== null) {
$subtable = Manager::getInstance()->getTable($idSubtable);
self::deleteInvalidSummedColumnsFromDataTable($subtable);
$subTable = $row->getSubtable();
if ($subTable) {
self::deleteInvalidSummedColumnsFromDataTable($subTable);
}
if ($row instanceof DataTableSummaryRow) {
$row->recalculate();
}
foreach (Archiver::$columnsToDeleteAfterAggregation as $name) {
foreach (Metrics::$columnsToDeleteAfterAggregation as $name) {
$row->deleteColumn($name);
}
}
@ -263,38 +266,45 @@ class ArchivingHelper
* @param $value
* @return mixed
*/
private static function getColumnValuesMerged($columnName, $alreadyValue, $value)
private static function getColumnValuesMerged($columnName, $alreadyValue, $value, $metricsConfig)
{
if ($columnName == Metrics::INDEX_PAGE_MIN_TIME_GENERATION) {
if (empty($alreadyValue)) {
$newValue = $value;
} else if (empty($value)) {
$newValue = $alreadyValue;
} else {
$newValue = min($alreadyValue, $value);
if (array_key_exists($columnName, $metricsConfig)) {
$config = $metricsConfig[$columnName];
if (!empty($config['aggregation'])) {
if ($config['aggregation'] == 'min') {
if (empty($alreadyValue)) {
$newValue = $value;
} else if (empty($value)) {
$newValue = $alreadyValue;
} else {
$newValue = min($alreadyValue, $value);
}
return $newValue;
}
if ($config['aggregation'] == 'max') {
$newValue = max($alreadyValue, $value);
return $newValue;
}
}
return $newValue;
}
if ($columnName == Metrics::INDEX_PAGE_MAX_TIME_GENERATION) {
$newValue = max($alreadyValue, $value);
return $newValue;
}
$newValue = $alreadyValue + $value;
return $newValue;
}
static public $maximumRowsInDataTableLevelZero;
static public $maximumRowsInSubDataTable;
static public $columnToSortByBeforeTruncation;
public static $maximumRowsInDataTableLevelZero;
public static $maximumRowsInSubDataTable;
public static $columnToSortByBeforeTruncation;
static protected $actionUrlCategoryDelimiter = null;
static protected $actionTitleCategoryDelimiter = null;
static protected $defaultActionName = null;
static protected $defaultActionNameWhenNotDefined = null;
static protected $defaultActionUrlWhenNotDefined = null;
protected static $actionUrlCategoryDelimiter = null;
protected static $actionTitleCategoryDelimiter = null;
protected static $defaultActionName = null;
protected static $defaultActionNameWhenNotDefined = null;
protected static $defaultActionUrlWhenNotDefined = null;
static public function reloadConfig()
public static function reloadConfig()
{
// for BC, we read the old style delimiter first (see #1067)Row
$actionDelimiter = @Config::getInstance()->General['action_category_delimiter'];
@ -306,7 +316,7 @@ class ArchivingHelper
}
self::$defaultActionName = Config::getInstance()->General['action_default_name'];
self::$columnToSortByBeforeTruncation = Metrics::INDEX_NB_VISITS;
self::$columnToSortByBeforeTruncation = PiwikMetrics::INDEX_NB_VISITS;
self::$maximumRowsInDataTableLevelZero = Config::getInstance()->General['datatable_archiving_maximum_rows_actions'];
self::$maximumRowsInSubDataTable = Config::getInstance()->General['datatable_archiving_maximum_rows_subtable_actions'];
@ -320,7 +330,7 @@ class ArchivingHelper
*
* @return Row
*/
static private function getDefaultRow()
private static function getDefaultRow()
{
static $row = false;
if ($row === false) {
@ -329,9 +339,9 @@ class ArchivingHelper
// so we add this fake row information to make sure there is a nb_hits, etc. column for every action
$row = new Row(array(
Row::COLUMNS => array(
Metrics::INDEX_NB_VISITS => 1,
Metrics::INDEX_NB_UNIQ_VISITORS => 1,
Metrics::INDEX_PAGE_NB_HITS => 1,
PiwikMetrics::INDEX_NB_VISITS => 1,
PiwikMetrics::INDEX_NB_UNIQ_VISITORS => 1,
PiwikMetrics::INDEX_PAGE_NB_HITS => 1,
)));
}
return $row;
@ -347,13 +357,13 @@ class ArchivingHelper
* @param array $actionsTablesByType
* @return DataTable
*/
private static function getActionRow($actionName, $actionType, $urlPrefix = null, &$actionsTablesByType)
public static function getActionRow($actionName, $actionType, $urlPrefix = null, &$actionsTablesByType)
{
// we work on the root table of the given TYPE (either ACTION_URL or DOWNLOAD or OUTLINK etc.)
/* @var DataTable $currentTable */
$currentTable =& $actionsTablesByType[$actionType];
if(is_null($currentTable)) {
if (is_null($currentTable)) {
throw new \Exception("Action table for type '$actionType' was not found during Actions archiving.");
}
@ -390,7 +400,7 @@ class ArchivingHelper
* @param $type
* @return string
*/
static public function getUnknownActionName($type)
public static function getUnknownActionName($type)
{
if (empty(self::$defaultActionNameWhenNotDefined)) {
self::$defaultActionNameWhenNotDefined = Piwik::translate('General_NotDefined', Piwik::translate('Actions_ColumnPageName'));
@ -402,7 +412,6 @@ class ArchivingHelper
return self::$defaultActionUrlWhenNotDefined;
}
/**
* Explodes action name into an array of elements.
*
@ -425,7 +434,7 @@ class ArchivingHelper
* @param int $urlPrefix url prefix (only used for TYPE_PAGE_URL)
* @return array of exploded elements from $name
*/
static public function getActionExplodedNames($name, $type, $urlPrefix = null)
public static function getActionExplodedNames($name, $type, $urlPrefix = null)
{
// Site Search does not split Search keywords
if ($type == Action::TYPE_SITE_SEARCH) {
@ -477,7 +486,7 @@ class ArchivingHelper
/**
* Static cache to store Rows during processing
*/
static protected $cacheParsedAction = array();
protected static $cacheParsedAction = array();
public static function clearActionsCache()
{
@ -526,10 +535,10 @@ class ArchivingHelper
*/
private static function getDefaultRowColumns()
{
return array(Metrics::INDEX_NB_VISITS => 0,
Metrics::INDEX_NB_UNIQ_VISITORS => 0,
Metrics::INDEX_PAGE_NB_HITS => 0,
Metrics::INDEX_PAGE_SUM_TIME_SPENT => 0);
return array(PiwikMetrics::INDEX_NB_VISITS => 0,
PiwikMetrics::INDEX_NB_UNIQ_VISITORS => 0,
PiwikMetrics::INDEX_PAGE_NB_HITS => 0,
PiwikMetrics::INDEX_PAGE_SUM_TIME_SPENT => 0);
}
/**
@ -594,7 +603,12 @@ class ArchivingHelper
$urlFragment = $matches[3];
if (in_array($type, array(Action::TYPE_DOWNLOAD, Action::TYPE_OUTLINK))) {
return array(trim($urlHost), '/' . trim($urlPath));
$path = '/' . trim($urlPath);
if (!empty($urlFragment)) {
$path .= '#' . $urlFragment;
}
return array(trim($urlHost), $path);
}
$name = $urlPath;

View file

@ -0,0 +1,70 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugins\Actions\Segment;
use Piwik\Tracker\Action;
use Exception;
/**
* This example dimension only defines a name and does not track any data. It's supposed to be only used in reports.
*
* See {@link http://developer.piwik.org/api-reference/Piwik/Columns\Dimension} for more information.
*/
class ActionType extends ActionDimension
{
private $types = array(
Action::TYPE_PAGE_URL => 'pageviews',
Action::TYPE_CONTENT => 'contents',
Action::TYPE_SITE_SEARCH => 'sitesearches',
Action::TYPE_EVENT => 'events',
Action::TYPE_OUTLINK => 'outlinks',
Action::TYPE_DOWNLOAD => 'downloads'
);
/**
* The name of the dimension which will be visible for instance in the UI of a related report and in the mobile app.
* @return string
*/
public function getName()
{
return Piwik::translate('Actions_ActionType');
}
protected function configureSegments()
{
$types = $this->types;
$segment = new Segment();
$segment->setSegment('actionType');
$segment->setName('Actions_ActionType');
$segment->setSqlSegment('log_action.type');
$segment->setType(Segment::TYPE_DIMENSION);
$segment->setAcceptedValues(sprintf('A type of action, such as: %s', implode(', ', $types)));
$segment->setSqlFilter(function ($type) use ($types) {
if (array_key_exists($type, $types)) {
return $type;
}
$index = array_search(strtolower(trim(urldecode($type))), $types);
if ($index === false) {
throw new Exception("actionType must be one of: " . implode(', ', $types));
}
return $index;
});
$segment->setSuggestedValuesCallback(function ($idSite, $maxSuggestionsToReturn) use ($types) {
return array_slice(array_values($types), 0, $maxSuggestionsToReturn);
});
$this->addSegment($segment);
}
}

View file

@ -0,0 +1,32 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugins\Actions\Segment;
class ActionUrl extends ActionDimension
{
public function getName()
{
return Piwik::translate('Actions_ColumnActionURL');
}
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('actionUrl');
$segment->setName('Actions_ColumnActionURL');
$segment->setUnionOfSegments(array('pageUrl', 'downloadUrl', 'outlinkUrl'));
$this->addSegment($segment);
}
}

View file

@ -0,0 +1,31 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugins\Actions\Segment;
class ClickedUrl extends ActionDimension
{
public function getName()
{
return Piwik::translate('Actions_ColumnClickedURL');
}
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('outlinkUrl');
$segment->setName('Actions_ColumnClickedURL');
$segment->setSqlSegment('log_link_visit_action.idaction_url');
$this->addSegment($segment);
}
}

View 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\Actions\Columns;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
class DestinationPage extends Dimension
{
public function getName()
{
return Piwik::translate('General_ColumnDestinationPage');
}
}

View file

@ -0,0 +1,31 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugins\Actions\Segment;
class DownloadUrl extends ActionDimension
{
public function getName()
{
return Piwik::translate('Actions_ColumnDownloadURL');
}
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('downloadUrl');
$segment->setName('Actions_ColumnDownloadURL');
$segment->setSqlSegment('log_link_visit_action.idaction_url');
$this->addSegment($segment);
}
}

View file

@ -0,0 +1,52 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugins\Actions\Segment;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
class EntryPageTitle extends VisitDimension
{
protected $columnName = 'visit_entry_idaction_name';
protected $columnType = 'INTEGER(11) UNSIGNED NOT NULL';
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('entryPageTitle');
$segment->setName('Actions_ColumnEntryPageTitle');
$this->addSegment($segment);
}
/**
* @param Request $request
* @param Visitor $visitor
* @param Action|null $action
* @return mixed
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$idActionName = false;
if (!empty($action)) {
$idActionName = $action->getIdActionNameForEntryAndExitIds();
}
return (int) $idActionName;
}
public function getName()
{
return Piwik::translate('Actions_ColumnEntryPageTitle');
}
}

View file

@ -0,0 +1,53 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugins\Actions\Segment;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
class EntryPageUrl extends VisitDimension
{
protected $columnName = 'visit_entry_idaction_url';
protected $columnType = 'INTEGER(11) UNSIGNED NOT NULL';
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('entryPageUrl');
$segment->setName('Actions_ColumnEntryPageURL');
$this->addSegment($segment);
}
/**
* @param Request $request
* @param Visitor $visitor
* @param Action|null $action
* @return mixed
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$idActionUrl = false;
if (!empty($action)) {
$idActionUrl = $action->getIdActionUrlForEntryAndExitIds();
}
return (int) $idActionUrl;
}
public function getName()
{
return Piwik::translate('Actions_ColumnEntryPageURL');
}
}

View file

@ -0,0 +1,67 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugins\Actions\Segment;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
class ExitPageTitle extends VisitDimension
{
protected $columnName = 'visit_exit_idaction_name';
protected $columnType = 'INTEGER(11) UNSIGNED NOT NULL';
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('exitPageTitle');
$segment->setName('Actions_ColumnExitPageTitle');
$this->addSegment($segment);
}
/**
* @param Request $request
* @param Visitor $visitor
* @param Action|null $action
* @return int|bool
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$idActionName = false;
if (!empty($action)) {
$idActionName = $action->getIdActionNameForEntryAndExitIds();
}
return (int) $idActionName;
}
/**
* @param Request $request
* @param Visitor $visitor
* @param Action|null $action
* @return int|bool
*/
public function onExistingVisit(Request $request, Visitor $visitor, $action)
{
if (empty($action)) {
return false;
}
return $action->getIdActionNameForEntryAndExitIds();
}
public function getName()
{
return Piwik::translate('Actions_ColumnExitPageTitle');
}
}

View file

@ -0,0 +1,73 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugins\Actions\Segment;
use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\Visitor;
class ExitPageUrl extends VisitDimension
{
protected $columnName = 'visit_exit_idaction_url';
protected $columnType = 'INTEGER(11) UNSIGNED NULL DEFAULT 0';
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('exitPageUrl');
$segment->setName('Actions_ColumnExitPageURL');
$this->addSegment($segment);
}
/**
* @param Request $request
* @param Visitor $visitor
* @param Action|null $action
* @return int|bool
*/
public function onNewVisit(Request $request, Visitor $visitor, $action)
{
$idActionUrl = false;
if (!empty($action)) {
$idActionUrl = $action->getIdActionUrlForEntryAndExitIds();
}
return (int) $idActionUrl;
}
/**
* @param Request $request
* @param Visitor $visitor
* @param Action|null $action
* @return int
*/
public function onExistingVisit(Request $request, Visitor $visitor, $action)
{
if (empty($action)) {
return false;
}
$id = $action->getIdActionUrlForEntryAndExitIds();
if (!empty($id)) {
$id = (int) $id;
}
return $id;
}
public function getName()
{
return Piwik::translate('Actions_ColumnExitPageURL');
}
}

View 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\Actions\Columns;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
class Keyword extends Dimension
{
public function getName()
{
return Piwik::translate('General_ColumnKeyword');
}
}

View 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\Actions\Columns;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
class KeywordwithNoSearchResult extends Dimension
{
public function getName()
{
return Piwik::translate('Actions_ColumnNoResultKeyword');
}
}

View file

@ -0,0 +1,108 @@
<?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\Actions\Columns\Metrics;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Plugin\Report;
/**
* The average amount of time it takes to generate a page. Calculated as
*
* sum_time_generation / nb_hits_with_time_generation
*
* The above metrics are calculated during archiving. This metric is calculated before
* serving a report.
*/
class AveragePageGenerationTime extends ProcessedMetric
{
public function getName()
{
return 'avg_time_generation';
}
public function getTranslatedName()
{
return Piwik::translate('General_ColumnAverageGenerationTime');
}
public function getDependentMetrics()
{
return array('sum_time_generation', 'nb_hits_with_time_generation');
}
public function getTemporaryMetrics()
{
return array('sum_time_generation');
}
public function compute(Row $row)
{
$sumGenerationTime = $this->getMetric($row, 'sum_time_generation');
$hitsWithTimeGeneration = $this->getMetric($row, 'nb_hits_with_time_generation');
return Piwik::getQuotientSafe($sumGenerationTime, $hitsWithTimeGeneration, $precision = 3);
}
public function format($value, Formatter $formatter)
{
if ($formatter instanceof Formatter\Html
&& !$value
) {
return '-';
} else {
return $formatter->getPrettyTimeFromSeconds($value, $displayAsSentence = true);
}
}
public function beforeCompute($report, DataTable $table)
{
$hasTimeGeneration = array_sum($this->getMetricValues($table, 'sum_time_generation')) > 0;
if (!$hasTimeGeneration
&& $table->getRowsCount() != 0
&& !$this->hasAverageTimeGeneration($table)
) {
// No generation time: remove it from the API output and add it to empty_columns metadata, so that
// the columns can also be removed from the view
$table->filter('ColumnDelete', array(array(
Metrics::INDEX_PAGE_SUM_TIME_GENERATION,
Metrics::INDEX_PAGE_NB_HITS_WITH_TIME_GENERATION,
Metrics::INDEX_PAGE_MIN_TIME_GENERATION,
Metrics::INDEX_PAGE_MAX_TIME_GENERATION,
'sum_time_generation',
'nb_hits_with_time_generation',
'min_time_generation',
'max_time_generation'
)));
if ($table instanceof DataTable) {
$emptyColumns = $table->getMetadata(DataTable::EMPTY_COLUMNS_METADATA_NAME);
if (!is_array($emptyColumns)) {
$emptyColumns = array();
}
$emptyColumns[] = 'sum_time_generation';
$emptyColumns[] = 'avg_time_generation';
$emptyColumns[] = 'min_time_generation';
$emptyColumns[] = 'max_time_generation';
$table->setMetadata(DataTable::EMPTY_COLUMNS_METADATA_NAME, $emptyColumns);
}
}
return $hasTimeGeneration;
}
private function hasAverageTimeGeneration(DataTable $table)
{
return $table->getFirstRow()->getColumn('avg_time_generation') !== false;
}
}

View file

@ -0,0 +1,51 @@
<?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\Actions\Columns\Metrics;
use Piwik\DataTable\Row;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
/**
* The average amount of time spent on a page. Calculated as:
*
* sum_time_spent / nb_visits
*
* sum_time_spent and nb_visits are calculated by Archiver classes.
*/
class AverageTimeOnPage extends ProcessedMetric
{
public function getName()
{
return 'avg_time_on_page';
}
public function getTranslatedName()
{
return Piwik::translate('General_ColumnAverageTimeOnPage');
}
public function compute(Row $row)
{
$sumTimeSpent = $this->getMetric($row, 'sum_time_spent');
$visits = $this->getMetric($row, 'nb_hits');
return Piwik::getQuotientSafe($sumTimeSpent, $visits, $precision = 0);
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyTimeFromSeconds($value, $timeAsSentence = false);
}
public function getDependentMetrics()
{
return array('sum_time_spent', 'nb_hits');
}
}

View file

@ -0,0 +1,51 @@
<?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\Actions\Columns\Metrics;
use Piwik\DataTable\Row;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
/**
* The bounce rate for individual pages. Calculated as:
*
* entry_bounce_count (single page visits on this page) / entry_nb_visits (all visits that started on this page)
*
* entry_bounce_count & entry_nb_visits are calculated by the Actions archiver.
*/
class BounceRate extends ProcessedMetric
{
public function getName()
{
return 'bounce_rate';
}
public function getTranslatedName()
{
return Piwik::translate('General_ColumnBounceRate');
}
public function compute(Row $row)
{
$entryBounceCount = $this->getMetric($row, 'entry_bounce_count');
$entryVisits = $this->getMetric($row, 'entry_nb_visits');
return Piwik::getQuotientSafe($entryBounceCount, $entryVisits, $precision = 2);
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyPercentFromQuotient($value);
}
public function getDependentMetrics()
{
return array('entry_bounce_count', 'entry_nb_visits');
}
}

View file

@ -0,0 +1,51 @@
<?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\Actions\Columns\Metrics;
use Piwik\DataTable\Row;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
/**
* Percent of visits that finished on this page. Calculated as:
*
* exit_nb_visits / nb_visits
*
* exit_nb_visits & nb_visits are calculated by the Actions archiver.
*/
class ExitRate extends ProcessedMetric
{
public function getName()
{
return 'exit_rate';
}
public function getTranslatedName()
{
return Piwik::translate('General_ColumnExitRate');
}
public function compute(Row $row)
{
$exitVisits = $this->getMetric($row, 'exit_nb_visits');
$visits = $this->getMetric($row, 'nb_visits');
return Piwik::getQuotientSafe($exitVisits, $visits, $precision = 2);
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyPercentFromQuotient($value);
}
public function getDependentMetrics()
{
return array('exit_nb_visits', 'nb_visits');
}
}

View file

@ -0,0 +1,33 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugins\Actions\Segment;
class PageTitle extends ActionDimension
{
protected $columnName = 'idaction_name';
protected $columnType = 'INTEGER(10) UNSIGNED';
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('pageTitle');
$segment->setName('Actions_ColumnPageName');
$this->addSegment($segment);
}
public function getName()
{
return Piwik::translate('Actions_ColumnPageName');
}
}

View file

@ -0,0 +1,33 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugins\Actions\Segment;
class PageUrl extends ActionDimension
{
protected $columnName = 'idaction_url';
protected $columnType = 'INTEGER(10) UNSIGNED DEFAULT NULL';
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('pageUrl');
$segment->setName('Actions_ColumnPageURL');
$segment->setAcceptedValues('All these segments must be URL encoded, for example: ' . urlencode('http://example.com/path/page?query'));
$this->addSegment($segment);
}
public function getName()
{
return Piwik::translate('Actions_ColumnPageURL');
}
}

View 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\Actions\Columns;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
class SearchCategory extends Dimension
{
public function getName()
{
return Piwik::translate('Actions_ColumnSearchCategory');
}
}

View 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\Actions\Columns;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
class SearchDestinationPage extends Dimension
{
public function getName()
{
return Piwik::translate('General_ColumnDestinationPage');
}
}

View file

@ -0,0 +1,30 @@
<?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\Actions\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ActionDimension;
use Piwik\Plugins\Actions\Segment;
class SearchKeyword extends ActionDimension
{
protected function configureSegments()
{
$segment = new Segment();
$segment->setSegment('siteSearchKeyword');
$segment->setName('Actions_SiteSearchKeyword');
$segment->setSqlSegment('log_link_visit_action.idaction_name');
$this->addSegment($segment);
}
public function getName()
{
return Piwik::translate('General_ColumnKeyword');
}
}

Some files were not shown because too many files have changed in this diff Show more