update Piwik to version 2.16 (fixes #91)

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

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,15 +9,24 @@
namespace Piwik\Plugins\Goals;
use Exception;
use Piwik\API\Request;
use Piwik\Archive;
use Piwik\CacheId;
use Piwik\Cache as PiwikCache;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Db;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugins\API\DataTable\MergeDataTables;
use Piwik\Plugins\CoreHome\Columns\Metrics\ConversionRate;
use Piwik\Plugins\Goals\Columns\Metrics\AverageOrderRevenue;
use Piwik\Segment\SegmentExpression;
use Piwik\Site;
use Piwik\Tracker\Cache;
use Piwik\Tracker\GoalManager;
use Piwik\Plugins\VisitFrequency\API as VisitFrequencyAPI;
/**
* Goals API lets you Manage existing goals, via "updateGoal" and "deleteGoal", create new Goals via "addGoal",
@ -35,7 +44,7 @@ use Piwik\Tracker\GoalManager;
* If you wish to request specific metrics about Ecommerce goals, you can set the parameter &idGoal=ecommerceAbandonedCart to get metrics about abandoned carts (including Lost revenue, and number of items left in the cart)
* or &idGoal=ecommerceOrder to get metrics about Ecommerce orders (number of orders, visits with an order, subtotal, tax, shipping, discount, revenue, items ordered)
*
* See also the documentation about <a href='http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>Tracking Goals</a> in Piwik.
* See also the documentation about <a href='http://piwik.org/docs/tracking-goals-web-analytics/' rel='noreferrer' target='_blank'>Tracking Goals</a> in Piwik.
*
* @method static \Piwik\Plugins\Goals\API getInstance()
*/
@ -51,27 +60,33 @@ class API extends \Piwik\Plugin\API
*/
public function getGoals($idSite)
{
//TODO calls to this function could be cached as static
// would help UI at least, since some UI requests would call this 2-3 times..
$idSite = Site::getIdSitesFromIdSitesString($idSite);
if (empty($idSite)) {
return array();
}
Piwik::checkUserHasViewAccess($idSite);
$goals = Db::fetchAll("SELECT *
FROM " . Common::prefixTable('goal') . "
WHERE idsite IN (" . implode(", ", $idSite) . ")
AND deleted = 0");
$cleanedGoals = array();
foreach ($goals as &$goal) {
if ($goal['match_attribute'] == 'manually') {
unset($goal['pattern']);
unset($goal['pattern_type']);
unset($goal['case_sensitive']);
$cacheId = self::getCacheId($idSite);
$cache = $this->getGoalsInfoStaticCache();
if (!$cache->contains($cacheId)) {
$idSite = Site::getIdSitesFromIdSitesString($idSite);
if (empty($idSite)) {
return array();
}
$cleanedGoals[$goal['idgoal']] = $goal;
Piwik::checkUserHasViewAccess($idSite);
$goals = $this->getModel()->getActiveGoals($idSite);
$cleanedGoals = array();
foreach ($goals as &$goal) {
if ($goal['match_attribute'] == 'manually') {
unset($goal['pattern']);
unset($goal['pattern_type']);
unset($goal['case_sensitive']);
}
$cleanedGoals[$goal['idgoal']] = $goal;
}
$cache->save($cacheId, $cleanedGoals);
}
return $cleanedGoals;
return $cache->fetch($cacheId);
}
/**
@ -79,7 +94,7 @@ class API extends \Piwik\Plugin\API
*
* @param int $idSite
* @param string $name
* @param string $matchAttribute 'url', 'title', 'file', 'external_website' or 'manually'
* @param string $matchAttribute 'url', 'title', 'file', 'external_website', 'manually', 'event_action', 'event_category' or 'event_name'
* @param string $pattern eg. purchase-confirmation.htm
* @param string $patternType 'regex', 'contains', 'exact'
* @param bool $caseSensitive
@ -91,35 +106,37 @@ class API extends \Piwik\Plugin\API
public function addGoal($idSite, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
{
Piwik::checkUserHasAdminAccess($idSite);
$this->checkPatternIsValid($patternType, $pattern);
$name = $this->checkName($name);
$this->checkPatternIsValid($patternType, $pattern, $matchAttribute);
$name = $this->checkName($name);
$pattern = $this->checkPattern($pattern);
// save in db
$db = Db::get();
$idGoal = $db->fetchOne("SELECT max(idgoal) + 1
FROM " . Common::prefixTable('goal') . "
WHERE idsite = ?", $idSite);
if ($idGoal == false) {
$idGoal = 1;
}
$db->insert(Common::prefixTable('goal'),
array(
'idsite' => $idSite,
'idgoal' => $idGoal,
'name' => $name,
'match_attribute' => $matchAttribute,
'pattern' => $pattern,
'pattern_type' => $patternType,
'case_sensitive' => (int)$caseSensitive,
'allow_multiple' => (int)$allowMultipleConversionsPerVisit,
'revenue' => (float)$revenue,
'deleted' => 0,
));
$revenue = Common::forceDotAsSeparatorForDecimalPoint((float)$revenue);
$goal = array(
'name' => $name,
'match_attribute' => $matchAttribute,
'pattern' => $pattern,
'pattern_type' => $patternType,
'case_sensitive' => (int)$caseSensitive,
'allow_multiple' => (int)$allowMultipleConversionsPerVisit,
'revenue' => $revenue,
'deleted' => 0,
);
$idGoal = $this->getModel()->createGoalForSite($idSite, $goal);
$this->getGoalsInfoStaticCache()->delete(self::getCacheId($idSite));
Cache::regenerateCacheWebsiteAttributes($idSite);
return $idGoal;
}
private function getModel()
{
return new Model();
}
/**
* Updates a Goal description.
* Will not update or re-process the conversions already recorded
@ -139,28 +156,33 @@ class API extends \Piwik\Plugin\API
public function updateGoal($idSite, $idGoal, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
{
Piwik::checkUserHasAdminAccess($idSite);
$name = $this->checkName($name);
$name = $this->checkName($name);
$pattern = $this->checkPattern($pattern);
$this->checkPatternIsValid($patternType, $pattern);
Db::get()->update(Common::prefixTable('goal'),
array(
'name' => $name,
'match_attribute' => $matchAttribute,
'pattern' => $pattern,
'pattern_type' => $patternType,
'case_sensitive' => (int)$caseSensitive,
'allow_multiple' => (int)$allowMultipleConversionsPerVisit,
'revenue' => (float)$revenue,
),
"idsite = '$idSite' AND idgoal = '$idGoal'"
);
$this->checkPatternIsValid($patternType, $pattern, $matchAttribute);
$revenue = Common::forceDotAsSeparatorForDecimalPoint((float)$revenue);
$this->getModel()->updateGoal($idSite, $idGoal, array(
'name' => $name,
'match_attribute' => $matchAttribute,
'pattern' => $pattern,
'pattern_type' => $patternType,
'case_sensitive' => (int) $caseSensitive,
'allow_multiple' => (int) $allowMultipleConversionsPerVisit,
'revenue' => $revenue,
));
$this->getGoalsInfoStaticCache()->delete(self::getCacheId($idSite));
Cache::regenerateCacheWebsiteAttributes($idSite);
}
private function checkPatternIsValid($patternType, $pattern)
private function checkPatternIsValid($patternType, $pattern, $matchAttribute)
{
if ($patternType == 'exact'
&& substr($pattern, 0, 4) != 'http'
&& substr($matchAttribute, 0, 6) != 'event_'
) {
throw new Exception(Piwik::translate('Goals_ExceptionInvalidMatchingString', array("http:// or https://", "http://www.yourwebsite.com/newsletter/subscribed.html")));
}
@ -187,12 +209,12 @@ class API extends \Piwik\Plugin\API
public function deleteGoal($idSite, $idGoal)
{
Piwik::checkUserHasAdminAccess($idSite);
Db::query("UPDATE " . Common::prefixTable('goal') . "
SET deleted = 1
WHERE idsite = ?
AND idgoal = ?",
array($idSite, $idGoal));
Db::deleteAllRows(Common::prefixTable("log_conversion"), "WHERE idgoal = ? AND idsite = ?", "idvisit", 100000, array($idGoal, $idSite));
$this->getModel()->deleteGoal($idSite, $idGoal);
$this->getModel()->deleteGoalConversions($idSite, $idGoal);
$this->getGoalsInfoStaticCache()->delete(self::getCacheId($idSite));
Cache::regenerateCacheWebsiteAttributes($idSite);
}
@ -203,41 +225,31 @@ class API extends \Piwik\Plugin\API
protected function getItems($recordName, $idSite, $period, $date, $abandonedCarts, $segment)
{
Piwik::checkUserHasViewAccess($idSite);
$recordNameFinal = $recordName;
if ($abandonedCarts) {
$recordNameFinal = Archiver::getItemRecordNameAbandonedCart($recordName);
}
$archive = Archive::build($idSite, $period, $date, $segment);
$dataTable = $archive->getDataTable($recordNameFinal);
$dataTable->filter('Sort', array(Metrics::INDEX_ECOMMERCE_ITEM_REVENUE));
$archive = Archive::build($idSite, $period, $date, $segment);
$dataTable = $archive->getDataTable($recordNameFinal);
$this->enrichItemsTableWithViewMetrics($dataTable, $recordName, $idSite, $period, $date, $segment);
// First rename the avg_price_viewed column
$renameColumn = array(self::AVG_PRICE_VIEWED => 'avg_price');
$dataTable->queueFilter('ReplaceColumnNames', array($renameColumn));
$dataTable->filter('ReplaceColumnNames', array($renameColumn));
$dataTable->queueFilter('ReplaceColumnNames');
$dataTable->queueFilter('ReplaceSummaryRowLabel');
$ordersColumn = 'orders';
if ($abandonedCarts) {
$ordersColumn = 'abandoned_carts';
$dataTable->renameColumn(Metrics::INDEX_ECOMMERCE_ORDERS, $ordersColumn);
}
// Average price = sum product revenue / quantity
$dataTable->queueFilter('ColumnCallbackAddColumnQuotient', array('avg_price', 'price', $ordersColumn, GoalManager::REVENUE_PRECISION));
// Average quantity = sum product quantity / abandoned carts
$dataTable->queueFilter('ColumnCallbackAddColumnQuotient',
array('avg_quantity', 'quantity', $ordersColumn, $precision = 1));
$dataTable->queueFilter('ColumnDelete', array('price'));
// Product conversion rate = orders / visits
$dataTable->queueFilter('ColumnCallbackAddColumnPercentage', array('conversion_rate', $ordersColumn, 'nb_visits', GoalManager::REVENUE_PRECISION));
return $dataTable;
}
@ -249,6 +261,7 @@ class API extends \Piwik\Plugin\API
}
return;
}
$rowNotDefined = $dataTable->getRowFromLabel(\Piwik\Plugins\CustomVariables\Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED);
if ($rowNotDefined) {
$rowNotDefined->setColumn('label', $notDefinedStringPretty);
@ -318,7 +331,7 @@ class API extends \Piwik\Plugin\API
}
/**
* Returns Goals data
* Returns Goals data.
*
* @param int $idSite
* @param string $period
@ -329,62 +342,134 @@ class API extends \Piwik\Plugin\API
* @return DataTable
*/
public function get($idSite, $period, $date, $segment = false, $idGoal = false, $columns = array())
{
Piwik::checkUserHasViewAccess($idSite);
/** @var DataTable|DataTable\Map $table */
$table = null;
$segments = array(
'' => false,
'_new_visit' => 'visitorType%3D%3Dnew', // visitorType==new
'_returning_visit' => VisitFrequencyAPI::RETURNING_VISITOR_SEGMENT
);
foreach ($segments as $appendToMetricName => $predefinedSegment) {
$segmentToUse = $this->appendSegment($predefinedSegment, $segment);
/** @var DataTable|DataTable\Map $tableSegmented */
$tableSegmented = Request::processRequest('Goals.getMetrics', array(
'segment' => $segmentToUse,
'idSite' => $idSite,
'period' => $period,
'date' => $date,
'idGoal' => $idGoal,
'columns' => $columns,
'format_metrics' => 'bc'
));
$tableSegmented->filter('Piwik\Plugins\Goals\DataTable\Filter\AppendNameToColumnNames',
array($appendToMetricName));
if (!isset($table)) {
$table = $tableSegmented;
} else {
$merger = new MergeDataTables();
$merger->mergeDataTables($table, $tableSegmented);
}
}
return $table;
}
/**
* Similar to {@link get()} but does not return any metrics for new and returning visitors. It won't apply
* any segment by default. This method is deprecated from the API as it is only there to make the implementation of
* the actual {@link get()} method easy.
*
* @deprecated
* @internal
*/
public function getMetrics($idSite, $period, $date, $segment = false, $idGoal = false, $columns = array())
{
Piwik::checkUserHasViewAccess($idSite);
$archive = Archive::build($idSite, $period, $date, $segment);
$columns = Piwik::getArrayFromApiParameter($columns);
// Mapping string idGoal to internal ID
$idGoal = self::convertSpecialGoalIds($idGoal);
$isEcommerceGoal = $idGoal === GoalManager::IDGOAL_ORDER || $idGoal === GoalManager::IDGOAL_CART;
if (empty($columns)) {
$columns = Goals::getGoalColumns($idGoal);
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$columns[] = 'avg_order_revenue';
}
}
if (in_array('avg_order_revenue', $columns)
&& $idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER
) {
$columns[] = 'nb_conversions';
$columns[] = 'revenue';
$columns = array_values(array_unique($columns));
}
$columnsToSelect = array();
foreach ($columns as &$columnName) {
$columnsToSelect[] = Archiver::getRecordName($columnName, $idGoal);
}
$dataTable = $archive->getDataTableFromNumeric($columnsToSelect);
$allMetrics = Goals::getGoalColumns($idGoal);
$columnsToShow = Piwik::getArrayFromApiParameter($columns);
$requestedColumns = $columnsToShow;
// Rewrite column names as we expect them
foreach ($columnsToSelect as $id => $oldName) {
$dataTable->renameColumn($oldName, $columns[$id]);
$shouldAddAverageOrderRevenue = (in_array('avg_order_revenue', $requestedColumns) || empty($requestedColumns)) && $isEcommerceGoal;
if ($shouldAddAverageOrderRevenue && !empty($requestedColumns)) {
$avgOrder = new AverageOrderRevenue();
$metricsToAdd = $avgOrder->getDependentMetrics();
$requestedColumns = array_unique(array_merge($requestedColumns, $metricsToAdd));
}
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
if ($dataTable instanceof DataTable\Map) {
foreach ($dataTable->getDataTables() as $row) {
$this->enrichTable($row);
$report = Report::factory('Goals', 'getMetrics');
$columnsToGet = $report->getMetricsRequiredForReport($allMetrics, $requestedColumns);
$inDbMetricNames = array_map(function ($name) use ($idGoal) {
return $name == 'nb_visits' ? $name : Archiver::getRecordName($name, $idGoal);
}, $columnsToGet);
$dataTable = $archive->getDataTableFromNumeric($inDbMetricNames);
if (count($columnsToGet) > 0) {
$newNameMapping = array_combine($inDbMetricNames, $columnsToGet);
} else {
$newNameMapping = array();
}
$dataTable->filter('ReplaceColumnNames', array($newNameMapping));
// TODO: this should be in Goals/Get.php but it depends on idGoal parameter which isn't always in _GET (ie,
// it's not in ProcessedReport.php). more refactoring must be done to report class before this can be
// corrected.
if ($shouldAddAverageOrderRevenue) {
$dataTable->filter(function (DataTable $table) {
$extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
if (empty($extraProcessedMetrics)) {
$extraProcessedMetrics = array();
}
} else {
$this->enrichTable($dataTable);
$extraProcessedMetrics[] = new AverageOrderRevenue();
$table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics);
});
}
// remove temporary metrics that were not explicitly requested
if (empty($columnsToShow)) {
$columnsToShow = $allMetrics;
$columnsToShow[] = 'conversion_rate';
if ($isEcommerceGoal) {
$columnsToShow[] = 'avg_order_revenue';
}
}
$dataTable->queueFilter('ColumnDelete', array($columnsToRemove = array(), $columnsToShow));
return $dataTable;
}
protected function enrichTable($table)
protected function appendSegment($segment, $segmentToAppend)
{
$row = $table->getFirstRow();
if (!$row) {
return;
if (empty($segment)) {
return $segmentToAppend;
}
// AVG order per visit
if (false !== $table->getColumn('avg_order_revenue')) {
$conversions = $row->getColumn('nb_conversions');
if ($conversions) {
$row->setColumn('avg_order_revenue', round($row->getColumn('revenue') / $conversions, 2));
}
if (empty($segmentToAppend)) {
return $segment;
}
$segment .= urlencode(SegmentExpression::AND_DELIMITER);
$segment .= $segmentToAppend;
return $segment;
}
protected function getNumeric($idSite, $period, $date, $segment, $toFetch)
@ -416,7 +501,9 @@ class API extends \Piwik\Plugin\API
*/
public function getConversionRate($idSite, $period, $date, $segment = false, $idGoal = false)
{
return $this->getNumeric($idSite, $period, $date, $segment, Archiver::getRecordName('conversion_rate', $idGoal));
$table = $this->get($idSite, $period, $date, $segment, $idGoal, 'conversion_rate');
$table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, array(new ConversionRate()));
return $table;
}
/**
@ -474,9 +561,9 @@ class API extends \Piwik\Plugin\API
$dataTable = $this->getGoalSpecificDataTable(
Archiver::DAYS_UNTIL_CONV_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
$dataTable->queueFilter('Sort', array('label', 'asc', true));
$dataTable->queueFilter('Sort', array('label', 'asc', true, false));
$dataTable->queueFilter(
'BeautifyRangeLabels', array(Piwik::translate('General_OneDay'), Piwik::translate('General_NDays')));
'BeautifyRangeLabels', array(Piwik::translate('Intl_OneDay'), Piwik::translate('Intl_NDays')));
return $dataTable;
}
@ -498,7 +585,7 @@ class API extends \Piwik\Plugin\API
$dataTable = $this->getGoalSpecificDataTable(
Archiver::VISITS_UNTIL_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
$dataTable->queueFilter('Sort', array('label', 'asc', true));
$dataTable->queueFilter('Sort', array('label', 'asc', true, false));
$dataTable->queueFilter(
'BeautifyRangeLabels', array(Piwik::translate('General_OneVisit'), Piwik::translate('General_NVisits')));
@ -564,4 +651,15 @@ class API extends \Piwik\Plugin\API
$this->renameNotDefinedRow($dataTable, $notDefinedStringPretty);
}
}
private function getCacheId($idSite)
{
return CacheId::pluginAware('Goals.getGoals.' . $idSite);
}
private function getGoalsInfoStaticCache()
{
return PiwikCache::getTransientCache();
}
}

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
@ -13,8 +13,6 @@ use Piwik\DataAccess\LogAggregator;
use Piwik\DataArray;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\PluginsArchiver;
use Piwik\PluginsManager;
use Piwik\Tracker\GoalManager;
class Archiver extends \Piwik\Plugin\Archiver
@ -156,7 +154,6 @@ class Archiver extends \Piwik\Plugin\Archiver
// Stats for all goals
$nbConvertedVisits = $this->getProcessor()->getNumberOfVisitsConverted();
$metrics = array(
self::getRecordName('conversion_rate') => $this->getConversionRate($nbConvertedVisits),
self::getRecordName('nb_conversions') => $totalConversions,
self::getRecordName('nb_visits_converted') => $nbConvertedVisits,
self::getRecordName('revenue') => $totalRevenue,
@ -174,11 +171,6 @@ class Archiver extends \Piwik\Plugin\Archiver
$recordName = self::getRecordName($metricName, $idGoal);
$numericRecords[$recordName] = $value;
}
if (!empty($array[Metrics::INDEX_GOAL_NB_VISITS_CONVERTED])) {
$conversion_rate = $this->getConversionRate($array[Metrics::INDEX_GOAL_NB_VISITS_CONVERTED]);
$recordName = self::getRecordName('conversion_rate', $idGoal);
$numericRecords[$recordName] = $conversion_rate;
}
}
return $numericRecords;
}
@ -188,7 +180,7 @@ class Archiver extends \Piwik\Plugin\Archiver
* @param int|bool $idGoal idGoal to return the metrics for, or false to return overall
* @return string Archive record name
*/
static public function getRecordName($recordName, $idGoal = false)
public static function getRecordName($recordName, $idGoal = false)
{
$idGoalStr = '';
if ($idGoal !== false) {
@ -197,12 +189,6 @@ class Archiver extends \Piwik\Plugin\Archiver
return 'Goal_' . $idGoalStr . $recordName;
}
protected function getConversionRate($count)
{
$visits = $this->getProcessor()->getNumberOfVisits();
return round(100 * $count / $visits, GoalManager::REVENUE_PRECISION);
}
protected function insertReports($recordName, $visitsToConversions)
{
foreach ($visitsToConversions as $idGoal => $table) {
@ -358,7 +344,7 @@ class Archiver extends \Piwik\Plugin\Archiver
return array(GoalManager::IDGOAL_CART, GoalManager::IDGOAL_ORDER);
}
static public function getItemRecordNameAbandonedCart($recordName)
public static function getItemRecordNameAbandonedCart($recordName)
{
return $recordName . '_Cart';
}
@ -375,7 +361,15 @@ class Archiver extends \Piwik\Plugin\Archiver
foreach ($this->dimensionRecord as $recordName) {
$dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
}
$this->getProcessor()->aggregateDataTableRecords($dataTableToSum);
$columnsAggregationOperation = null;
$this->getProcessor()->aggregateDataTableRecords($dataTableToSum,
$maximumRowsInDataTableLevelZero = null,
$maximumRowsInSubDataTable = null,
$columnToSortByBeforeTruncation = null,
$columnsAggregationOperation,
$columnsToRenameAfterAggregation = null,
$countRowsRecursive = array());
/*
* Archive General Goal metrics
@ -391,28 +385,37 @@ class Archiver extends \Piwik\Plugin\Archiver
$fieldsToSum = array();
foreach ($goalIdsToSum as $goalId) {
$metricsToSum = Goals::getGoalColumns($goalId);
unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
foreach ($metricsToSum as $metricName) {
$fieldsToSum[] = self::getRecordName($metricName, $goalId);
}
}
$records = $this->getProcessor()->aggregateNumericMetrics($fieldsToSum);
$this->getProcessor()->aggregateNumericMetrics($fieldsToSum);
$columnsAggregationOperation = null;
// also recording conversion_rate for each goal
foreach ($goalIdsToSum as $goalId) {
$nb_conversions = $records[self::getRecordName('nb_visits_converted', $goalId)];
$conversion_rate = $this->getConversionRate($nb_conversions);
$this->getProcessor()->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
// sum up the visits to conversion data table & the days to conversion data table
$this->getProcessor()->aggregateDataTableRecords(array(
self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
$this->getProcessor()->aggregateDataTableRecords(
array(self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)),
$maximumRowsInDataTableLevelZero = null,
$maximumRowsInSubDataTable = null,
$columnToSortByBeforeTruncation = null,
$columnsAggregationOperation,
$columnsToRenameAfterAggregation = null,
$countRowsRecursive = array());
}
$columnsAggregationOperation = null;
// sum up goal overview reports
$this->getProcessor()->aggregateDataTableRecords(array(
self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
$this->getProcessor()->aggregateDataTableRecords(
array(self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)),
$maximumRowsInDataTableLevelZero = null,
$maximumRowsInSubDataTable = null,
$columnToSortByBeforeTruncation = null,
$columnsAggregationOperation,
$columnsToRenameAfterAggregation = null,
$countRowsRecursive = array());
}
}

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\Goals\Columns;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
class DaysToConversion extends Dimension
{
public function getName()
{
return Piwik::translate('Goals_DaysToConv');
}
}

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\Goals\Columns;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\ConversionDimension;
use Piwik\Plugin\Segment;
class IdGoal extends ConversionDimension
{
protected $columnName = 'idgoal';
protected function configureSegments()
{
$segment = new Segment();
$segment->setCategory('General_Visit');
$segment->setName('General_VisitConvertedGoalId');
$segment->setSegment('visitConvertedGoalId');
$segment->setAcceptedValues('1, 2, 3, etc.');
$this->addSegment($segment);
}
public function getName()
{
return Piwik::translate('General_VisitConvertedGoalId');
}
}

View file

@ -0,0 +1,61 @@
<?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\Goals\Columns\Metrics;
use Piwik\Archive\DataTableFactory;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
/**
* The average value for each order. Calculated as:
*
* revenue / nb_conversions
*
* revenue & nb_conversions are calculated by the Goals archiver.
*/
class AverageOrderRevenue extends ProcessedMetric
{
private $idSite;
public function getName()
{
return 'avg_order_revenue';
}
public function compute(Row $row)
{
$revenue = $this->getMetric($row, 'revenue');
$conversions = $this->getMetric($row, 'nb_conversions');
return Piwik::getQuotientSafe($revenue, $conversions, $precision = 2);
}
public function getTranslatedName()
{
return Piwik::translate('General_AverageOrderValue');
}
public function getDependentMetrics()
{
return array('revenue', 'nb_conversions');
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyMoney($value, $this->idSite);
}
public function beforeFormat($report, DataTable $table)
{
$this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
return !empty($this->idSite); // skip formatting if there is no site to get currency info from
}
}

View file

@ -0,0 +1,64 @@
<?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\Goals\Columns\Metrics;
use Piwik\Archive\DataTableFactory;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Tracker\GoalManager;
/**
* The average price for each ecommerce order or abandoned cart. Calculated as:
*
* price / (orders or abandoned_carts)
*
* price, orders and abandoned_carts are calculated by the Goals archiver.
*/
class AveragePrice extends ProcessedMetric
{
private $idSite;
public function getName()
{
return 'avg_price';
}
public function getTranslatedName()
{
return Piwik::translate('General_AveragePrice');
}
public function compute(Row $row)
{
$price = $this->getMetric($row, 'price');
$orders = $this->getMetric($row, 'orders');
$abandonedCarts = $this->getMetric($row, 'abandoned_carts');
return Piwik::getQuotientSafe($price, $orders === false ? $abandonedCarts : $orders, GoalManager::REVENUE_PRECISION);
}
public function getDependentMetrics()
{
return array('price', 'orders', 'abandoned_carts');
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyMoney($value, $this->idSite);
}
public function beforeFormat($report, DataTable $table)
{
$this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
return !empty($this->idSite); // skip formatting if there is no site to get currency info from
}
}

View file

@ -0,0 +1,47 @@
<?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\Goals\Columns\Metrics;
use Piwik\DataTable\Row;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
/**
* The average amount of products in each order or abandoned cart. Calculated as:
*
* quantity / (orders or abandoned_carts)
*
* quantity, orders and abandoned_carts are calculated by the Goals archiver.
*/
class AverageQuantity extends ProcessedMetric
{
public function getName()
{
return 'avg_quantity';
}
public function getTranslatedName()
{
return Piwik::translate('General_AverageQuantity');
}
public function compute(Row $row)
{
$quantity = $this->getMetric($row, 'quantity');
$orders = $this->getMetric($row, 'orders');
$abandonedCarts = $this->getMetric($row, 'abandoned_carts');
return Piwik::getQuotientSafe($quantity, $orders === false ? $abandonedCarts : $orders, $precision = 1);
}
public function getDependentMetrics()
{
return array('quantity', 'orders', 'abandoned_carts');
}
}

View file

@ -0,0 +1,68 @@
<?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\Goals\Columns\Metrics\GoalSpecific;
use Piwik\Archive\DataTableFactory;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecificProcessedMetric;
use Piwik\Tracker\GoalManager;
/**
* The average order revenue for a specific goal. Calculated as:
*
* goals' revenue / goal's nb_conversions
*/
class AverageOrderRevenue extends GoalSpecificProcessedMetric
{
public function getName()
{
return $this->getColumnPrefix() . '_avg_order_revenue';
}
public function getTranslatedName()
{
return Piwik::translate('General_AverageOrderValue');
}
public function getDocumentation()
{
return Piwik::translate('Goals_ColumnAverageOrderRevenueDocumentation', $this->getGoalNameForDocs());
}
public function getDependentMetrics()
{
return array('goals');
}
public function compute(Row $row)
{
$mappingFromNameToIdGoal = Metrics::getMappingFromNameToIdGoal();
$goalMetrics = $this->getGoalMetrics($row);
$goalRevenue = $this->getMetric($goalMetrics, 'revenue', $mappingFromNameToIdGoal);
$conversions = $this->getMetric($goalMetrics, 'nb_conversions', $mappingFromNameToIdGoal);
return Piwik::getQuotientSafe($goalRevenue, $conversions, GoalManager::REVENUE_PRECISION);
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyMoney($value, $this->idSite);
}
public function beforeFormat($report, DataTable $table)
{
$this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
return !empty($this->idSite); // skip formatting if there is no site to get currency info from
}
}

View file

@ -0,0 +1,63 @@
<?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\Goals\Columns\Metrics\GoalSpecific;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecificProcessedMetric;
use Piwik\Tracker\GoalManager;
/**
* The conversion rate for a specific goal. Calculated as:
*
* goal's nb_conversions / nb_visits
*
* The goal's nb_conversions is calculated by the Goal archiver and nb_visits
* by the core archiving process.
*/
class ConversionRate extends GoalSpecificProcessedMetric
{
public function getName()
{
return $this->getColumnPrefix() . '_conversion_rate';
}
public function getTranslatedName()
{
return Piwik::translate('Goals_ConversionRate', $this->getGoalName());
}
public function getDocumentation()
{
return Piwik::translate('Goals_ColumnConversionRateDocumentation', $this->getGoalNameForDocs());
}
public function getDependentMetrics()
{
return array('goals');
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyPercentFromQuotient($value);
}
public function compute(Row $row)
{
$mappingFromNameToIdGoal = Metrics::getMappingFromNameToIdGoal();
$goalMetrics = $this->getGoalMetrics($row);
$nbVisits = $this->getMetric($row, 'nb_visits');
$conversions = $this->getMetric($goalMetrics, 'nb_conversions', $mappingFromNameToIdGoal);
return Piwik::getQuotientSafe($conversions, $nbVisits, GoalManager::REVENUE_PRECISION + 2);
}
}

View file

@ -0,0 +1,43 @@
<?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\Goals\Columns\Metrics\GoalSpecific;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecificProcessedMetric;
/**
* The conversions for a specific goal. Returns the conversions for a single goal which
* is then treated as a new column.
*/
class Conversions extends GoalSpecificProcessedMetric
{
public function getName()
{
return $this->getColumnPrefix() . '_nb_conversions';
}
public function getTranslatedName()
{
return Piwik::translate('Goals_Conversions', $this->getGoalNameForDocs());
}
public function getDependentMetrics()
{
return array('goals');
}
public function compute(Row $row)
{
$mappingFromNameToIdGoal = Metrics::getMappingFromNameToIdGoal();
$goalMetrics = $this->getGoalMetrics($row);
return (int) $this->getMetric($goalMetrics, 'nb_conversions', $mappingFromNameToIdGoal);
}
}

View file

@ -0,0 +1,48 @@
<?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\Goals\Columns\Metrics\GoalSpecific;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecificProcessedMetric;
/**
* The number of ecommerce order items for conversions of a goal. Returns the 'items'
* goal specific metric.
*/
class ItemsCount extends GoalSpecificProcessedMetric
{
public function getName()
{
return $this->getColumnPrefix() . '_items';
}
public function getTranslatedName()
{
return Piwik::translate('General_PurchasedProducts');
}
public function getDocumentation()
{
return Piwik::translate('Goals_ColumnPurchasedProductsDocumentation', $this->getGoalNameForDocs());
}
public function getDependentMetrics()
{
return array('goals');
}
public function compute(Row $row)
{
$mappingFromNameToIdGoal = Metrics::getMappingFromNameToIdGoal();
$goalMetrics = $this->getGoalMetrics($row);
return (int) $this->getMetric($goalMetrics, 'items', $mappingFromNameToIdGoal);
}
}

View file

@ -0,0 +1,61 @@
<?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\Goals\Columns\Metrics\GoalSpecific;
use Piwik\Archive\DataTableFactory;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecificProcessedMetric;
/**
* Revenue for a specific goal.
*/
class Revenue extends GoalSpecificProcessedMetric
{
public function getName()
{
return $this->getColumnPrefix() . '_revenue';
}
public function getTranslatedName()
{
return Piwik::translate('%s ' . Piwik::translate('General_ColumnRevenue'), $this->getGoalName());
}
public function getDocumentation()
{
return Piwik::translate('Goals_ColumnRevenueDocumentation', $this->getGoalNameForDocs());
}
public function getDependentMetrics()
{
return array('goals');
}
public function compute(Row $row)
{
$mappingFromNameToIdGoal = Metrics::getMappingFromNameToIdGoal();
$goalMetrics = $this->getGoalMetrics($row);
return (float) $this->getMetric($goalMetrics, 'revenue', $mappingFromNameToIdGoal);
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyMoney($value, $this->idSite);
}
public function beforeFormat($report, DataTable $table)
{
$this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
return !empty($this->idSite); // skip formatting if there is no site to get currency info from
}
}

View file

@ -0,0 +1,76 @@
<?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\Goals\Columns\Metrics\GoalSpecific;
use Piwik\Archive\DataTableFactory;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecificProcessedMetric;
use Piwik\Tracker\GoalManager;
/**
* Revenue per visit for a specific goal. Calculated as:
*
* goal's revenue / (nb_visits or goal's nb_conversions depending on what is present in data)
*
* Goal revenue & nb_conversion are calculated by the Goals archiver.
*/
class RevenuePerVisit extends GoalSpecificProcessedMetric
{
public function getName()
{
return $this->getColumnPrefix() . '_revenue_per_visit';
}
public function getTranslatedName()
{
return $this->getGoalName() . ' ' . Piwik::translate('General_ColumnValuePerVisit');
}
public function getDocumentation()
{
if ($this->idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
return Piwik::translate('Goals_ColumnAverageOrderRevenueDocumentation', $this->getGoalNameForDocs());
} else {
return Piwik::translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik::translate('Goals_EcommerceAndGoalsMenu'));
}
}
public function getDependentMetrics()
{
return array('goals', 'nb_visits');
}
public function compute(Row $row)
{
$mappingFromNameToIdGoal = Metrics::getMappingFromNameToIdGoal();
$goalMetrics = $this->getGoalMetrics($row);
$nbVisits = $this->getMetric($row, 'nb_visits');
$conversions = $this->getMetric($goalMetrics, 'nb_conversions', $mappingFromNameToIdGoal);
$goalRevenue = (float) $this->getMetric($goalMetrics, 'revenue', $mappingFromNameToIdGoal);
return Piwik::getQuotientSafe($goalRevenue, $nbVisits == 0 ? $conversions : $nbVisits, GoalManager::REVENUE_PRECISION);
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyMoney($value, $this->idSite);
}
public function beforeFormat($report, DataTable $table)
{
$this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
return !empty($this->idSite); // skip formatting if there is no site to get currency info from
}
}

View file

@ -0,0 +1,104 @@
<?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\Goals\Columns\Metrics;
use Piwik\Common;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Plugins\Goals\API as GoalsAPI;
use Piwik\Tracker\GoalManager;
/**
* Base class for processed metrics that are calculated using metrics that are
* specific to certain goals.
*/
abstract class GoalSpecificProcessedMetric extends ProcessedMetric
{
/**
* The ID of the goal to calculate metrics for.
*
* @var int
*/
protected $idGoal;
/**
* The ID of the site the goal belongs to.
*
* @var int
*/
protected $idSite;
/**
* Constructor.
*
* @param int|null $idSite The ID of the site the goal belongs to. If supplied, affects the formatting
* and translated name of the metric.
* @param int $idGoal The ID of the goal to calculate metrics for.
*/
public function __construct($idSite, $idGoal)
{
$this->idSite = $idSite;
$this->idGoal = $idGoal;
}
protected function getColumnPrefix()
{
return 'goal_' . $this->idGoal;
}
protected function getGoalMetrics(Row $row)
{
$allGoalMetrics = $this->getMetric($row, 'goals');
if (isset($allGoalMetrics[$this->idGoal])) {
return $allGoalMetrics[$this->idGoal];
} else {
$alternateKey = 'idgoal=' . $this->idGoal;
if (isset($allGoalMetrics[$alternateKey])) {
return $allGoalMetrics[$alternateKey];
} elseif ($this->idGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$alternateKey = GoalManager::IDGOAL_ORDER;
if (isset($allGoalMetrics[$alternateKey])) {
return $allGoalMetrics[$alternateKey];
}
} elseif ($this->idGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
$alternateKey = GoalManager::IDGOAL_CART;
if (isset($allGoalMetrics[$alternateKey])) {
return $allGoalMetrics[$alternateKey];
}
} else {
return array();
}
}
}
protected function getGoalName()
{
if ($this->idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
return Piwik::translate('Goals_EcommerceOrder');
}
if (isset($this->idSite)) {
$allGoals = GoalsAPI::getInstance()->getGoals($this->idSite);
$goalName = @$allGoals[$this->idGoal]['name'];
return Common::sanitizeInputValue($goalName);
} else {
return "";
}
}
protected function getGoalNameForDocs()
{
$goalName = $this->getGoalName();
if ($goalName == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$goalName = '"' . $goalName . '"';
}
return $goalName;
}
}

View file

@ -0,0 +1,54 @@
<?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\Goals\Columns\Metrics;
use Piwik\DataTable\Row;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Tracker\GoalManager;
/**
* The conversion rate for ecommerce orders. Calculated as:
*
* (orders or abandoned_carts) / nb_visits
*
* orders and abandoned_carts are calculated by the Goals archiver.
*/
class ProductConversionRate extends ProcessedMetric
{
public function getName()
{
return 'conversion_rate';
}
public function getTranslatedName()
{
return Piwik::translate('General_ProductConversionRate');
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyPercentFromQuotient($value);
}
public function compute(Row $row)
{
$orders = $this->getMetric($row, 'orders');
$abandonedCarts = $this->getMetric($row, 'abandoned_carts');
$visits = $this->getMetric($row, 'nb_visits');
return Piwik::getQuotientSafe($orders === false ? $abandonedCarts : $orders, $visits, GoalManager::REVENUE_PRECISION + 2);
}
public function getDependentMetrics()
{
return array('orders', 'abandoned_carts', 'nb_visits');
}
}

View file

@ -0,0 +1,84 @@
<?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\Goals\Columns\Metrics;
use Piwik\Archive\DataTableFactory;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Metrics;
use Piwik\Metrics\Formatter;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Tracker\GoalManager;
/**
* The amount of revenue per visit (or per conversion if there are no visits). Calculated as:
*
* sum(revenue for all goals) / (nb_visits or nb_conversions if no visits)
*
* Goal revenue and nb_visits & nb_conversions are calculated by the archiving process.
*/
class RevenuePerVisit extends ProcessedMetric
{
private $idSite;
public function getName()
{
return 'revenue_per_visit';
}
public function getTranslatedName()
{
return Piwik::translate('General_ColumnValuePerVisit');
}
public function getDependentMetrics()
{
return array('revenue', 'nb_visits', 'nb_conversions','goals');
}
public function compute(Row $row)
{
$mappingFromNameToIdGoal = Metrics::getMappingFromNameToIdGoal();
$goals = $this->getMetric($row, 'goals') ?: array();
$revenue = 0;
foreach ($goals as $goalId => $goalMetrics) {
if ($goalId == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
continue;
}
if ($goalId >= GoalManager::IDGOAL_ORDER
|| $goalId == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER
) {
$revenue += (int) $this->getMetric($goalMetrics, 'revenue', $mappingFromNameToIdGoal);
}
}
if ($revenue == 0) {
$revenue = (int) $this->getMetric($row, 'revenue');
}
$nbVisits = (int) $this->getMetric($row, 'nb_visits');
$conversions = (int) $this->getMetric($row, 'nb_conversions');
// If no visit for this metric, but some conversions, we still want to display some kind of "revenue per visit"
// even though it will actually be in this edge case "Revenue per conversion"
return Piwik::getQuotientSafe($revenue, $nbVisits == 0 ? $conversions : $nbVisits, GoalManager::REVENUE_PRECISION);
}
public function format($value, Formatter $formatter)
{
return $formatter->getPrettyMoney($value, $this->idSite);
}
public function beforeFormat($report, DataTable $table)
{
$this->idSite = DataTableFactory::getSiteIdFromMetadata($table);
return !empty($this->idSite); // skip formatting if there is no site to get currency info from
}
}

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\Goals\Columns;
use Piwik\Columns\Dimension;
use Piwik\Piwik;
class VisitsUntilConversion extends Dimension
{
public function getName()
{
return Piwik::translate('Goals_VisitsUntilConv');
}
}

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,15 @@
*/
namespace Piwik\Plugins\Goals;
use Exception;
use Piwik\API\Request;
use Piwik\Common;
use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal;
use Piwik\DataTable;
use Piwik\FrontController;
use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal;
use Piwik\Piwik;
use Piwik\Plugins\Referrers\API as APIReferrers;
use Piwik\View\ReportsByDimension;
use Piwik\Translation\Translator;
use Piwik\View;
use Piwik\ViewDataTable\Factory;
use Piwik\View\ReportsByDimension;
/**
*
@ -44,30 +42,42 @@ class Controller extends \Piwik\Plugin\Controller
'items' => 'General_PurchasedProducts',
);
private function formatConversionRate($conversionRate)
/**
* @var Translator
*/
private $translator;
/**
* @var TranslationHelper
*/
private $translationHelper;
private function formatConversionRate($conversionRate, $columnName = 'conversion_rate')
{
if ($conversionRate instanceof DataTable) {
if ($conversionRate->getRowsCount() == 0) {
$conversionRate = 0;
} else {
$columns = $conversionRate->getFirstRow()->getColumns();
$conversionRate = (float)reset($columns);
$conversionRate = $conversionRate->getFirstRow()->getColumn($columnName);
}
}
return sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
if (!is_numeric($conversionRate)) {
$conversionRate = sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
}
return $conversionRate;
}
public function __construct()
public function __construct(Translator $translator, TranslationHelper $translationHelper)
{
parent::__construct();
$this->translator = $translator;
$this->translationHelper = $translationHelper;
$this->idSite = Common::getRequestVar('idSite', null, 'int');
$this->goals = API::getInstance()->getGoals($this->idSite);
foreach ($this->goals as &$goal) {
$goal['name'] = Common::sanitizeInputValue($goal['name']);
if (isset($goal['pattern'])) {
$goal['pattern'] = Common::sanitizeInputValue($goal['pattern']);
}
}
}
public function widgetGoalReport()
@ -84,39 +94,11 @@ class Controller extends \Piwik\Plugin\Controller
return $view->render();
}
public function ecommerceReport()
{
if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated('CustomVariables')) {
throw new Exception("Ecommerce Tracking requires that the plugin Custom Variables is enabled. Please enable the plugin CustomVariables (or ask your admin).");
}
$view = $this->getGoalReportView($idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER);
$view->displayFullReport = true;
return $view->render();
}
public function getEcommerceLog($fetch = false)
{
$saveGET = $_GET;
$filterEcommerce = Common::getRequestVar('filterEcommerce', self::ECOMMERCE_LOG_SHOW_ORDERS, 'int');
if($filterEcommerce == self::ECOMMERCE_LOG_SHOW_ORDERS) {
$segment = urlencode('visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart');
} else {
$segment = urlencode('visitEcommerceStatus==abandonedCart,visitEcommerceStatus==orderedThenAbandonedCart');
}
$_GET['segment'] = $segment;
$_GET['filterEcommerce'] = $filterEcommerce;
$_GET['widget'] = 1;
$output = FrontController::getInstance()->dispatch('Live', 'getVisitorLog', array($fetch));
$_GET = $saveGET;
return $output;
}
protected function getGoalReportView($idGoal = false)
{
$view = new View('@Goals/getGoalReportView');
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$goalDefinition['name'] = Piwik::translate('Goals_Ecommerce');
$goalDefinition['name'] = $this->translator->translate('Goals_Ecommerce');
$goalDefinition['allow_multiple'] = true;
$ecommerce = $view->ecommerce = true;
} else {
@ -137,20 +119,20 @@ class Controller extends \Piwik\Plugin\Controller
$view->$name = $value;
}
}
$view->showHeadline = false;
$view->idGoal = $idGoal;
$view->goalName = $goalDefinition['name'];
$view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple'];
$view->graphEvolution = $this->getEvolutionGraph(array('nb_conversions'), $idGoal);
$view->graphEvolution = $this->getEvolutionGraph(array(), $idGoal, array('nb_conversions'));
$view->nameGraphEvolution = 'Goals.getEvolutionGraph' . $idGoal;
$view->topDimensions = $this->getTopDimensions($idGoal);
$goalMetrics = Request::processRequest('Goals.get', array('idGoal' => $idGoal));
// conversion rate for new and returning visitors
$segment = urldecode(\Piwik\Plugins\VisitFrequency\API::RETURNING_VISITOR_SEGMENT);
$conversionRateReturning = API::getInstance()->getConversionRate($this->idSite, Common::getRequestVar('period'), Common::getRequestVar('date'), $segment, $idGoal);
$view->conversion_rate_returning = $this->formatConversionRate($conversionRateReturning);
$segment = 'visitorType==new';
$conversionRateNew = API::getInstance()->getConversionRate($this->idSite, Common::getRequestVar('period'), Common::getRequestVar('date'), $segment, $idGoal);
$view->conversion_rate_new = $this->formatConversionRate($conversionRateNew);
$view->conversion_rate_returning = $this->formatConversionRate($goalMetrics, 'conversion_rate_returning_visit');
$view->conversion_rate_new = $this->formatConversionRate($goalMetrics, 'conversion_rate_new_visit');
$view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
$view->nb_conversions, isset($ecommerce), !empty($view->cart_nb_conversions));
return $view;
@ -163,20 +145,24 @@ class Controller extends \Piwik\Plugin\Controller
// unsanitize goal names and other text data (not done in API so as not to break
// any other code/cause security issues)
$goals = $this->goals;
foreach ($goals as &$goal) {
$goal['name'] = Common::unsanitizeInputValue($goal['name']);
if (isset($goal['pattern'])) {
$goal['pattern'] = Common::unsanitizeInputValue($goal['pattern']);
}
}
$view->goalsJSON = Common::json_encode($goals);
$view->goalsJSON = json_encode($goals);
$view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
$view->ecommerceEnabled = $this->site->isEcommerceEnabled();
$view->displayFullReport = true;
return $view->render();
}
public function manage()
{
Piwik::checkUserHasAdminAccess($this->idSite);
$view = new View('@Goals/manageGoals');
$this->setGeneralVariablesView($view);
$this->setEditGoalsViewVariables($view);
$this->setUserCanEditGoals($view);
return $view->render();
}
public function widgetGoalsOverview()
{
$view = $this->getOverviewView();
@ -189,7 +175,7 @@ class Controller extends \Piwik\Plugin\Controller
$view = new View('@Goals/getOverviewView');
$this->setGeneralVariablesView($view);
$view->graphEvolution = $this->getEvolutionGraph(array('nb_conversions'));
$view->graphEvolution = $this->getEvolutionGraph(array(), false, array('nb_conversions'));
$view->nameGraphEvolution = 'GoalsgetEvolutionGraph';
// sparkline for the historical data of the above values
@ -201,6 +187,7 @@ class Controller extends \Piwik\Plugin\Controller
$request = new Request("method=Goals.get&format=original&idGoal=");
$datatable = $request->process();
$dataRow = $datatable->getFirstRow();
$view->nb_conversions = $dataRow->getColumn('nb_conversions');
$view->nb_visits_converted = $dataRow->getColumn('nb_visits_converted');
$view->conversion_rate = $this->formatConversionRate($dataRow->getColumn('conversion_rate'));
@ -242,19 +229,32 @@ class Controller extends \Piwik\Plugin\Controller
{
$view = new View('@Goals/addNewGoal');
$this->setGeneralVariablesView($view);
$view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
$this->setUserCanEditGoals($view);
$view->onlyShowAddNewGoal = true;
return $view->render();
}
public function getEvolutionGraph(array $columns = array(), $idGoal = false)
public function editGoals()
{
$view = new View('@Goals/editGoals');
$this->setGeneralVariablesView($view);
$this->setEditGoalsViewVariables($view);
$this->setUserCanEditGoals($view);
return $view->render();
}
public function getEvolutionGraph(array $columns = array(), $idGoal = false, array $defaultColumns = array())
{
if (empty($columns)) {
$columns = Common::getRequestVar('columns');
$columns = Piwik::getArrayFromApiParameter($columns);
$columns = Common::getRequestVar('columns', false);
if (false !== $columns) {
$columns = Piwik::getArrayFromApiParameter($columns);
}
}
$columns = !is_array($columns) ? array($columns) : $columns;
if (false !== $columns) {
$columns = !is_array($columns) ? array($columns) : $columns;
}
if (empty($idGoal)) {
$idGoal = Common::getRequestVar('idGoal', false, 'string');
@ -266,10 +266,10 @@ class Controller extends \Piwik\Plugin\Controller
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
$nameToLabel['nb_conversions'] = 'General_EcommerceOrders';
} elseif ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
$nameToLabel['nb_conversions'] = Piwik::translate('General_VisitsWith', Piwik::translate('Goals_AbandonedCart'));
$nameToLabel['nb_conversions'] = $this->translator->translate('General_VisitsWith', $this->translator->translate('Goals_AbandonedCart'));
$nameToLabel['conversion_rate'] = $nameToLabel['nb_conversions'];
$nameToLabel['revenue'] = Piwik::translate('Goals_LeftInCart', Piwik::translate('General_ColumnRevenue'));
$nameToLabel['items'] = Piwik::translate('Goals_LeftInCart', Piwik::translate('Goals_Products'));
$nameToLabel['revenue'] = $this->translator->translate('Goals_LeftInCart', $this->translator->translate('General_ColumnRevenue'));
$nameToLabel['items'] = $this->translator->translate('Goals_LeftInCart', $this->translator->translate('Goals_Products'));
}
$selectableColumns = array('nb_conversions', 'conversion_rate', 'revenue');
@ -278,27 +278,33 @@ class Controller extends \Piwik\Plugin\Controller
$selectableColumns[] = 'avg_order_revenue';
}
foreach (array_merge($columns, $selectableColumns) as $columnName) {
foreach (array_merge($columns ? $columns : array(), $selectableColumns) as $columnName) {
$columnTranslation = '';
// find the right translation for this column, eg. find 'revenue' if column is Goal_1_revenue
foreach ($nameToLabel as $metric => $metricTranslation) {
if (strpos($columnName, $metric) !== false) {
$columnTranslation = Piwik::translate($metricTranslation);
$columnTranslation = $this->translator->translate($metricTranslation);
break;
}
}
if (!empty($idGoal) && isset($this->goals[$idGoal])) {
$goalName = $this->goals[$idGoal]['name'];
$columnTranslation = "$columnTranslation (" . Piwik::translate('Goals_GoalX', "$goalName") . ")";
$columnTranslation = "$columnTranslation (" . $this->translator->translate('Goals_GoalX', "$goalName") . ")";
}
$view->config->translations[$columnName] = $columnTranslation;
}
$view->config->columns_to_display = $columns;
if (!empty($columns)) {
$view->config->columns_to_display = $columns;
} elseif (empty($view->config->columns_to_display) && !empty($defaultColumns)) {
$view->config->columns_to_display = $defaultColumns;
}
$view->config->selectable_columns = $selectableColumns;
$langString = $idGoal ? 'Goals_SingleGoalOverviewDocumentation' : 'Goals_GoalsOverviewDocumentation';
$view->config->documentation = Piwik::translate($langString, '<br />');
$view->config->documentation = $this->translator->translate($langString, '<br />');
return $this->renderView($view);
}
@ -327,11 +333,11 @@ class Controller extends \Piwik\Plugin\Controller
$topDimensions = array();
foreach ($topDimensionsToLoad as $dimensionName => $apiMethod) {
$request = new Request("method=$apiMethod
&format=original
&filter_update_columns_when_show_all_goals=1
&idGoal=" . AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE . "
&filter_sort_order=desc
&filter_sort_column=$columnNbConversions" .
&format=original
&filter_update_columns_when_show_all_goals=1
&idGoal=" . AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE . "
&filter_sort_order=desc
&filter_sort_column=$columnNbConversions" .
// select a couple more in case some are not valid (ie. conversions==0 or they are "Keyword not defined")
"&filter_limit=" . (self::COUNT_TOP_ROWS_TO_DISPLAY + 2));
$datatable = $request->process();
@ -421,22 +427,13 @@ class Controller extends \Piwik\Plugin\Controller
$ecommerceCustomParams = array();
if ($ecommerce) {
if ($preloadAbandonedCart) {
$ecommerceCustomParams['viewDataTable'] = 'ecommerceAbandonedCart';
$ecommerceCustomParams['filterEcommerce'] = self::ECOMMERCE_LOG_SHOW_ABANDONED_CARTS;
$ecommerceCustomParams['abandonedCarts'] = '1';
} else {
$ecommerceCustomParams['abandonedCarts'] = '0';
}
$goalReportsByDimension->addReport(
'Goals_EcommerceReports', 'Goals_ProductSKU', 'Goals.getItemsSku', $ecommerceCustomParams);
$goalReportsByDimension->addReport(
'Goals_EcommerceReports', 'Goals_ProductName', 'Goals.getItemsName', $ecommerceCustomParams);
$goalReportsByDimension->addReport(
'Goals_EcommerceReports', 'Goals_ProductCategory', 'Goals.getItemsCategory', $ecommerceCustomParams);
$goalReportsByDimension->addReport(
'Goals_EcommerceReports', 'Goals_EcommerceLog', 'Goals.getEcommerceLog', $ecommerceCustomParams);
}
if ($conversions > 0) {
if ($conversions > 0 || $ecommerce) {
// for non-Goals reports, we show the goals table
$customParams = $ecommerceCustomParams + array('documentationForGoalsPage' => '1');
@ -447,15 +444,28 @@ class Controller extends \Piwik\Plugin\Controller
$allReports = Goals::getReportsWithGoalMetrics();
foreach ($allReports as $category => $reports) {
$categoryText = Piwik::translate('Goals_ViewGoalsBy', $category);
if ($ecommerce) {
$categoryText = $this->translationHelper->translateEcommerceMetricCategory($category);
} else {
$categoryText = $this->translationHelper->translateGoalMetricCategory($category);
}
foreach ($reports as $report) {
if(empty($report['viewDataTable'])) {
if (empty($report['viewDataTable'])
&& empty($report['abandonedCarts'])
) {
$report['viewDataTable'] = 'tableGoals';
}
$customParams['viewDataTable'] = $report['viewDataTable'];
if (!empty($report['parameters'])) {
$params = array_merge($customParams, $report['parameters']);
} else {
$params = $customParams;
}
$goalReportsByDimension->addReport(
$categoryText, $report['name'], $report['module'] . '.' . $report['action'], $customParams);
$categoryText, $report['name'], $report['module'] . '.' . $report['action'], $params);
}
}
}
@ -463,32 +473,32 @@ class Controller extends \Piwik\Plugin\Controller
return $goalReportsByDimension->render();
}
//
// Report rendering actions
//
public function getItemsSku()
private function setEditGoalsViewVariables($view)
{
return $this->renderReport(__FUNCTION__);
$goals = $this->goals;
$view->goals = $goals;
$idGoal = Common::getRequestVar('idGoal', 0, 'int');
$view->idGoal = 0;
if ($idGoal && array_key_exists($idGoal, $goals)) {
$view->idGoal = $idGoal;
}
// unsanitize goal names and other text data (not done in API so as not to break
// any other code/cause security issues)
foreach ($goals as &$goal) {
$goal['name'] = Common::unsanitizeInputValue($goal['name']);
if (isset($goal['pattern'])) {
$goal['pattern'] = Common::unsanitizeInputValue($goal['pattern']);
}
}
$view->goalsJSON = json_encode($goals);
$view->ecommerceEnabled = $this->site->isEcommerceEnabled();
}
public function getItemsName()
private function setUserCanEditGoals(View $view)
{
return $this->renderReport(__FUNCTION__);
}
public function getItemsCategory()
{
return $this->renderReport(__FUNCTION__);
}
public function getVisitsUntilConversion()
{
return $this->renderReport(__FUNCTION__);
}
public function getDaysToConversion()
{
return $this->renderReport(__FUNCTION__);
$view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
}
}

View file

@ -0,0 +1,57 @@
<?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\Goals\DataTable\Filter;
use Piwik\DataTable\BaseFilter;
use Piwik\DataTable;
use Piwik\Metrics;
/**
* Appends a string to each column name in each row of a table. Please note this filter even appends the name to a
* 'label' column. If you do not need this behaviour feel free to add a check to ignore label columns.
*/
class AppendNameToColumnNames extends BaseFilter
{
protected $nameToAppend;
/**
* Constructor.
*
* @param DataTable $table The table that will be eventually filtered.
* @param string $nameToAppend The name that will be appended to each column
*/
public function __construct($table, $nameToAppend)
{
parent::__construct($table);
$this->nameToAppend = $nameToAppend;
}
/**
* See {@link ReplaceColumnNames}.
*
* @param DataTable $table
*/
public function filter($table)
{
if (!isset($this->nameToAppend) || '' === $this->nameToAppend || false === $this->nameToAppend) {
return;
}
foreach ($table->getRows() as $row) {
$columns = $row->getColumns();
foreach ($columns as $column => $value) {
$row->deleteColumn($column);
$row->setColumn($column . $this->nameToAppend, $value);
}
$this->filterSubTable($row);
}
}
}

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,44 +9,27 @@
namespace Piwik\Plugins\Goals;
use Piwik\ArchiveProcessor;
use Piwik\Common;
use Piwik\Db;
use Piwik\Menu\MenuMain;
use Piwik\Piwik;
use Piwik\Plugin\ViewDataTable;
use Piwik\Site;
use Piwik\Plugin\Report;
use Piwik\Tracker\GoalManager;
use Piwik\Translate;
use Piwik\WidgetsList;
/**
*
*/
class Goals extends \Piwik\Plugin
{
public function getInformation()
{
$suffix = Piwik::translate('SitesManager_PiwikOffersEcommerceAnalytics',
array('<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">', '</a>'));
$info = parent::getInformation();
$info['description'] .= ' ' . $suffix;
return $info;
}
protected $ecommerceReports = array(
array('Goals_ProductSKU', 'Goals', 'getItemsSku'),
array('Goals_ProductName', 'Goals', 'getItemsName'),
array('Goals_ProductCategory', 'Goals', 'getItemsCategory')
);
static public function getReportsWithGoalMetrics()
public static function getReportsWithGoalMetrics()
{
$dimensions = self::getAllReportsWithGoalMetrics();
$dimensionsByGroup = array();
foreach ($dimensions as $dimension) {
$group = $dimension['category'];
// move "Custom Variables" report to the "Goals/Sales by User attribute" category
if ($dimension['module'] === 'CustomVariables') {
$group = 'VisitsSummary_VisitsSummary';
}
unset($dimension['category']);
$dimensionsByGroup[$group][] = $dimension;
}
@ -57,22 +40,28 @@ class Goals extends \Piwik\Plugin
public static function sortGoalDimensionsByModule($a, $b)
{
$order = array(
Piwik::translate('Referrers_Referrers'),
Piwik::translate('General_Visit'),
Piwik::translate('VisitTime_ColumnServerTime'),
);
static $order = null;
if (is_null($order)) {
$order = array(
'Referrers_Referrers',
'General_Visit',
'General_Visitors',
'VisitsSummary_VisitsSummary',
'VisitTime_ColumnServerTime',
);
}
$orderA = array_search($a, $order);
$orderB = array_search($b, $order);
return $orderA > $orderB;
}
static public function getGoalColumns($idGoal)
public static function getGoalColumns($idGoal)
{
$columns = array(
'nb_conversions',
'nb_visits_converted',
'conversion_rate',
'revenue',
);
if ($idGoal === false) {
@ -95,38 +84,51 @@ class Goals extends \Piwik\Plugin
}
/**
* @see Piwik\Plugin::getListHooksRegistered
* @see Piwik\Plugin::registerEvents
*/
public function getListHooksRegistered()
public function registerEvents()
{
$hooks = array(
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'Tracker.Cache.getSiteAttributes' => 'fetchGoalsFromDb',
'API.getReportMetadata.end' => 'getReportMetadata',
'API.getSegmentDimensionMetadata' => 'getSegmentsMetadata',
'WidgetsList.addWidgets' => 'addWidgets',
'Menu.Reporting.addItems' => 'addMenus',
'API.getReportMetadata.end' => 'getReportMetadataEnd',
'SitesManager.deleteSite.end' => 'deleteSiteGoals',
'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics',
'ViewDataTable.configure' => 'configureViewDataTable',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
'ViewDataTable.addViewDataTable' => 'getAvailableDataTableVisualizations'
'Metrics.getDefaultMetricTranslations' => 'addMetricTranslations'
);
return $hooks;
}
public function getAvailableDataTableVisualizations(&$visualizations)
public function addMetricTranslations(&$translations)
{
$visualizations[] = 'Piwik\\Plugins\\Goals\\Visualizations\\Goals';
$metrics = array(
'orders' => 'General_EcommerceOrders',
'ecommerce_revenue' => 'General_ProductRevenue',
'revenue_per_visit' => 'General_ColumnValuePerVisit',
'quantity' => 'General_Quantity',
'avg_price' => 'General_AveragePrice',
'avg_quantity' => 'General_AverageQuantity',
'revenue_subtotal' => 'General_Subtotal',
'revenue_tax' => 'General_Tax',
'revenue_shipping' => 'General_Shipping',
'revenue_discount' => 'General_Discount',
'avg_order_revenue' => 'General_AverageOrderValue'
);
$metrics = array_map(array('\\Piwik\\Piwik', 'translate'), $metrics);
$translations = array_merge($translations, $metrics);
}
/**
* Delete goals recorded for this site
*/
function deleteSiteGoals($idSite)
public function deleteSiteGoals($idSite)
{
Db::query("DELETE FROM " . Common::prefixTable('goal') . " WHERE idsite = ? ", array($idSite));
$model = new Model();
$model->deleteGoalsForSite($idSite);
}
/**
@ -136,10 +138,8 @@ class Goals extends \Piwik\Plugin
*
* Also, this will update metadata of all other reports that have Goal segmentation
*/
public function getReportMetadata(&$reports, $info)
public function getReportMetadataEnd(&$reports, $info)
{
$idSites = $info['idSites'];
// Processed in AddColumnsProcessedMetricsGoal
// These metrics will also be available for some reports, for each goal
// Example: Conversion rate for Goal 2 for the keyword 'piwik'
@ -148,210 +148,11 @@ class Goals extends \Piwik\Plugin
);
$goalMetrics = array(
'nb_conversions' => Piwik::translate('Goals_ColumnConversions'),
'nb_visits_converted' => Piwik::translate('General_ColumnVisitsWithConversions'),
'conversion_rate' => Piwik::translate('General_ColumnConversionRate'),
'revenue' => Piwik::translate('General_ColumnRevenue')
'nb_conversions' => Piwik::translate('Goals_ColumnConversions'),
'conversion_rate' => Piwik::translate('General_ColumnConversionRate'),
'revenue' => Piwik::translate('General_ColumnRevenue')
);
$conversionReportMetrics = array(
'nb_conversions' => Piwik::translate('Goals_ColumnConversions')
);
// General Goal metrics: conversions, conv rate, revenue
$goalsCategory = Piwik::translate('Goals_Goals');
$reports[] = array(
'category' => $goalsCategory,
'name' => Piwik::translate('Goals_Goals'),
'module' => 'Goals',
'action' => 'get',
'metrics' => $goalMetrics,
'processedMetrics' => array(),
'order' => 1
);
// If only one website is selected, we add the Goal metrics
if (count($idSites) == 1) {
$idSite = reset($idSites);
$goals = API::getInstance()->getGoals($idSite);
// Add overall visits to conversion report
$reports[] = array(
'category' => $goalsCategory,
'name' => Piwik::translate('Goals_VisitsUntilConv'),
'module' => 'Goals',
'action' => 'getVisitsUntilConversion',
'dimension' => Piwik::translate('Goals_VisitsUntilConv'),
'constantRowsCount' => true,
'parameters' => array(),
'metrics' => $conversionReportMetrics,
'order' => 5
);
// Add overall days to conversion report
$reports[] = array(
'category' => $goalsCategory,
'name' => Piwik::translate('Goals_DaysToConv'),
'module' => 'Goals',
'action' => 'getDaysToConversion',
'dimension' => Piwik::translate('Goals_DaysToConv'),
'constantRowsCount' => true,
'parameters' => array(),
'metrics' => $conversionReportMetrics,
'order' => 10
);
foreach ($goals as $goal) {
// Add the general Goal metrics: ie. total Goal conversions,
// Goal conv rate or Goal total revenue.
// This API call requires a custom parameter
$goal['name'] = Common::sanitizeInputValue($goal['name']);
$reports[] = array(
'category' => $goalsCategory,
'name' => Piwik::translate('Goals_GoalX', $goal['name']),
'module' => 'Goals',
'action' => 'get',
'parameters' => array('idGoal' => $goal['idgoal']),
'metrics' => $goalMetrics,
'processedMetrics' => false,
'order' => 50 + $goal['idgoal'] * 3
);
// Add visits to conversion report
$reports[] = array(
'category' => $goalsCategory,
'name' => $goal['name'] . ' - ' . Piwik::translate('Goals_VisitsUntilConv'),
'module' => 'Goals',
'action' => 'getVisitsUntilConversion',
'dimension' => Piwik::translate('Goals_VisitsUntilConv'),
'constantRowsCount' => true,
'parameters' => array('idGoal' => $goal['idgoal']),
'metrics' => $conversionReportMetrics,
'order' => 51 + $goal['idgoal'] * 3
);
// Add days to conversion report
$reports[] = array(
'category' => $goalsCategory,
'name' => $goal['name'] . ' - ' . Piwik::translate('Goals_DaysToConv'),
'module' => 'Goals',
'action' => 'getDaysToConversion',
'dimension' => Piwik::translate('Goals_DaysToConv'),
'constantRowsCount' => true,
'parameters' => array('idGoal' => $goal['idgoal']),
'metrics' => $conversionReportMetrics,
'order' => 52 + $goal['idgoal'] * 3
);
}
$site = new Site($idSite);
if ($site->isEcommerceEnabled()) {
$category = Piwik::translate('Goals_Ecommerce');
$ecommerceMetrics = array_merge($goalMetrics, array(
'revenue_subtotal' => Piwik::translate('General_Subtotal'),
'revenue_tax' => Piwik::translate('General_Tax'),
'revenue_shipping' => Piwik::translate('General_Shipping'),
'revenue_discount' => Piwik::translate('General_Discount'),
'items' => Piwik::translate('General_PurchasedProducts'),
'avg_order_revenue' => Piwik::translate('General_AverageOrderValue')
));
$ecommerceMetrics['nb_conversions'] = Piwik::translate('General_EcommerceOrders');
// General Ecommerce metrics
$reports[] = array(
'category' => $category,
'name' => Piwik::translate('General_EcommerceOrders'),
'module' => 'Goals',
'action' => 'get',
'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER),
'metrics' => $ecommerceMetrics,
'processedMetrics' => false,
'order' => 10
);
$reports[] = array(
'category' => $category,
'name' => Piwik::translate('General_EcommerceOrders') . ' - ' . Piwik::translate('Goals_VisitsUntilConv'),
'module' => 'Goals',
'action' => 'getVisitsUntilConversion',
'dimension' => Piwik::translate('Goals_VisitsUntilConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER),
'order' => 11
);
$reports[] = array(
'category' => $category,
'name' => Piwik::translate('General_EcommerceOrders') . ' - ' . Piwik::translate('Goals_DaysToConv'),
'module' => 'Goals',
'action' => 'getDaysToConversion',
'dimension' => Piwik::translate('Goals_DaysToConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER),
'order' => 12
);
// Abandoned cart general metrics
$abandonedCartMetrics = $goalMetrics;
$abandonedCartMetrics['nb_conversions'] = Piwik::translate('General_AbandonedCarts');
$abandonedCartMetrics['revenue'] = Piwik::translate('Goals_LeftInCart', Piwik::translate('General_ColumnRevenue'));
$abandonedCartMetrics['items'] = Piwik::translate('Goals_LeftInCart', Piwik::translate('Goals_Products'));
unset($abandonedCartMetrics['nb_visits_converted']);
// Abandoned Cart metrics
$reports[] = array(
'category' => $category,
'name' => Piwik::translate('General_AbandonedCarts'),
'module' => 'Goals',
'action' => 'get',
'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART),
'metrics' => $abandonedCartMetrics,
'processedMetrics' => false,
'order' => 15
);
$reports[] = array(
'category' => $category,
'name' => Piwik::translate('General_AbandonedCarts') . ' - ' . Piwik::translate('Goals_VisitsUntilConv'),
'module' => 'Goals',
'action' => 'getVisitsUntilConversion',
'dimension' => Piwik::translate('Goals_VisitsUntilConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART),
'order' => 20
);
$reports[] = array(
'category' => $category,
'name' => Piwik::translate('General_AbandonedCarts') . ' - ' . Piwik::translate('Goals_DaysToConv'),
'module' => 'Goals',
'action' => 'getDaysToConversion',
'dimension' => Piwik::translate('Goals_DaysToConv'),
'constantRowsCount' => true,
'metrics' => $conversionReportMetrics,
'parameters' => array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART),
'order' => 25
);
// Product reports metadata
$productColumns = self::getProductReportColumns();
foreach ($this->ecommerceReports as $i => $ecommerceReport) {
$reports[] = array(
'category' => $category,
'name' => Piwik::translate($ecommerceReport[0]),
'module' => 'Goals',
'action' => $ecommerceReport[2],
'dimension' => Piwik::translate($ecommerceReport[0]),
'metrics' => $productColumns,
'processedMetrics' => false,
'order' => 30 + $i
);
}
}
}
unset($goalMetrics['nb_visits_converted']);
$reportsWithGoals = self::getAllReportsWithGoalMetrics();
foreach ($reportsWithGoals as $reportWithGoals) {
@ -360,7 +161,7 @@ class Goals extends \Piwik\Plugin
foreach ($reports as &$apiReportToUpdate) {
if ($apiReportToUpdate['module'] == $reportWithGoals['module']
&& $apiReportToUpdate['action'] == $reportWithGoals['action']
) {
&& empty($apiReportToUpdate['parameters'])) {
$apiReportToUpdate['metricsGoal'] = $goalMetrics;
$apiReportToUpdate['processedMetricsGoal'] = $goalProcessedMetrics;
break;
@ -369,19 +170,30 @@ class Goals extends \Piwik\Plugin
}
}
static private function getAllReportsWithGoalMetrics()
private static function getAllReportsWithGoalMetrics()
{
$reportsWithGoals = array();
foreach (Report::getAllReports() as $report) {
if ($report->hasGoalMetrics()) {
$reportsWithGoals[] = array(
'category' => $report->getCategoryKey(),
'name' => $report->getName(),
'module' => $report->getModule(),
'action' => $report->getAction(),
);
}
}
/**
* Triggered when gathering all reports that contain Goal metrics. The list of reports
* will be displayed on the left column of the bottom of every _Goals_ page.
*
*
* If plugins define reports that contain goal metrics (such as **conversions** or **revenue**),
* they can use this event to make sure their reports can be viewed on Goals pages.
*
*
* **Example**
*
*
* public function getReportsWithGoalMetrics(&$reports)
* {
* $reports[] = array(
@ -391,34 +203,23 @@ class Goals extends \Piwik\Plugin
* 'action' => 'getMyReport'
* );
* }
*
*
* @param array &$reportsWithGoals The list of arrays describing reports that have Goal metrics.
* Each element of this array must be an array with the following
* properties:
*
*
* - **category**: The report category. This should be a translated string.
* - **name**: The report's translated name.
* - **module**: The plugin the report is in, eg, `'UserCountry'`.
* - **action**: The API method of the report, eg, `'getCountry'`.
* @ignore
* @deprecated since 2.5.0
*/
Piwik::postEvent('Goals.getReportsWithGoalMetrics', array(&$reportsWithGoals));
return $reportsWithGoals;
}
static public function getProductReportColumns()
{
return array(
'revenue' => Piwik::translate('General_ProductRevenue'),
'quantity' => Piwik::translate('General_Quantity'),
'orders' => Piwik::translate('General_UniquePurchases'),
'avg_price' => Piwik::translate('General_AveragePrice'),
'avg_quantity' => Piwik::translate('General_AverageQuantity'),
'nb_visits' => Piwik::translate('General_ColumnNbVisits'),
'conversion_rate' => Piwik::translate('General_ProductConversionRate'),
);
}
/**
* This function executes when the 'Goals.getReportsWithGoalMetrics' event fires. It
* adds the 'visits to conversion' report metadata to the list of goal reports so
@ -427,13 +228,13 @@ class Goals extends \Piwik\Plugin
public function getActualReportsWithGoalMetrics(&$dimensions)
{
$reportWithGoalMetrics = array(
array('category' => Piwik::translate('General_Visit'),
array('category' => 'General_Visit',
'name' => Piwik::translate('Goals_VisitsUntilConv'),
'module' => 'Goals',
'action' => 'getVisitsUntilConversion',
'viewDataTable' => 'table',
),
array('category' => Piwik::translate('General_Visit'),
array('category' => 'General_Visit',
'name' => Piwik::translate('Goals_DaysToConv'),
'module' => 'Goals',
'action' => 'getDaysToConversion',
@ -443,18 +244,6 @@ class Goals extends \Piwik\Plugin
$dimensions = array_merge($dimensions, $reportWithGoalMetrics);
}
public function getSegmentsMetadata(&$segments)
{
$segments[] = array(
'type' => 'dimension',
'category' => Piwik::translate('General_Visit'),
'name' => 'General_VisitConvertedGoalId',
'segment' => 'visitConvertedGoalId',
'sqlSegment' => 'log_conversion.idgoal',
'acceptedValues' => '1, 2, 3, etc.',
);
}
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/Goals/javascripts/goalsForm.js";
@ -471,210 +260,14 @@ class Goals extends \Piwik\Plugin
$array['goals'] = API::getInstance()->getGoals($idSite);
}
public function addWidgets()
{
$idSite = Common::getRequestVar('idSite', null, 'int');
// Ecommerce widgets
$site = new Site($idSite);
if ($site->isEcommerceEnabled()) {
WidgetsList::add('Goals_Ecommerce', 'Goals_EcommerceOverview', 'Goals', 'widgetGoalReport', array('idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER));
WidgetsList::add('Goals_Ecommerce', 'Goals_EcommerceLog', 'Goals', 'getEcommerceLog');
foreach ($this->ecommerceReports as $widget) {
WidgetsList::add('Goals_Ecommerce', $widget[0], $widget[1], $widget[2]);
}
}
// Goals widgets
WidgetsList::add('Goals_Goals', 'Goals_GoalsOverview', 'Goals', 'widgetGoalsOverview');
$goals = API::getInstance()->getGoals($idSite);
if (count($goals) > 0) {
foreach ($goals as $goal) {
WidgetsList::add('Goals_Goals', Common::sanitizeInputValue($goal['name']), 'Goals', 'widgetGoalReport', array('idGoal' => $goal['idgoal']));
}
}
}
function addMenus()
{
$idSite = Common::getRequestVar('idSite', null, 'int');
$goals = API::getInstance()->getGoals($idSite);
$mainGoalMenu = $this->getGoalCategoryName($idSite);
$site = new Site($idSite);
if (count($goals) == 0) {
MenuMain::getInstance()->add($mainGoalMenu, '', array(
'module' => 'Goals',
'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'addNewGoal'),
'idGoal' => ($site->isEcommerceEnabled() ? Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER : null)),
true,
25);
if ($site->isEcommerceEnabled()) {
MenuMain::getInstance()->add($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER), true, 1);
}
MenuMain::getInstance()->add($mainGoalMenu, 'Goals_AddNewGoal', array('module' => 'Goals', 'action' => 'addNewGoal'));
} else {
MenuMain::getInstance()->add($mainGoalMenu, '', array(
'module' => 'Goals',
'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'index'),
'idGoal' => ($site->isEcommerceEnabled() ? Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER : null)),
true,
25);
if ($site->isEcommerceEnabled()) {
MenuMain::getInstance()->add($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER), true, 1);
}
MenuMain::getInstance()->add($mainGoalMenu, 'Goals_GoalsOverview', array('module' => 'Goals', 'action' => 'index'), true, 2);
foreach ($goals as $goal) {
MenuMain::getInstance()->add($mainGoalMenu, str_replace('%', '%%', Translate::clean($goal['name'])), array('module' => 'Goals', 'action' => 'goalReport', 'idGoal' => $goal['idgoal']));
}
}
}
protected function getGoalCategoryName($idSite)
{
$site = new Site($idSite);
return $site->isEcommerceEnabled() ? 'Goals_EcommerceAndGoalsMenu' : 'Goals_Goals';
}
public function configureViewDataTable(ViewDataTable $view)
{
switch ($view->requestConfig->apiMethodToRequestDataTable) {
case 'Goals.getItemsSku':
$this->configureViewForGetItemsSku($view);
break;
case 'Goals.getItemsName':
$this->configureViewForGetItemsName($view);
break;
case 'Goals.getItemsCategory':
$this->configureViewForGetItemsCategory($view);
break;
case 'Goals.getVisitsUntilConversion':
$this->configureViewForGetVisitsUntilConversion($view);
break;
case 'Goals.getDaysToConversion':
$this->configureViewForGetDaysToConversion($view);
break;
}
}
private function configureViewForGetItemsSku(ViewDataTable $view)
{
return $this->configureViewForItemsReport($view, Piwik::translate('Goals_ProductSKU'));
}
private function configureViewForGetItemsName(ViewDataTable $view)
{
return $this->configureViewForItemsReport($view, Piwik::translate('Goals_ProductName'));
}
private function configureViewForGetItemsCategory(ViewDataTable $view)
{
return $this->configureViewForItemsReport($view, Piwik::translate('Goals_ProductCategory'));
}
private function configureViewForGetVisitsUntilConversion(ViewDataTable $view)
{
$view->config->show_search = false;
$view->config->show_exclude_low_population = false;
$view->config->show_table_all_columns = false;
$view->config->columns_to_display = array('label', 'nb_conversions');
$view->config->show_offset_information = false;
$view->config->show_pagination_control = false;
$view->config->show_all_views_icons = false;
$view->requestConfig->filter_sort_column = 'label';
$view->requestConfig->filter_sort_order = 'asc';
$view->requestConfig->filter_limit = count(Archiver::$visitCountRanges);
$view->config->addTranslations(array(
'label' => Piwik::translate('Goals_VisitsUntilConv'),
'nb_conversions' => Piwik::translate('Goals_ColumnConversions'),
));
}
private function configureViewForGetDaysToConversion(ViewDataTable $view)
{
$view->config->show_search = false;
$view->config->show_exclude_low_population = false;
$view->config->show_table_all_columns = false;
$view->config->show_all_views_icons = false;
$view->config->show_offset_information = false;
$view->config->show_pagination_control = false;
$view->config->columns_to_display = array('label', 'nb_conversions');
$view->requestConfig->filter_sort_column = 'label';
$view->requestConfig->filter_sort_order = 'asc';
$view->requestConfig->filter_limit = count(Archiver::$daysToConvRanges);
$view->config->addTranslations(array(
'label' => Piwik::translate('Goals_DaysToConv'),
'nb_conversions' => Piwik::translate('Goals_ColumnConversions'),
));
}
private function configureViewForItemsReport(ViewDataTable $view, $label)
{
$idSite = Common::getRequestVar('idSite');
$moneyColumns = array('revenue', 'avg_price');
$prettifyMoneyColumns = array(
'ColumnCallbackReplace', array($moneyColumns, '\Piwik\MetricsFormatter::getPrettyMoney', array($idSite)));
$view->config->show_ecommerce = true;
$view->config->show_table = false;
$view->config->show_all_views_icons = false;
$view->config->show_exclude_low_population = false;
$view->config->show_table_all_columns = false;
$view->config->addTranslation('label', $label);
$view->config->filters[] = $prettifyMoneyColumns;
$view->requestConfig->filter_limit = 10;
$view->requestConfig->filter_sort_column = 'revenue';
$view->requestConfig->filter_sort_order = 'desc';
// set columns/translations which differ based on viewDataTable TODO: shouldn't have to do this check... amount of reports should be dynamic, but metadata should be static
$columns = Goals::getProductReportColumns();
$abandonedCart = Common::getRequestVar('viewDataTable', 'ecommerceOrder', 'string') == 'ecommerceAbandonedCart';
if ($abandonedCart) {
$columns['abandoned_carts'] = Piwik::translate('General_AbandonedCarts');
$columns['revenue'] = Piwik::translate('Goals_LeftInCart', Piwik::translate('General_ProductRevenue'));
$columns['quantity'] = Piwik::translate('Goals_LeftInCart', Piwik::translate('General_Quantity'));
$columns['avg_quantity'] = Piwik::translate('Goals_LeftInCart', Piwik::translate('General_AverageQuantity'));
unset($columns['orders']);
unset($columns['conversion_rate']);
$view->requestConfig->request_parameters_to_modify['abandonedCarts'] = '1';
}
$translations = array_merge(array('label' => $label), $columns);
$view->config->addTranslations($translations);
$view->config->columns_to_display = array_keys($translations);
// set metrics documentation in normal ecommerce report
if (!$abandonedCart) {
$view->config->metrics_documentation = array(
'revenue' => Piwik::translate('Goals_ColumnRevenueDocumentation',
Piwik::translate('Goals_DocumentationRevenueGeneratedByProductSales')),
'quantity' => Piwik::translate('Goals_ColumnQuantityDocumentation', $label),
'orders' => Piwik::translate('Goals_ColumnOrdersDocumentation', $label),
'avg_price' => Piwik::translate('Goals_ColumnAveragePriceDocumentation', $label),
'avg_quantity' => Piwik::translate('Goals_ColumnAverageQuantityDocumentation', $label),
'nb_visits' => Piwik::translate('Goals_ColumnVisitsProductDocumentation', $label),
'conversion_rate' => Piwik::translate('Goals_ColumnConversionRateProductDocumentation', $label),
);
}
$view->config->custom_parameters['viewDataTable'] =
$abandonedCart ? Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART : Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER;
}
public function getClientSideTranslationKeys(&$translationKeys)
{
$translationKeys[] = 'Goals_AddGoal';
$translationKeys[] = 'Goals_UpdateGoal';
$translationKeys[] = 'Goals_DeleteGoalConfirm';
$translationKeys[] = 'Goals_UpdateGoal';
$translationKeys[] = 'Goals_DeleteGoalConfirm';
$translationKeys[] = 'Goals_Ecommerce';
$translationKeys[] = 'Goals_Optional';
}
}

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