fixed stretching for narrow crossword quests
This commit is contained in:
commit
c1a314f6e7
3452 changed files with 593206 additions and 0 deletions
567
www/analytics/plugins/Goals/API.php
Normal file
567
www/analytics/plugins/Goals/API.php
Normal file
|
|
@ -0,0 +1,567 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\Goals;
|
||||
|
||||
use Exception;
|
||||
use Piwik\Archive;
|
||||
use Piwik\Common;
|
||||
use Piwik\DataTable;
|
||||
use Piwik\Db;
|
||||
use Piwik\Metrics;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Site;
|
||||
use Piwik\Tracker\Cache;
|
||||
use Piwik\Tracker\GoalManager;
|
||||
|
||||
/**
|
||||
* Goals API lets you Manage existing goals, via "updateGoal" and "deleteGoal", create new Goals via "addGoal",
|
||||
* or list existing Goals for one or several websites via "getGoals"
|
||||
*
|
||||
* If you are <a href='http://piwik.org/docs/ecommerce-analytics/' target='_blank'>tracking Ecommerce orders and products</a> on your site, the functions "getItemsSku", "getItemsName" and "getItemsCategory"
|
||||
* will return the list of products purchased on your site, either grouped by Product SKU, Product Name or Product Category. For each name, SKU or category, the following
|
||||
* metrics are returned: Total revenue, Total quantity, average price, average quantity, number of orders (or abandoned carts) containing this product, number of visits on the Product page,
|
||||
* Conversion rate.
|
||||
*
|
||||
* By default, these functions return the 'Products purchased'. These functions also accept an optional parameter &abandonedCarts=1.
|
||||
* If the parameter is set, it will instead return the metrics for products that were left in an abandoned cart therefore not purchased.
|
||||
*
|
||||
* The API also lets you request overall Goal metrics via the method "get": Conversions, Visits with at least one conversion, Conversion rate and Revenue.
|
||||
* 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.
|
||||
*
|
||||
* @method static \Piwik\Plugins\Goals\API getInstance()
|
||||
*/
|
||||
class API extends \Piwik\Plugin\API
|
||||
{
|
||||
const AVG_PRICE_VIEWED = 'avg_price_viewed';
|
||||
|
||||
/**
|
||||
* Returns all Goals for a given website, or list of websites
|
||||
*
|
||||
* @param string|array $idSite Array or Comma separated list of website IDs to request the goals for
|
||||
* @return array Array of Goal attributes
|
||||
*/
|
||||
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']);
|
||||
}
|
||||
$cleanedGoals[$goal['idgoal']] = $goal;
|
||||
}
|
||||
return $cleanedGoals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Goal for a given website.
|
||||
*
|
||||
* @param int $idSite
|
||||
* @param string $name
|
||||
* @param string $matchAttribute 'url', 'title', 'file', 'external_website' or 'manually'
|
||||
* @param string $pattern eg. purchase-confirmation.htm
|
||||
* @param string $patternType 'regex', 'contains', 'exact'
|
||||
* @param bool $caseSensitive
|
||||
* @param bool|float $revenue If set, default revenue to assign to conversions
|
||||
* @param bool $allowMultipleConversionsPerVisit By default, multiple conversions in the same visit will only record the first conversion.
|
||||
* If set to true, multiple conversions will all be recorded within a visit (useful for Ecommerce goals)
|
||||
* @return int ID of the new goal
|
||||
*/
|
||||
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);
|
||||
$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,
|
||||
));
|
||||
Cache::regenerateCacheWebsiteAttributes($idSite);
|
||||
return $idGoal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a Goal description.
|
||||
* Will not update or re-process the conversions already recorded
|
||||
*
|
||||
* @see addGoal() for parameters description
|
||||
* @param int $idSite
|
||||
* @param int $idGoal
|
||||
* @param $name
|
||||
* @param $matchAttribute
|
||||
* @param string $pattern
|
||||
* @param string $patternType
|
||||
* @param bool $caseSensitive
|
||||
* @param bool|float $revenue
|
||||
* @param bool $allowMultipleConversionsPerVisit
|
||||
* @return void
|
||||
*/
|
||||
public function updateGoal($idSite, $idGoal, $name, $matchAttribute, $pattern, $patternType, $caseSensitive = false, $revenue = false, $allowMultipleConversionsPerVisit = false)
|
||||
{
|
||||
Piwik::checkUserHasAdminAccess($idSite);
|
||||
$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'"
|
||||
);
|
||||
Cache::regenerateCacheWebsiteAttributes($idSite);
|
||||
}
|
||||
|
||||
private function checkPatternIsValid($patternType, $pattern)
|
||||
{
|
||||
if ($patternType == 'exact'
|
||||
&& substr($pattern, 0, 4) != 'http'
|
||||
) {
|
||||
throw new Exception(Piwik::translate('Goals_ExceptionInvalidMatchingString', array("http:// or https://", "http://www.yourwebsite.com/newsletter/subscribed.html")));
|
||||
}
|
||||
}
|
||||
|
||||
private function checkName($name)
|
||||
{
|
||||
return urldecode($name);
|
||||
}
|
||||
|
||||
private function checkPattern($pattern)
|
||||
{
|
||||
return urldecode($pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Soft deletes a given Goal.
|
||||
* Stats data in the archives will still be recorded, but not displayed.
|
||||
*
|
||||
* @param int $idSite
|
||||
* @param int $idGoal
|
||||
* @return void
|
||||
*/
|
||||
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));
|
||||
Cache::regenerateCacheWebsiteAttributes($idSite);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a datatable of Items SKU/name or categories and their metrics
|
||||
* If $abandonedCarts set to 1, will return items abandoned in carts. If set to 0, will return items ordered
|
||||
*/
|
||||
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));
|
||||
|
||||
$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->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;
|
||||
}
|
||||
|
||||
protected function renameNotDefinedRow($dataTable, $notDefinedStringPretty)
|
||||
{
|
||||
if ($dataTable instanceof DataTable\Map) {
|
||||
foreach ($dataTable->getDataTables() as $table) {
|
||||
$this->renameNotDefinedRow($table, $notDefinedStringPretty);
|
||||
}
|
||||
return;
|
||||
}
|
||||
$rowNotDefined = $dataTable->getRowFromLabel(\Piwik\Plugins\CustomVariables\Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED);
|
||||
if ($rowNotDefined) {
|
||||
$rowNotDefined->setColumn('label', $notDefinedStringPretty);
|
||||
}
|
||||
}
|
||||
|
||||
protected function enrichItemsDataTableWithItemsViewMetrics($dataTable, $idSite, $period, $date, $segment, $idSubtable)
|
||||
{
|
||||
$ecommerceViews = \Piwik\Plugins\CustomVariables\API::getInstance()->getCustomVariablesValuesFromNameId($idSite, $period, $date, $idSubtable, $segment, $_leavePriceViewedColumn = true);
|
||||
|
||||
// For Product names and SKU reports, and for Category report
|
||||
// Use the Price (tracked on page views)
|
||||
// ONLY when the price sold in conversions is not found (ie. product viewed but not sold)
|
||||
foreach ($ecommerceViews->getRows() as $rowView) {
|
||||
// If there is not already a 'sum price' for this product
|
||||
$rowFound = $dataTable->getRowFromLabel($rowView->getColumn('label'));
|
||||
$price = $rowFound
|
||||
? $rowFound->getColumn(Metrics::INDEX_ECOMMERCE_ITEM_PRICE)
|
||||
: false;
|
||||
if (empty($price)) {
|
||||
// If a price was tracked on the product page
|
||||
if ($rowView->getColumn(Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED)) {
|
||||
$rowView->renameColumn(Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED, self::AVG_PRICE_VIEWED);
|
||||
}
|
||||
}
|
||||
$rowView->deleteColumn(Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED);
|
||||
}
|
||||
|
||||
$dataTable->addDataTable($ecommerceViews);
|
||||
}
|
||||
|
||||
public function getItemsSku($idSite, $period, $date, $abandonedCarts = false, $segment = false)
|
||||
{
|
||||
return $this->getItems('Goals_ItemsSku', $idSite, $period, $date, $abandonedCarts, $segment);
|
||||
}
|
||||
|
||||
public function getItemsName($idSite, $period, $date, $abandonedCarts = false, $segment = false)
|
||||
{
|
||||
return $this->getItems('Goals_ItemsName', $idSite, $period, $date, $abandonedCarts, $segment);
|
||||
}
|
||||
|
||||
public function getItemsCategory($idSite, $period, $date, $abandonedCarts = false, $segment = false)
|
||||
{
|
||||
return $this->getItems('Goals_ItemsCategory', $idSite, $period, $date, $abandonedCarts, $segment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function that checks for special string goal IDs and converts them to
|
||||
* their integer equivalents.
|
||||
*
|
||||
* Checks for the following values:
|
||||
* Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER
|
||||
* Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART
|
||||
*
|
||||
* @param string|int $idGoal The goal id as an integer or a special string.
|
||||
* @return int The numeric goal id.
|
||||
*/
|
||||
protected static function convertSpecialGoalIds($idGoal)
|
||||
{
|
||||
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
|
||||
return GoalManager::IDGOAL_ORDER;
|
||||
} else if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
|
||||
return GoalManager::IDGOAL_CART;
|
||||
} else {
|
||||
return $idGoal;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Goals data
|
||||
*
|
||||
* @param int $idSite
|
||||
* @param string $period
|
||||
* @param string $date
|
||||
* @param bool $segment
|
||||
* @param bool|int $idGoal
|
||||
* @param array $columns Array of metrics to fetch: nb_conversions, conversion_rate, revenue
|
||||
* @return DataTable
|
||||
*/
|
||||
public function get($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);
|
||||
|
||||
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);
|
||||
|
||||
// Rewrite column names as we expect them
|
||||
foreach ($columnsToSelect as $id => $oldName) {
|
||||
$dataTable->renameColumn($oldName, $columns[$id]);
|
||||
}
|
||||
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
|
||||
if ($dataTable instanceof DataTable\Map) {
|
||||
foreach ($dataTable->getDataTables() as $row) {
|
||||
$this->enrichTable($row);
|
||||
}
|
||||
} else {
|
||||
$this->enrichTable($dataTable);
|
||||
}
|
||||
}
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
protected function enrichTable($table)
|
||||
{
|
||||
$row = $table->getFirstRow();
|
||||
if (!$row) {
|
||||
return;
|
||||
}
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getNumeric($idSite, $period, $date, $segment, $toFetch)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
$archive = Archive::build($idSite, $period, $date, $segment);
|
||||
$dataTable = $archive->getDataTableFromNumeric($toFetch);
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function getConversions($idSite, $period, $date, $segment = false, $idGoal = false)
|
||||
{
|
||||
return $this->getNumeric($idSite, $period, $date, $segment, Archiver::getRecordName('nb_conversions', $idGoal));
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function getNbVisitsConverted($idSite, $period, $date, $segment = false, $idGoal = false)
|
||||
{
|
||||
return $this->getNumeric($idSite, $period, $date, $segment, Archiver::getRecordName('nb_visits_converted', $idGoal));
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function getConversionRate($idSite, $period, $date, $segment = false, $idGoal = false)
|
||||
{
|
||||
return $this->getNumeric($idSite, $period, $date, $segment, Archiver::getRecordName('conversion_rate', $idGoal));
|
||||
}
|
||||
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
public function getRevenue($idSite, $period, $date, $segment = false, $idGoal = false)
|
||||
{
|
||||
return $this->getNumeric($idSite, $period, $date, $segment, Archiver::getRecordName('revenue', $idGoal));
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that retrieve an archived DataTable for a specific site, date range,
|
||||
* segment and goal. If not goal is specified, this method will retrieve and sum the
|
||||
* data for every goal.
|
||||
*
|
||||
* @param string $recordName The archive entry name.
|
||||
* @param int|string $idSite The site(s) to select data for.
|
||||
* @param string $period The period type.
|
||||
* @param string $date The date type.
|
||||
* @param string $segment The segment.
|
||||
* @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
|
||||
* data for every goal that belongs to $idSite is returned.
|
||||
* @return false|DataTable
|
||||
*/
|
||||
protected function getGoalSpecificDataTable($recordName, $idSite, $period, $date, $segment, $idGoal)
|
||||
{
|
||||
Piwik::checkUserHasViewAccess($idSite);
|
||||
|
||||
$archive = Archive::build($idSite, $period, $date, $segment);
|
||||
|
||||
// check for the special goal ids
|
||||
$realGoalId = $idGoal != true ? false : self::convertSpecialGoalIds($idGoal);
|
||||
|
||||
// get the data table
|
||||
$dataTable = $archive->getDataTable(Archiver::getRecordName($recordName, $realGoalId), $idSubtable = null);
|
||||
$dataTable->queueFilter('ReplaceColumnNames');
|
||||
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a DataTable that maps ranges of days to the number of conversions that occurred
|
||||
* within those ranges, for the specified site, date range, segment and goal.
|
||||
*
|
||||
* @param int $idSite The site to select data from.
|
||||
* @param string $period The period type.
|
||||
* @param string $date The date type.
|
||||
* @param string|bool $segment The segment.
|
||||
* @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
|
||||
* data for every goal that belongs to $idSite is returned.
|
||||
* @return false|DataTable
|
||||
*/
|
||||
public function getDaysToConversion($idSite, $period, $date, $segment = false, $idGoal = false)
|
||||
{
|
||||
$dataTable = $this->getGoalSpecificDataTable(
|
||||
Archiver::DAYS_UNTIL_CONV_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
|
||||
|
||||
$dataTable->queueFilter('Sort', array('label', 'asc', true));
|
||||
$dataTable->queueFilter(
|
||||
'BeautifyRangeLabels', array(Piwik::translate('General_OneDay'), Piwik::translate('General_NDays')));
|
||||
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a DataTable that maps ranges of visit counts to the number of conversions that
|
||||
* occurred on those visits for the specified site, date range, segment and goal.
|
||||
*
|
||||
* @param int $idSite The site to select data from.
|
||||
* @param string $period The period type.
|
||||
* @param string $date The date type.
|
||||
* @param string|bool $segment The segment.
|
||||
* @param int|bool $idGoal The id of the goal to get data for. If this is set to false,
|
||||
* data for every goal that belongs to $idSite is returned.
|
||||
* @return bool|DataTable
|
||||
*/
|
||||
public function getVisitsUntilConversion($idSite, $period, $date, $segment = false, $idGoal = false)
|
||||
{
|
||||
$dataTable = $this->getGoalSpecificDataTable(
|
||||
Archiver::VISITS_UNTIL_RECORD_NAME, $idSite, $period, $date, $segment, $idGoal);
|
||||
|
||||
$dataTable->queueFilter('Sort', array('label', 'asc', true));
|
||||
$dataTable->queueFilter(
|
||||
'BeautifyRangeLabels', array(Piwik::translate('General_OneVisit'), Piwik::translate('General_NVisits')));
|
||||
|
||||
return $dataTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhances the dataTable with Items attributes found in the Custom Variables report.
|
||||
*
|
||||
* @param $dataTable
|
||||
* @param $recordName
|
||||
* @param $idSite
|
||||
* @param $period
|
||||
* @param $date
|
||||
* @param $segment
|
||||
*/
|
||||
protected function enrichItemsTableWithViewMetrics($dataTable, $recordName, $idSite, $period, $date, $segment)
|
||||
{
|
||||
// Enrich the datatable with Product/Categories views, and conversion rates
|
||||
$customVariables = \Piwik\Plugins\CustomVariables\API::getInstance()->getCustomVariables($idSite, $period, $date, $segment, $expanded = false,
|
||||
$_leavePiwikCoreVariables = true);
|
||||
$mapping = array(
|
||||
'Goals_ItemsSku' => '_pks',
|
||||
'Goals_ItemsName' => '_pkn',
|
||||
'Goals_ItemsCategory' => '_pkc',
|
||||
);
|
||||
$reportToNotDefinedString = array(
|
||||
'Goals_ItemsSku' => Piwik::translate('General_NotDefined', Piwik::translate('Goals_ProductSKU')), // Note: this should never happen
|
||||
'Goals_ItemsName' => Piwik::translate('General_NotDefined', Piwik::translate('Goals_ProductName')),
|
||||
'Goals_ItemsCategory' => Piwik::translate('General_NotDefined', Piwik::translate('Goals_ProductCategory'))
|
||||
);
|
||||
$notDefinedStringPretty = $reportToNotDefinedString[$recordName];
|
||||
$customVarNameToLookFor = $mapping[$recordName];
|
||||
|
||||
// Handle case where date=last30&period=day
|
||||
if ($customVariables instanceof DataTable\Map) {
|
||||
$customVariableDatatables = $customVariables->getDataTables();
|
||||
$dataTables = $dataTable->getDataTables();
|
||||
foreach ($customVariableDatatables as $key => $customVariableTableForDate) {
|
||||
$dataTableForDate = isset($dataTables[$key]) ? $dataTables[$key] : new DataTable();
|
||||
|
||||
// we do not enter the IF
|
||||
// if case idSite=1,3 AND period=day&date=datefrom,dateto,
|
||||
if ($customVariableTableForDate instanceof DataTable
|
||||
&& $customVariableTableForDate->getMetadata(Archive\DataTableFactory::TABLE_METADATA_PERIOD_INDEX)
|
||||
) {
|
||||
$dateRewrite = $customVariableTableForDate->getMetadata(Archive\DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getDateStart()->toString();
|
||||
$row = $customVariableTableForDate->getRowFromLabel($customVarNameToLookFor);
|
||||
if ($row) {
|
||||
$idSubtable = $row->getIdSubDataTable();
|
||||
$this->enrichItemsDataTableWithItemsViewMetrics($dataTableForDate, $idSite, $period, $dateRewrite, $segment, $idSubtable);
|
||||
}
|
||||
$dataTable->addTable($dataTableForDate, $key);
|
||||
}
|
||||
$this->renameNotDefinedRow($dataTableForDate, $notDefinedStringPretty);
|
||||
}
|
||||
} elseif ($customVariables instanceof DataTable) {
|
||||
$row = $customVariables->getRowFromLabel($customVarNameToLookFor);
|
||||
if ($row) {
|
||||
$idSubtable = $row->getIdSubDataTable();
|
||||
$this->enrichItemsDataTableWithItemsViewMetrics($dataTable, $idSite, $period, $date, $segment, $idSubtable);
|
||||
}
|
||||
$this->renameNotDefinedRow($dataTable, $notDefinedStringPretty);
|
||||
}
|
||||
}
|
||||
}
|
||||
418
www/analytics/plugins/Goals/Archiver.php
Normal file
418
www/analytics/plugins/Goals/Archiver.php
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Piwik\Plugins\Goals;
|
||||
|
||||
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
|
||||
{
|
||||
const VISITS_UNTIL_RECORD_NAME = 'visits_until_conv';
|
||||
const DAYS_UNTIL_CONV_RECORD_NAME = 'days_until_conv';
|
||||
const ITEMS_SKU_RECORD_NAME = 'Goals_ItemsSku';
|
||||
const ITEMS_NAME_RECORD_NAME = 'Goals_ItemsName';
|
||||
const ITEMS_CATEGORY_RECORD_NAME = 'Goals_ItemsCategory';
|
||||
const SKU_FIELD = 'idaction_sku';
|
||||
const NAME_FIELD = 'idaction_name';
|
||||
const CATEGORY_FIELD = 'idaction_category';
|
||||
const CATEGORY2_FIELD = 'idaction_category2';
|
||||
const CATEGORY3_FIELD = 'idaction_category3';
|
||||
const CATEGORY4_FIELD = 'idaction_category4';
|
||||
const CATEGORY5_FIELD = 'idaction_category5';
|
||||
const NO_LABEL = ':';
|
||||
const LOG_CONVERSION_TABLE = 'log_conversion';
|
||||
const VISITS_COUNT_FIELD = 'visitor_count_visits';
|
||||
const DAYS_SINCE_FIRST_VISIT_FIELD = 'visitor_days_since_first';
|
||||
/**
|
||||
* This array stores the ranges to use when displaying the 'visits to conversion' report
|
||||
*/
|
||||
public static $visitCountRanges = array(
|
||||
array(1, 1),
|
||||
array(2, 2),
|
||||
array(3, 3),
|
||||
array(4, 4),
|
||||
array(5, 5),
|
||||
array(6, 6),
|
||||
array(7, 7),
|
||||
array(8, 8),
|
||||
array(9, 14),
|
||||
array(15, 25),
|
||||
array(26, 50),
|
||||
array(51, 100),
|
||||
array(100)
|
||||
);
|
||||
/**
|
||||
* This array stores the ranges to use when displaying the 'days to conversion' report
|
||||
*/
|
||||
public static $daysToConvRanges = array(
|
||||
array(0, 0),
|
||||
array(1, 1),
|
||||
array(2, 2),
|
||||
array(3, 3),
|
||||
array(4, 4),
|
||||
array(5, 5),
|
||||
array(6, 6),
|
||||
array(7, 7),
|
||||
array(8, 14),
|
||||
array(15, 30),
|
||||
array(31, 60),
|
||||
array(61, 120),
|
||||
array(121, 364),
|
||||
array(364)
|
||||
);
|
||||
protected $dimensionRecord = array(
|
||||
self::SKU_FIELD => self::ITEMS_SKU_RECORD_NAME,
|
||||
self::NAME_FIELD => self::ITEMS_NAME_RECORD_NAME,
|
||||
self::CATEGORY_FIELD => self::ITEMS_CATEGORY_RECORD_NAME
|
||||
);
|
||||
|
||||
/**
|
||||
* Array containing one DataArray for each Ecommerce items dimension (name/sku/category abandoned carts and orders)
|
||||
* @var array
|
||||
*/
|
||||
protected $itemReports = array();
|
||||
|
||||
public function aggregateDayReport()
|
||||
{
|
||||
$this->aggregateGeneralGoalMetrics();
|
||||
$this->aggregateEcommerceItems();
|
||||
}
|
||||
|
||||
protected function aggregateGeneralGoalMetrics()
|
||||
{
|
||||
$prefixes = array(
|
||||
self::VISITS_UNTIL_RECORD_NAME => 'vcv',
|
||||
self::DAYS_UNTIL_CONV_RECORD_NAME => 'vdsf',
|
||||
);
|
||||
|
||||
$selects = array();
|
||||
$selects = array_merge($selects, LogAggregator::getSelectsFromRangedColumn(
|
||||
self::VISITS_COUNT_FIELD, self::$visitCountRanges, self::LOG_CONVERSION_TABLE, $prefixes[self::VISITS_UNTIL_RECORD_NAME]
|
||||
));
|
||||
$selects = array_merge($selects, LogAggregator::getSelectsFromRangedColumn(
|
||||
self::DAYS_SINCE_FIRST_VISIT_FIELD, self::$daysToConvRanges, self::LOG_CONVERSION_TABLE, $prefixes[self::DAYS_UNTIL_CONV_RECORD_NAME]
|
||||
));
|
||||
|
||||
$query = $this->getLogAggregator()->queryConversionsByDimension(array(), false, $selects);
|
||||
if ($query === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$totalConversions = $totalRevenue = 0;
|
||||
$goals = new DataArray();
|
||||
$visitsToConversions = $daysToConversions = array();
|
||||
|
||||
$conversionMetrics = $this->getLogAggregator()->getConversionsMetricFields();
|
||||
while ($row = $query->fetch()) {
|
||||
$idGoal = $row['idgoal'];
|
||||
unset($row['idgoal']);
|
||||
unset($row['label']);
|
||||
|
||||
$values = array();
|
||||
foreach ($conversionMetrics as $field => $statement) {
|
||||
$values[$field] = $row[$field];
|
||||
}
|
||||
$goals->sumMetrics($idGoal, $values);
|
||||
|
||||
if (empty($visitsToConversions[$idGoal])) {
|
||||
$visitsToConversions[$idGoal] = new DataTable();
|
||||
}
|
||||
$array = LogAggregator::makeArrayOneColumn($row, Metrics::INDEX_NB_CONVERSIONS, $prefixes[self::VISITS_UNTIL_RECORD_NAME]);
|
||||
$visitsToConversions[$idGoal]->addDataTable(DataTable::makeFromIndexedArray($array));
|
||||
|
||||
if (empty($daysToConversions[$idGoal])) {
|
||||
$daysToConversions[$idGoal] = new DataTable();
|
||||
}
|
||||
$array = LogAggregator::makeArrayOneColumn($row, Metrics::INDEX_NB_CONVERSIONS, $prefixes[self::DAYS_UNTIL_CONV_RECORD_NAME]);
|
||||
$daysToConversions[$idGoal]->addDataTable(DataTable::makeFromIndexedArray($array));
|
||||
|
||||
// We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits
|
||||
// since it is a "negative conversion"
|
||||
if ($idGoal != GoalManager::IDGOAL_CART) {
|
||||
$totalConversions += $row[Metrics::INDEX_GOAL_NB_CONVERSIONS];
|
||||
$totalRevenue += $row[Metrics::INDEX_GOAL_REVENUE];
|
||||
}
|
||||
}
|
||||
|
||||
// Stats by goal, for all visitors
|
||||
$numericRecords = $this->getConversionsNumericMetrics($goals);
|
||||
$this->getProcessor()->insertNumericRecords($numericRecords);
|
||||
|
||||
$this->insertReports(self::VISITS_UNTIL_RECORD_NAME, $visitsToConversions);
|
||||
$this->insertReports(self::DAYS_UNTIL_CONV_RECORD_NAME, $daysToConversions);
|
||||
|
||||
// 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,
|
||||
);
|
||||
$this->getProcessor()->insertNumericRecords($metrics);
|
||||
}
|
||||
|
||||
protected function getConversionsNumericMetrics(DataArray $goals)
|
||||
{
|
||||
$numericRecords = array();
|
||||
$goals = $goals->getDataArray();
|
||||
foreach ($goals as $idGoal => $array) {
|
||||
foreach ($array as $metricId => $value) {
|
||||
$metricName = Metrics::$mappingFromIdToNameGoal[$metricId];
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $recordName 'nb_conversions'
|
||||
* @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)
|
||||
{
|
||||
$idGoalStr = '';
|
||||
if ($idGoal !== false) {
|
||||
$idGoalStr = $idGoal . "_";
|
||||
}
|
||||
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) {
|
||||
$record = self::getRecordName($recordName, $idGoal);
|
||||
$this->getProcessor()->insertBlobRecord($record, $table->getSerialized());
|
||||
}
|
||||
$overviewTable = $this->getOverviewFromGoalTables($visitsToConversions);
|
||||
$this->getProcessor()->insertBlobRecord(self::getRecordName($recordName), $overviewTable->getSerialized());
|
||||
}
|
||||
|
||||
protected function getOverviewFromGoalTables($tableByGoal)
|
||||
{
|
||||
$overview = new DataTable();
|
||||
foreach ($tableByGoal as $idGoal => $table) {
|
||||
if ($this->isStandardGoal($idGoal)) {
|
||||
$overview->addDataTable($table);
|
||||
}
|
||||
}
|
||||
return $overview;
|
||||
}
|
||||
|
||||
protected function isStandardGoal($idGoal)
|
||||
{
|
||||
return !in_array($idGoal, $this->getEcommerceIdGoals());
|
||||
}
|
||||
|
||||
protected function aggregateEcommerceItems()
|
||||
{
|
||||
$this->initItemReports();
|
||||
foreach ($this->getItemsDimensions() as $dimension) {
|
||||
$query = $this->getLogAggregator()->queryEcommerceItems($dimension);
|
||||
if ($query == false) {
|
||||
continue;
|
||||
}
|
||||
$this->aggregateFromEcommerceItems($query, $dimension);
|
||||
}
|
||||
$this->insertItemReports();
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function initItemReports()
|
||||
{
|
||||
foreach ($this->getEcommerceIdGoals() as $ecommerceType) {
|
||||
foreach ($this->dimensionRecord as $dimension => $record) {
|
||||
$this->itemReports[$dimension][$ecommerceType] = new DataArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function insertItemReports()
|
||||
{
|
||||
/** @var DataArray $array */
|
||||
foreach ($this->itemReports as $dimension => $itemAggregatesByType) {
|
||||
foreach ($itemAggregatesByType as $ecommerceType => $itemAggregate) {
|
||||
$recordName = $this->dimensionRecord[$dimension];
|
||||
if ($ecommerceType == GoalManager::IDGOAL_CART) {
|
||||
$recordName = self::getItemRecordNameAbandonedCart($recordName);
|
||||
}
|
||||
$table = $itemAggregate->asDataTable();
|
||||
$this->getProcessor()->insertBlobRecord($recordName, $table->getSerialized());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getItemsDimensions()
|
||||
{
|
||||
$dimensions = array_keys($this->dimensionRecord);
|
||||
foreach ($this->getItemExtraCategories() as $category) {
|
||||
$dimensions[] = $category;
|
||||
}
|
||||
return $dimensions;
|
||||
}
|
||||
|
||||
protected function getItemExtraCategories()
|
||||
{
|
||||
return array(self::CATEGORY2_FIELD, self::CATEGORY3_FIELD, self::CATEGORY4_FIELD, self::CATEGORY5_FIELD);
|
||||
}
|
||||
|
||||
protected function isItemExtraCategory($field)
|
||||
{
|
||||
return in_array($field, $this->getItemExtraCategories());
|
||||
}
|
||||
|
||||
protected function aggregateFromEcommerceItems($query, $dimension)
|
||||
{
|
||||
while ($row = $query->fetch()) {
|
||||
$ecommerceType = $row['ecommerceType'];
|
||||
|
||||
$label = $this->cleanupRowGetLabel($row, $dimension);
|
||||
if ($label === false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Aggregate extra categories in the Item categories array
|
||||
if ($this->isItemExtraCategory($dimension)) {
|
||||
$array = $this->itemReports[self::CATEGORY_FIELD][$ecommerceType];
|
||||
} else {
|
||||
$array = $this->itemReports[$dimension][$ecommerceType];
|
||||
}
|
||||
|
||||
$this->roundColumnValues($row);
|
||||
$array->sumMetrics($label, $row);
|
||||
}
|
||||
}
|
||||
|
||||
protected function cleanupRowGetLabel(&$row, $currentField)
|
||||
{
|
||||
$label = $row['label'];
|
||||
if (empty($label)) {
|
||||
// An empty additional category -> skip this iteration
|
||||
if ($this->isItemExtraCategory($currentField)) {
|
||||
return false;
|
||||
}
|
||||
$label = "Value not defined";
|
||||
// Product Name/Category not defined"
|
||||
if (\Piwik\Plugin\Manager::getInstance()->isPluginActivated('CustomVariables')) {
|
||||
$label = \Piwik\Plugins\CustomVariables\Archiver::LABEL_CUSTOM_VALUE_NOT_DEFINED;
|
||||
}
|
||||
}
|
||||
|
||||
if ($row['ecommerceType'] == GoalManager::IDGOAL_CART) {
|
||||
// abandoned carts are the numner of visits with an abandoned cart
|
||||
$row[Metrics::INDEX_ECOMMERCE_ORDERS] = $row[Metrics::INDEX_NB_VISITS];
|
||||
}
|
||||
|
||||
unset($row[Metrics::INDEX_NB_VISITS]);
|
||||
unset($row['label']);
|
||||
unset($row['labelIdAction']);
|
||||
unset($row['ecommerceType']);
|
||||
|
||||
return $label;
|
||||
}
|
||||
|
||||
protected function roundColumnValues(&$row)
|
||||
{
|
||||
$columnsToRound = array(
|
||||
Metrics::INDEX_ECOMMERCE_ITEM_REVENUE,
|
||||
Metrics::INDEX_ECOMMERCE_ITEM_QUANTITY,
|
||||
Metrics::INDEX_ECOMMERCE_ITEM_PRICE,
|
||||
Metrics::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED,
|
||||
);
|
||||
foreach ($columnsToRound as $column) {
|
||||
if (isset($row[$column])
|
||||
&& $row[$column] == round($row[$column])
|
||||
) {
|
||||
$row[$column] = round($row[$column]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getEcommerceIdGoals()
|
||||
{
|
||||
return array(GoalManager::IDGOAL_CART, GoalManager::IDGOAL_ORDER);
|
||||
}
|
||||
|
||||
static public function getItemRecordNameAbandonedCart($recordName)
|
||||
{
|
||||
return $recordName . '_Cart';
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal param $this->getProcessor()
|
||||
*/
|
||||
public function aggregateMultipleReports()
|
||||
{
|
||||
/*
|
||||
* Archive Ecommerce Items
|
||||
*/
|
||||
$dataTableToSum = $this->dimensionRecord;
|
||||
foreach ($this->dimensionRecord as $recordName) {
|
||||
$dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
|
||||
}
|
||||
$this->getProcessor()->aggregateDataTableRecords($dataTableToSum);
|
||||
|
||||
/*
|
||||
* Archive General Goal metrics
|
||||
*/
|
||||
$goalIdsToSum = GoalManager::getGoalIds($this->getProcessor()->getParams()->getSite()->getId());
|
||||
|
||||
//Ecommerce
|
||||
$goalIdsToSum[] = GoalManager::IDGOAL_ORDER;
|
||||
$goalIdsToSum[] = GoalManager::IDGOAL_CART; //bug here if idgoal=1
|
||||
// Overall goal metrics
|
||||
$goalIdsToSum[] = false;
|
||||
|
||||
$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);
|
||||
|
||||
// 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)));
|
||||
}
|
||||
|
||||
// sum up goal overview reports
|
||||
$this->getProcessor()->aggregateDataTableRecords(array(
|
||||
self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
|
||||
self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
|
||||
}
|
||||
}
|
||||
494
www/analytics/plugins/Goals/Controller.php
Normal file
494
www/analytics/plugins/Goals/Controller.php
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
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\Piwik;
|
||||
use Piwik\Plugins\Referrers\API as APIReferrers;
|
||||
use Piwik\View\ReportsByDimension;
|
||||
use Piwik\View;
|
||||
use Piwik\ViewDataTable\Factory;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Controller extends \Piwik\Plugin\Controller
|
||||
{
|
||||
const CONVERSION_RATE_PRECISION = 1;
|
||||
|
||||
/**
|
||||
* Number of "Your top converting keywords/etc are" to display in the per Goal overview page
|
||||
* @var int
|
||||
*/
|
||||
const COUNT_TOP_ROWS_TO_DISPLAY = 3;
|
||||
|
||||
const ECOMMERCE_LOG_SHOW_ORDERS = 1;
|
||||
const ECOMMERCE_LOG_SHOW_ABANDONED_CARTS = 2;
|
||||
|
||||
protected $goalColumnNameToLabel = array(
|
||||
'avg_order_revenue' => 'General_AverageOrderValue',
|
||||
'nb_conversions' => 'Goals_ColumnConversions',
|
||||
'conversion_rate' => 'General_ColumnConversionRate',
|
||||
'revenue' => 'General_TotalRevenue',
|
||||
'items' => 'General_PurchasedProducts',
|
||||
);
|
||||
|
||||
private function formatConversionRate($conversionRate)
|
||||
{
|
||||
if ($conversionRate instanceof DataTable) {
|
||||
if ($conversionRate->getRowsCount() == 0) {
|
||||
$conversionRate = 0;
|
||||
} else {
|
||||
$columns = $conversionRate->getFirstRow()->getColumns();
|
||||
$conversionRate = (float)reset($columns);
|
||||
}
|
||||
}
|
||||
return sprintf('%.' . self::CONVERSION_RATE_PRECISION . 'f%%', $conversionRate);
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$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()
|
||||
{
|
||||
$view = $this->getGoalReportView($idGoal = Common::getRequestVar('idGoal', null, 'string'));
|
||||
$view->displayFullReport = false;
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function goalReport()
|
||||
{
|
||||
$view = $this->getGoalReportView($idGoal = Common::getRequestVar('idGoal', null, 'string'));
|
||||
$view->displayFullReport = true;
|
||||
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['allow_multiple'] = true;
|
||||
$ecommerce = $view->ecommerce = true;
|
||||
} else {
|
||||
if (!isset($this->goals[$idGoal])) {
|
||||
Piwik::redirectToModule('Goals', 'index', array('idGoal' => null));
|
||||
}
|
||||
$goalDefinition = $this->goals[$idGoal];
|
||||
}
|
||||
$this->setGeneralVariablesView($view);
|
||||
$goal = $this->getMetricsForGoal($idGoal);
|
||||
foreach ($goal as $name => $value) {
|
||||
$view->$name = $value;
|
||||
}
|
||||
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
|
||||
$goal = $this->getMetricsForGoal(Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART);
|
||||
foreach ($goal as $name => $value) {
|
||||
$name = 'cart_' . $name;
|
||||
$view->$name = $value;
|
||||
}
|
||||
}
|
||||
$view->idGoal = $idGoal;
|
||||
$view->goalName = $goalDefinition['name'];
|
||||
$view->goalAllowMultipleConversionsPerVisit = $goalDefinition['allow_multiple'];
|
||||
$view->graphEvolution = $this->getEvolutionGraph(array('nb_conversions'), $idGoal);
|
||||
$view->nameGraphEvolution = 'Goals.getEvolutionGraph' . $idGoal;
|
||||
$view->topDimensions = $this->getTopDimensions($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->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
|
||||
$view->nb_conversions, isset($ecommerce), !empty($view->cart_nb_conversions));
|
||||
return $view;
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$view = $this->getOverviewView();
|
||||
|
||||
// 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->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
|
||||
$view->ecommerceEnabled = $this->site->isEcommerceEnabled();
|
||||
$view->displayFullReport = true;
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function widgetGoalsOverview()
|
||||
{
|
||||
$view = $this->getOverviewView();
|
||||
$view->displayFullReport = false;
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
protected function getOverviewView()
|
||||
{
|
||||
$view = new View('@Goals/getOverviewView');
|
||||
$this->setGeneralVariablesView($view);
|
||||
|
||||
$view->graphEvolution = $this->getEvolutionGraph(array('nb_conversions'));
|
||||
$view->nameGraphEvolution = 'GoalsgetEvolutionGraph';
|
||||
|
||||
// sparkline for the historical data of the above values
|
||||
$view->urlSparklineConversions = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => ''));
|
||||
$view->urlSparklineConversionRate = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => ''));
|
||||
$view->urlSparklineRevenue = $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => ''));
|
||||
|
||||
// Pass empty idGoal will return Goal overview
|
||||
$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'));
|
||||
$view->revenue = $dataRow->getColumn('revenue');
|
||||
|
||||
$goalMetrics = array();
|
||||
foreach ($this->goals as $idGoal => $goal) {
|
||||
$goalMetrics[$idGoal] = $this->getMetricsForGoal($idGoal);
|
||||
$goalMetrics[$idGoal]['name'] = $goal['name'];
|
||||
$goalMetrics[$idGoal]['goalAllowMultipleConversionsPerVisit'] = $goal['allow_multiple'];
|
||||
}
|
||||
|
||||
$view->goalMetrics = $goalMetrics;
|
||||
$view->goals = $this->goals;
|
||||
$view->goalReportsByDimension = $this->getGoalReportsByDimensionTable(
|
||||
$view->nb_conversions, $ecommerce = false, !empty($view->cart_nb_conversions));
|
||||
return $view;
|
||||
}
|
||||
|
||||
public function getLastNbConversionsGraph()
|
||||
{
|
||||
$view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversions');
|
||||
return $this->renderView($view);
|
||||
}
|
||||
|
||||
public function getLastConversionRateGraph()
|
||||
{
|
||||
$view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversionRate');
|
||||
return $this->renderView($view);
|
||||
}
|
||||
|
||||
public function getLastRevenueGraph()
|
||||
{
|
||||
$view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getRevenue');
|
||||
return $this->renderView($view);
|
||||
}
|
||||
|
||||
public function addNewGoal()
|
||||
{
|
||||
$view = new View('@Goals/addNewGoal');
|
||||
$this->setGeneralVariablesView($view);
|
||||
$view->userCanEditGoals = Piwik::isUserHasAdminAccess($this->idSite);
|
||||
$view->onlyShowAddNewGoal = true;
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function getEvolutionGraph(array $columns = array(), $idGoal = false)
|
||||
{
|
||||
if (empty($columns)) {
|
||||
$columns = Common::getRequestVar('columns');
|
||||
$columns = Piwik::getArrayFromApiParameter($columns);
|
||||
}
|
||||
|
||||
$columns = !is_array($columns) ? array($columns) : $columns;
|
||||
|
||||
if (empty($idGoal)) {
|
||||
$idGoal = Common::getRequestVar('idGoal', false, 'string');
|
||||
}
|
||||
$view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.get');
|
||||
$view->requestConfig->request_parameters_to_modify['idGoal'] = $idGoal;
|
||||
|
||||
$nameToLabel = $this->goalColumnNameToLabel;
|
||||
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['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'));
|
||||
}
|
||||
|
||||
$selectableColumns = array('nb_conversions', 'conversion_rate', 'revenue');
|
||||
if ($this->site->isEcommerceEnabled()) {
|
||||
$selectableColumns[] = 'items';
|
||||
$selectableColumns[] = 'avg_order_revenue';
|
||||
}
|
||||
|
||||
foreach (array_merge($columns, $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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($idGoal) && isset($this->goals[$idGoal])) {
|
||||
$goalName = $this->goals[$idGoal]['name'];
|
||||
$columnTranslation = "$columnTranslation (" . Piwik::translate('Goals_GoalX', "$goalName") . ")";
|
||||
}
|
||||
$view->config->translations[$columnName] = $columnTranslation;
|
||||
}
|
||||
$view->config->columns_to_display = $columns;
|
||||
$view->config->selectable_columns = $selectableColumns;
|
||||
|
||||
$langString = $idGoal ? 'Goals_SingleGoalOverviewDocumentation' : 'Goals_GoalsOverviewDocumentation';
|
||||
$view->config->documentation = Piwik::translate($langString, '<br />');
|
||||
|
||||
return $this->renderView($view);
|
||||
}
|
||||
|
||||
protected function getTopDimensions($idGoal)
|
||||
{
|
||||
$columnNbConversions = 'goal_' . $idGoal . '_nb_conversions';
|
||||
$columnConversionRate = 'goal_' . $idGoal . '_conversion_rate';
|
||||
|
||||
$topDimensionsToLoad = array();
|
||||
|
||||
if (\Piwik\Plugin\Manager::getInstance()->isPluginActivated('UserCountry')) {
|
||||
$topDimensionsToLoad += array(
|
||||
'country' => 'UserCountry.getCountry',
|
||||
);
|
||||
}
|
||||
|
||||
$keywordNotDefinedString = '';
|
||||
if (\Piwik\Plugin\Manager::getInstance()->isPluginActivated('Referrers')) {
|
||||
$keywordNotDefinedString = APIReferrers::getKeywordNotDefinedString();
|
||||
$topDimensionsToLoad += array(
|
||||
'keyword' => 'Referrers.getKeywords',
|
||||
'website' => 'Referrers.getWebsites',
|
||||
);
|
||||
}
|
||||
$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" .
|
||||
// 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();
|
||||
$topDimension = array();
|
||||
$count = 0;
|
||||
foreach ($datatable->getRows() as $row) {
|
||||
$conversions = $row->getColumn($columnNbConversions);
|
||||
if ($conversions > 0
|
||||
&& $count < self::COUNT_TOP_ROWS_TO_DISPLAY
|
||||
|
||||
// Don't put the "Keyword not defined" in the best segment since it's irritating
|
||||
&& !($dimensionName == 'keyword'
|
||||
&& $row->getColumn('label') == $keywordNotDefinedString)
|
||||
) {
|
||||
$topDimension[] = array(
|
||||
'name' => $row->getColumn('label'),
|
||||
'nb_conversions' => $conversions,
|
||||
'conversion_rate' => $this->formatConversionRate($row->getColumn($columnConversionRate)),
|
||||
'metadata' => $row->getMetadata(),
|
||||
);
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
$topDimensions[$dimensionName] = $topDimension;
|
||||
}
|
||||
return $topDimensions;
|
||||
}
|
||||
|
||||
protected function getMetricsForGoal($idGoal)
|
||||
{
|
||||
$request = new Request("method=Goals.get&format=original&idGoal=$idGoal");
|
||||
$datatable = $request->process();
|
||||
$dataRow = $datatable->getFirstRow();
|
||||
$nbConversions = $dataRow->getColumn('nb_conversions');
|
||||
$nbVisitsConverted = $dataRow->getColumn('nb_visits_converted');
|
||||
// Backward compatibilty before 1.3, this value was not processed
|
||||
if (empty($nbVisitsConverted)) {
|
||||
$nbVisitsConverted = $nbConversions;
|
||||
}
|
||||
$revenue = $dataRow->getColumn('revenue');
|
||||
$return = array(
|
||||
'id' => $idGoal,
|
||||
'nb_conversions' => (int)$nbConversions,
|
||||
'nb_visits_converted' => (int)$nbVisitsConverted,
|
||||
'conversion_rate' => $this->formatConversionRate($dataRow->getColumn('conversion_rate')),
|
||||
'revenue' => $revenue ? $revenue : 0,
|
||||
'urlSparklineConversions' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('nb_conversions'), 'idGoal' => $idGoal)),
|
||||
'urlSparklineConversionRate' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('conversion_rate'), 'idGoal' => $idGoal)),
|
||||
'urlSparklineRevenue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('revenue'), 'idGoal' => $idGoal)),
|
||||
);
|
||||
if ($idGoal == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
|
||||
$items = $dataRow->getColumn('items');
|
||||
$aov = $dataRow->getColumn('avg_order_revenue');
|
||||
$return = array_merge($return, array(
|
||||
'revenue_subtotal' => $dataRow->getColumn('revenue_subtotal'),
|
||||
'revenue_tax' => $dataRow->getColumn('revenue_tax'),
|
||||
'revenue_shipping' => $dataRow->getColumn('revenue_shipping'),
|
||||
'revenue_discount' => $dataRow->getColumn('revenue_discount'),
|
||||
|
||||
'items' => $items ? $items : 0,
|
||||
'avg_order_revenue' => $aov ? $aov : 0,
|
||||
'urlSparklinePurchasedProducts' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('items'), 'idGoal' => $idGoal)),
|
||||
'urlSparklineAverageOrderValue' => $this->getUrlSparkline('getEvolutionGraph', array('columns' => array('avg_order_revenue'), 'idGoal' => $idGoal)),
|
||||
));
|
||||
}
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility function that returns HTML that displays Goal information for reports. This
|
||||
* is the HTML that is at the bottom of every goals page.
|
||||
*
|
||||
* @param int $conversions The number of conversions for this goal (or all goals
|
||||
* in case of the overview).
|
||||
* @param bool $ecommerce Whether to show ecommerce reports or not.
|
||||
* @param bool $cartNbConversions Whether there are cart conversions or not for this
|
||||
* goal.
|
||||
* @return string
|
||||
*/
|
||||
private function getGoalReportsByDimensionTable($conversions, $ecommerce = false, $cartNbConversions = false)
|
||||
{
|
||||
$preloadAbandonedCart = $cartNbConversions !== false && $conversions == 0;
|
||||
|
||||
$goalReportsByDimension = new ReportsByDimension('Goals');
|
||||
|
||||
// add ecommerce reports
|
||||
$ecommerceCustomParams = array();
|
||||
if ($ecommerce) {
|
||||
if ($preloadAbandonedCart) {
|
||||
$ecommerceCustomParams['viewDataTable'] = 'ecommerceAbandonedCart';
|
||||
$ecommerceCustomParams['filterEcommerce'] = self::ECOMMERCE_LOG_SHOW_ABANDONED_CARTS;
|
||||
}
|
||||
|
||||
$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) {
|
||||
// for non-Goals reports, we show the goals table
|
||||
$customParams = $ecommerceCustomParams + array('documentationForGoalsPage' => '1');
|
||||
|
||||
if (Common::getRequestVar('idGoal', '') === '') // if no idGoal, use 0 for overview
|
||||
{
|
||||
$customParams['idGoal'] = '0'; // NOTE: Must be string! Otherwise Piwik_View_HtmlTable_Goals fails.
|
||||
}
|
||||
|
||||
$allReports = Goals::getReportsWithGoalMetrics();
|
||||
foreach ($allReports as $category => $reports) {
|
||||
$categoryText = Piwik::translate('Goals_ViewGoalsBy', $category);
|
||||
foreach ($reports as $report) {
|
||||
if(empty($report['viewDataTable'])) {
|
||||
$report['viewDataTable'] = 'tableGoals';
|
||||
}
|
||||
$customParams['viewDataTable'] = $report['viewDataTable'];
|
||||
|
||||
$goalReportsByDimension->addReport(
|
||||
$categoryText, $report['name'], $report['module'] . '.' . $report['action'], $customParams);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $goalReportsByDimension->render();
|
||||
}
|
||||
|
||||
//
|
||||
// Report rendering actions
|
||||
//
|
||||
|
||||
public function getItemsSku()
|
||||
{
|
||||
return $this->renderReport(__FUNCTION__);
|
||||
}
|
||||
|
||||
public function getItemsName()
|
||||
{
|
||||
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__);
|
||||
}
|
||||
}
|
||||
680
www/analytics/plugins/Goals/Goals.php
Normal file
680
www/analytics/plugins/Goals/Goals.php
Normal file
|
|
@ -0,0 +1,680 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
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\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()
|
||||
{
|
||||
$dimensions = self::getAllReportsWithGoalMetrics();
|
||||
|
||||
$dimensionsByGroup = array();
|
||||
foreach ($dimensions as $dimension) {
|
||||
$group = $dimension['category'];
|
||||
unset($dimension['category']);
|
||||
$dimensionsByGroup[$group][] = $dimension;
|
||||
}
|
||||
|
||||
uksort($dimensionsByGroup, array('self', 'sortGoalDimensionsByModule'));
|
||||
return $dimensionsByGroup;
|
||||
}
|
||||
|
||||
public static function sortGoalDimensionsByModule($a, $b)
|
||||
{
|
||||
$order = array(
|
||||
Piwik::translate('Referrers_Referrers'),
|
||||
Piwik::translate('General_Visit'),
|
||||
Piwik::translate('VisitTime_ColumnServerTime'),
|
||||
);
|
||||
$orderA = array_search($a, $order);
|
||||
$orderB = array_search($b, $order);
|
||||
return $orderA > $orderB;
|
||||
}
|
||||
|
||||
static public function getGoalColumns($idGoal)
|
||||
{
|
||||
$columns = array(
|
||||
'nb_conversions',
|
||||
'nb_visits_converted',
|
||||
'conversion_rate',
|
||||
'revenue',
|
||||
);
|
||||
if ($idGoal === false) {
|
||||
return $columns;
|
||||
}
|
||||
// Orders
|
||||
if ($idGoal === GoalManager::IDGOAL_ORDER) {
|
||||
$columns = array_merge($columns, array(
|
||||
'revenue_subtotal',
|
||||
'revenue_tax',
|
||||
'revenue_shipping',
|
||||
'revenue_discount',
|
||||
));
|
||||
}
|
||||
// Abandoned carts & orders
|
||||
if ($idGoal <= GoalManager::IDGOAL_ORDER) {
|
||||
$columns[] = 'items';
|
||||
}
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Piwik\Plugin::getListHooksRegistered
|
||||
*/
|
||||
public function getListHooksRegistered()
|
||||
{
|
||||
$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',
|
||||
'SitesManager.deleteSite.end' => 'deleteSiteGoals',
|
||||
'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics',
|
||||
'ViewDataTable.configure' => 'configureViewDataTable',
|
||||
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
|
||||
'ViewDataTable.addViewDataTable' => 'getAvailableDataTableVisualizations'
|
||||
);
|
||||
return $hooks;
|
||||
}
|
||||
|
||||
public function getAvailableDataTableVisualizations(&$visualizations)
|
||||
{
|
||||
$visualizations[] = 'Piwik\\Plugins\\Goals\\Visualizations\\Goals';
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete goals recorded for this site
|
||||
*/
|
||||
function deleteSiteGoals($idSite)
|
||||
{
|
||||
Db::query("DELETE FROM " . Common::prefixTable('goal') . " WHERE idsite = ? ", array($idSite));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Metadata for the Goals plugin API.
|
||||
* The API returns general Goal metrics: conv, conv rate and revenue globally
|
||||
* and for each goal.
|
||||
*
|
||||
* Also, this will update metadata of all other reports that have Goal segmentation
|
||||
*/
|
||||
public function getReportMetadata(&$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'
|
||||
$goalProcessedMetrics = array(
|
||||
'revenue_per_visit' => Piwik::translate('General_ColumnValuePerVisit'),
|
||||
);
|
||||
|
||||
$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')
|
||||
);
|
||||
|
||||
$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) {
|
||||
// Select this report from the API metadata array
|
||||
// and add the Goal metrics to it
|
||||
foreach ($reports as &$apiReportToUpdate) {
|
||||
if ($apiReportToUpdate['module'] == $reportWithGoals['module']
|
||||
&& $apiReportToUpdate['action'] == $reportWithGoals['action']
|
||||
) {
|
||||
$apiReportToUpdate['metricsGoal'] = $goalMetrics;
|
||||
$apiReportToUpdate['processedMetricsGoal'] = $goalProcessedMetrics;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static private function getAllReportsWithGoalMetrics()
|
||||
{
|
||||
$reportsWithGoals = array();
|
||||
|
||||
/**
|
||||
* 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(
|
||||
* 'category' => Piwik::translate('MyPlugin_myReportCategory'),
|
||||
* 'name' => Piwik::translate('MyPlugin_myReportDimension'),
|
||||
* 'module' => 'MyPlugin',
|
||||
* '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'`.
|
||||
*/
|
||||
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
|
||||
* this report will be displayed.
|
||||
*/
|
||||
public function getActualReportsWithGoalMetrics(&$dimensions)
|
||||
{
|
||||
$reportWithGoalMetrics = array(
|
||||
array('category' => Piwik::translate('General_Visit'),
|
||||
'name' => Piwik::translate('Goals_VisitsUntilConv'),
|
||||
'module' => 'Goals',
|
||||
'action' => 'getVisitsUntilConversion',
|
||||
'viewDataTable' => 'table',
|
||||
),
|
||||
array('category' => Piwik::translate('General_Visit'),
|
||||
'name' => Piwik::translate('Goals_DaysToConv'),
|
||||
'module' => 'Goals',
|
||||
'action' => 'getDaysToConversion',
|
||||
'viewDataTable' => 'table',
|
||||
)
|
||||
);
|
||||
$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";
|
||||
}
|
||||
|
||||
public function getStylesheetFiles(&$stylesheets)
|
||||
{
|
||||
$stylesheets[] = "plugins/Goals/stylesheets/goals.css";
|
||||
}
|
||||
|
||||
public function fetchGoalsFromDb(&$array, $idSite)
|
||||
{
|
||||
// add the 'goal' entry in the website array
|
||||
$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';
|
||||
}
|
||||
}
|
||||
258
www/analytics/plugins/Goals/Visualizations/Goals.php
Normal file
258
www/analytics/plugins/Goals/Visualizations/Goals.php
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Piwik\Plugins\Goals\Visualizations;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\DataTable\Filter\AddColumnsProcessedMetricsGoal;
|
||||
use Piwik\MetricsFormatter;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
|
||||
use Piwik\Plugins\Goals\API as APIGoals;
|
||||
use Piwik\Site;
|
||||
use Piwik\View;
|
||||
|
||||
/**
|
||||
* DataTable Visualization that derives from HtmlTable and sets show_goals_columns to true.
|
||||
*/
|
||||
class Goals extends HtmlTable
|
||||
{
|
||||
const ID = 'tableGoals';
|
||||
const FOOTER_ICON = 'plugins/Zeitgeist/images/goal.png';
|
||||
const FOOTER_ICON_TITLE = 'General_DisplayTableWithMoreMetrics';
|
||||
|
||||
public function beforeLoadDataTable()
|
||||
{
|
||||
parent::beforeLoadDataTable();
|
||||
|
||||
if($this->config->disable_subtable_when_show_goals) {
|
||||
$this->config->subtable_controller_action = null;
|
||||
}
|
||||
|
||||
$this->setShowGoalsColumnsProperties();
|
||||
}
|
||||
|
||||
public function beforeRender()
|
||||
{
|
||||
$this->config->show_goals = true;
|
||||
$this->config->show_goals_columns = true;
|
||||
$this->config->datatable_css_class = 'dataTableVizGoals';
|
||||
$this->config->show_exclude_low_population = true;
|
||||
|
||||
$this->config->translations += array(
|
||||
'nb_conversions' => Piwik::translate('Goals_ColumnConversions'),
|
||||
'conversion_rate' => Piwik::translate('General_ColumnConversionRate'),
|
||||
'revenue' => Piwik::translate('General_ColumnRevenue'),
|
||||
'revenue_per_visit' => Piwik::translate('General_ColumnValuePerVisit'),
|
||||
);
|
||||
|
||||
$this->config->metrics_documentation['nb_visits'] = Piwik::translate('Goals_ColumnVisits');
|
||||
|
||||
if (1 == Common::getRequestVar('documentationForGoalsPage', 0, 'int')) {
|
||||
// TODO: should not use query parameter
|
||||
$this->config->documentation = Piwik::translate('Goals_ConversionByTypeReportDocumentation',
|
||||
array('<br />', '<br />', '<a href="http://piwik.org/docs/tracking-goals-web-analytics/" target="_blank">', '</a>'));
|
||||
}
|
||||
|
||||
parent::beforeRender();
|
||||
}
|
||||
|
||||
private function setShowGoalsColumnsProperties()
|
||||
{
|
||||
// set view properties based on goal requested
|
||||
$idSite = Common::getRequestVar('idSite', null, 'int');
|
||||
$idGoal = Common::getRequestVar('idGoal', AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW, 'string');
|
||||
|
||||
if (Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER == $idGoal) {
|
||||
$this->setPropertiesForEcommerceView();
|
||||
} else if (AddColumnsProcessedMetricsGoal::GOALS_FULL_TABLE == $idGoal) {
|
||||
$this->setPropertiesForGoals($idSite, 'all');
|
||||
} else if (AddColumnsProcessedMetricsGoal::GOALS_OVERVIEW == $idGoal) {
|
||||
$this->setPropertiesForGoalsOverview($idSite);
|
||||
} else {
|
||||
$this->setPropertiesForGoals($idSite, array($idGoal));
|
||||
}
|
||||
|
||||
// add goals columns
|
||||
$this->config->filters[] = array('AddColumnsProcessedMetricsGoal', array($ignore = true, $idGoal), $priority = true);
|
||||
|
||||
// prettify columns
|
||||
$setRatePercent = function ($rate, $thang = false) {
|
||||
return $rate == 0 ? "0%" : $rate;
|
||||
};
|
||||
|
||||
foreach ($this->config->columns_to_display as $columnName) {
|
||||
if (false !== strpos($columnName, 'conversion_rate')) {
|
||||
$this->config->filters[] = array('ColumnCallbackReplace', array($columnName, $setRatePercent));
|
||||
}
|
||||
}
|
||||
|
||||
$formatPercent = function ($value) use ($idSite) {
|
||||
return MetricsFormatter::getPrettyMoney(sprintf("%.1f", $value), $idSite);
|
||||
};
|
||||
|
||||
foreach ($this->config->columns_to_display as $columnName) {
|
||||
if ($this->isRevenueColumn($columnName)) {
|
||||
$this->config->filters[] = array('ColumnCallbackReplace', array($columnName, $formatPercent));
|
||||
}
|
||||
}
|
||||
|
||||
// this ensures that the value is set to zero for all rows where the value was not set (no conversion)
|
||||
$identityFunction = function ($value) {
|
||||
return $value;
|
||||
};
|
||||
|
||||
foreach ($this->config->columns_to_display as $columnName) {
|
||||
if (!$this->isRevenueColumn($columnName)) {
|
||||
$this->config->filters[] = array('ColumnCallbackReplace', array($columnName, $identityFunction));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function setPropertiesForEcommerceView()
|
||||
{
|
||||
$this->requestConfig->filter_sort_column = 'goal_ecommerceOrder_revenue';
|
||||
$this->requestConfig->filter_sort_order = 'desc';
|
||||
|
||||
$this->config->columns_to_display = array(
|
||||
'label', 'nb_visits', 'goal_ecommerceOrder_nb_conversions', 'goal_ecommerceOrder_revenue',
|
||||
'goal_ecommerceOrder_conversion_rate', 'goal_ecommerceOrder_avg_order_revenue', 'goal_ecommerceOrder_items',
|
||||
'goal_ecommerceOrder_revenue_per_visit'
|
||||
);
|
||||
|
||||
$this->config->translations += array(
|
||||
'goal_ecommerceOrder_conversion_rate' => Piwik::translate('Goals_ConversionRate', Piwik::translate('Goals_EcommerceOrder')),
|
||||
'goal_ecommerceOrder_nb_conversions' => Piwik::translate('General_EcommerceOrders'),
|
||||
'goal_ecommerceOrder_revenue' => Piwik::translate('General_TotalRevenue'),
|
||||
'goal_ecommerceOrder_revenue_per_visit' => Piwik::translate('General_ColumnValuePerVisit'),
|
||||
'goal_ecommerceOrder_avg_order_revenue' => Piwik::translate('General_AverageOrderValue'),
|
||||
'goal_ecommerceOrder_items' => Piwik::translate('General_PurchasedProducts')
|
||||
);
|
||||
|
||||
$goalName = Piwik::translate('General_EcommerceOrders');
|
||||
$this->config->metrics_documentation += array(
|
||||
'goal_ecommerceOrder_conversion_rate' => Piwik::translate('Goals_ColumnConversionRateDocumentation', $goalName),
|
||||
'goal_ecommerceOrder_nb_conversions' => Piwik::translate('Goals_ColumnConversionsDocumentation', $goalName),
|
||||
'goal_ecommerceOrder_revenue' => Piwik::translate('Goals_ColumnRevenueDocumentation', $goalName),
|
||||
'goal_ecommerceOrder_revenue_per_visit' => Piwik::translate('Goals_ColumnAverageOrderRevenueDocumentation', $goalName),
|
||||
'goal_ecommerceOrder_avg_order_revenue' => Piwik::translate('Goals_ColumnAverageOrderRevenueDocumentation', $goalName),
|
||||
'goal_ecommerceOrder_items' => Piwik::translate('Goals_ColumnPurchasedProductsDocumentation', $goalName),
|
||||
'revenue_per_visit' => Piwik::translate('Goals_ColumnRevenuePerVisitDocumentation', $goalName)
|
||||
);
|
||||
}
|
||||
|
||||
private function setPropertiesForGoalsOverview($idSite)
|
||||
{
|
||||
$allGoals = $this->getGoals($idSite);
|
||||
|
||||
// set view properties
|
||||
$this->config->columns_to_display = array('label', 'nb_visits');
|
||||
|
||||
foreach ($allGoals as $goal) {
|
||||
$column = "goal_{$goal['idgoal']}_conversion_rate";
|
||||
$documentation = Piwik::translate('Goals_ColumnConversionRateDocumentation', $goal['quoted_name'] ? : $goal['name']);
|
||||
|
||||
$this->config->columns_to_display[] = $column;
|
||||
$this->config->translations[$column] = Piwik::translate('Goals_ConversionRate', $goal['name']);
|
||||
$this->config->metrics_documentation[$column] = $documentation;
|
||||
}
|
||||
|
||||
$this->config->columns_to_display[] = 'revenue_per_visit';
|
||||
$this->config->metrics_documentation['revenue_per_visit'] =
|
||||
Piwik::translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik::translate('Goals_EcommerceAndGoalsMenu'));
|
||||
}
|
||||
|
||||
private function setPropertiesForGoals($idSite, $idGoals)
|
||||
{
|
||||
$allGoals = $this->getGoals($idSite);
|
||||
|
||||
if ('all' == $idGoals) {
|
||||
$idGoals = array_keys($allGoals);
|
||||
} else {
|
||||
// only sort by a goal's conversions if not showing all goals (for FULL_REPORT)
|
||||
$this->requestConfig->filter_sort_column = 'goal_' . reset($idGoals) . '_nb_conversions';
|
||||
$this->requestConfig->filter_sort_order = 'desc';
|
||||
}
|
||||
|
||||
$this->config->columns_to_display = array('label', 'nb_visits');
|
||||
|
||||
$goalColumnTemplates = array(
|
||||
'goal_%s_nb_conversions',
|
||||
'goal_%s_conversion_rate',
|
||||
'goal_%s_revenue',
|
||||
'goal_%s_revenue_per_visit',
|
||||
);
|
||||
|
||||
// set columns to display (columns of same type but different goals will be next to each other,
|
||||
// ie, goal_0_nb_conversions, goal_1_nb_conversions, etc.)
|
||||
foreach ($goalColumnTemplates as $idx => $columnTemplate) {
|
||||
foreach ($idGoals as $idGoal) {
|
||||
$this->config->columns_to_display[] = sprintf($columnTemplate, $idGoal);
|
||||
}
|
||||
}
|
||||
|
||||
// set translations & metric docs for goal specific metrics
|
||||
foreach ($idGoals as $idGoal) {
|
||||
$goalName = $allGoals[$idGoal]['name'];
|
||||
$quotedGoalName = $allGoals[$idGoal]['quoted_name'] ? : $goalName;
|
||||
|
||||
$this->config->translations += array(
|
||||
'goal_' . $idGoal . '_nb_conversions' => Piwik::translate('Goals_Conversions', $goalName),
|
||||
'goal_' . $idGoal . '_conversion_rate' => Piwik::translate('Goals_ConversionRate', $goalName),
|
||||
'goal_' . $idGoal . '_revenue' =>
|
||||
Piwik::translate('%s ' . Piwik::translate('General_ColumnRevenue'), $goalName),
|
||||
'goal_' . $idGoal . '_revenue_per_visit' =>
|
||||
Piwik::translate('%s ' . Piwik::translate('General_ColumnValuePerVisit'), $goalName),
|
||||
);
|
||||
|
||||
$this->config->metrics_documentation += array(
|
||||
'goal_' . $idGoal . '_nb_conversions' => Piwik::translate('Goals_ColumnConversionsDocumentation', $quotedGoalName),
|
||||
'goal_' . $idGoal . '_conversion_rate' => Piwik::translate('Goals_ColumnConversionRateDocumentation', $quotedGoalName),
|
||||
'goal_' . $idGoal . '_revenue' => Piwik::translate('Goals_ColumnRevenueDocumentation', $quotedGoalName),
|
||||
'goal_' . $idGoal . '_revenue_per_visit' =>
|
||||
Piwik::translate('Goals_ColumnRevenuePerVisitDocumentation', Piwik::translate('Goals_EcommerceAndGoalsMenu')),
|
||||
);
|
||||
}
|
||||
|
||||
$this->config->columns_to_display[] = 'revenue_per_visit';
|
||||
}
|
||||
|
||||
private function getGoals($idSite)
|
||||
{
|
||||
// get all goals to display info for
|
||||
$allGoals = array();
|
||||
|
||||
// add the ecommerce goal if ecommerce is enabled for the site
|
||||
if (Site::isEcommerceEnabledFor($idSite)) {
|
||||
$ecommerceGoal = array(
|
||||
'idgoal' => Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER,
|
||||
'name' => Piwik::translate('Goals_EcommerceOrder'),
|
||||
'quoted_name' => false
|
||||
);
|
||||
$allGoals[$ecommerceGoal['idgoal']] = $ecommerceGoal;
|
||||
}
|
||||
|
||||
// add the site's goals (and escape all goal names)
|
||||
$siteGoals = APIGoals::getInstance()->getGoals($idSite);
|
||||
|
||||
foreach ($siteGoals as &$goal) {
|
||||
$goal['name'] = Common::sanitizeInputValue($goal['name']);
|
||||
|
||||
$goal['quoted_name'] = '"' . $goal['name'] . '"';
|
||||
$allGoals[$goal['idgoal']] = $goal;
|
||||
}
|
||||
|
||||
return $allGoals;
|
||||
}
|
||||
|
||||
private function isRevenueColumn($name)
|
||||
{
|
||||
return strpos($name, '_revenue') !== false || $name == 'revenue_per_visit';
|
||||
}
|
||||
}
|
||||
173
www/analytics/plugins/Goals/javascripts/goalsForm.js
Normal file
173
www/analytics/plugins/Goals/javascripts/goalsForm.js
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
function showAddNewGoal() {
|
||||
hideForms();
|
||||
$(".entityAddContainer").show();
|
||||
showCancel();
|
||||
piwikHelper.lazyScrollTo(".entityContainer", 400);
|
||||
return false;
|
||||
}
|
||||
|
||||
function showEditGoals() {
|
||||
hideForms();
|
||||
$("#entityEditContainer").show();
|
||||
showCancel();
|
||||
piwikHelper.lazyScrollTo(".entityContainer", 400);
|
||||
return false;
|
||||
}
|
||||
|
||||
function hideForms() {
|
||||
$(".entityAddContainer").hide();
|
||||
$("#entityEditContainer").hide();
|
||||
}
|
||||
|
||||
function showCancel() {
|
||||
$(".entityCancel").show();
|
||||
$('.entityCancelLink').click(function () {
|
||||
hideForms();
|
||||
$(".entityCancel").hide();
|
||||
});
|
||||
}
|
||||
|
||||
// init the goal form with existing goal value, if any
|
||||
function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, pattern, patternType, caseSensitive, revenue, allowMultiple, goalId) {
|
||||
$('#goal_name').val(goalName);
|
||||
if (matchAttribute == 'manually') {
|
||||
$('select[name=trigger_type] option[value=manually]').prop('selected', true);
|
||||
$('input[name=match_attribute]').prop('disabled', true);
|
||||
$('#match_attribute_section').hide();
|
||||
$('#manual_trigger_section').show();
|
||||
matchAttribute = 'url';
|
||||
} else {
|
||||
$('select[name=trigger_type] option[value=visitors]').prop('selected', true);
|
||||
}
|
||||
$('input[name=match_attribute][value=' + matchAttribute + ']').prop('checked', true);
|
||||
$('input[name=allow_multiple][value=' + allowMultiple + ']').prop('checked', true);
|
||||
$('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]);
|
||||
$('#examples_pattern').html(mappingMatchTypeExamples[matchAttribute]);
|
||||
$('select[name=pattern_type] option[value=' + patternType + ']').prop('selected', true);
|
||||
$('input[name=pattern]').val(pattern);
|
||||
$('#case_sensitive').prop('checked', caseSensitive);
|
||||
$('input[name=revenue]').val(revenue);
|
||||
$('input[name=methodGoalAPI]').val(goalMethodAPI);
|
||||
$('#goal_submit').val(submitText);
|
||||
if (goalId != undefined) {
|
||||
$('input[name=goalIdUpdate]').val(goalId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function bindGoalForm() {
|
||||
$('select[name=trigger_type]').click(function () {
|
||||
var triggerTypeId = $(this).val();
|
||||
if (triggerTypeId == "manually") {
|
||||
$('input[name=match_attribute]').prop('disabled', true);
|
||||
$('#match_attribute_section').hide();
|
||||
$('#manual_trigger_section').show();
|
||||
} else {
|
||||
$('input[name=match_attribute]').removeProp('disabled');
|
||||
$('#match_attribute_section').show();
|
||||
$('#manual_trigger_section').hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('input[name=match_attribute]').click(function () {
|
||||
var matchTypeId = $(this).val();
|
||||
$('#match_attribute_name').html(mappingMatchTypeName[matchTypeId]);
|
||||
$('#examples_pattern').html(mappingMatchTypeExamples[matchTypeId]);
|
||||
});
|
||||
|
||||
$('#goal_submit').click(function () {
|
||||
// prepare ajax query to API to add goal
|
||||
ajaxAddGoal();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('a[name=linkAddNewGoal]').click(function () {
|
||||
initAndShowAddGoalForm();
|
||||
piwikHelper.lazyScrollTo('#goal_name');
|
||||
});
|
||||
}
|
||||
|
||||
function ajaxDeleteGoal(idGoal) {
|
||||
piwikHelper.lazyScrollTo(".entityContainer", 400);
|
||||
|
||||
var parameters = {};
|
||||
parameters.format = 'json';
|
||||
parameters.idGoal = idGoal;
|
||||
parameters.module = 'API';
|
||||
parameters.method = 'Goals.deleteGoal';
|
||||
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.addParams(parameters, 'get');
|
||||
ajaxRequest.setLoadingElement('#goalAjaxLoading');
|
||||
ajaxRequest.setCallback(function () { location.reload(); });
|
||||
ajaxRequest.send(true);
|
||||
}
|
||||
|
||||
function ajaxAddGoal() {
|
||||
piwikHelper.lazyScrollTo(".entityContainer", 400);
|
||||
|
||||
var parameters = {};
|
||||
parameters.name = encodeURIComponent($('#goal_name').val());
|
||||
|
||||
if ($('[name=trigger_type]').val() == 'manually') {
|
||||
parameters.matchAttribute = 'manually';
|
||||
parameters.patternType = 'regex';
|
||||
parameters.pattern = '.*';
|
||||
parameters.caseSensitive = 0;
|
||||
} else {
|
||||
parameters.matchAttribute = $('input[name=match_attribute]:checked').val();
|
||||
parameters.patternType = $('[name=pattern_type]').val();
|
||||
parameters.pattern = encodeURIComponent($('input[name=pattern]').val());
|
||||
parameters.caseSensitive = $('#case_sensitive').prop('checked') == true ? 1 : 0;
|
||||
}
|
||||
parameters.revenue = $('input[name=revenue]').val();
|
||||
parameters.allowMultipleConversionsPerVisit = $('input[name=allow_multiple]:checked').val() == true ? 1 : 0;
|
||||
|
||||
parameters.idGoal = $('input[name=goalIdUpdate]').val();
|
||||
parameters.format = 'json';
|
||||
parameters.module = 'API';
|
||||
parameters.method = $('input[name=methodGoalAPI]').val();
|
||||
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.addParams(parameters, 'get');
|
||||
ajaxRequest.setLoadingElement('#goalAjaxLoading');
|
||||
ajaxRequest.setCallback(function () { location.reload(); });
|
||||
ajaxRequest.send(true);
|
||||
}
|
||||
|
||||
function bindListGoalEdit() {
|
||||
$('a[name=linkEditGoal]').click(function () {
|
||||
var goalId = $(this).attr('id');
|
||||
var goal = piwik.goals[goalId];
|
||||
initGoalForm("Goals.updateGoal", _pk_translate('Goals_UpdateGoal'), goal.name, goal.match_attribute, goal.pattern, goal.pattern_type, (goal.case_sensitive != '0'), goal.revenue, goal.allow_multiple, goalId);
|
||||
showAddNewGoal();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('a[name=linkDeleteGoal]').click(function () {
|
||||
var goalId = $(this).attr('id');
|
||||
var goal = piwik.goals[goalId];
|
||||
|
||||
$('#confirm').find('h2').text(sprintf(_pk_translate('Goals_DeleteGoalConfirm'), '"' + goal.name + '"'));
|
||||
piwikHelper.modalConfirm('#confirm', {yes: function () {
|
||||
ajaxDeleteGoal(goalId);
|
||||
}});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('a[name=linkEditGoals]').click(function () {
|
||||
return showEditGoals();
|
||||
});
|
||||
}
|
||||
|
||||
function initAndShowAddGoalForm() {
|
||||
initGoalForm('Goals.addGoal', _pk_translate('Goals_AddGoal'), '', 'url', '', 'contains', /*caseSensitive = */false, /*allowMultiple = */'0', '0');
|
||||
return showAddNewGoal();
|
||||
}
|
||||
27
www/analytics/plugins/Goals/stylesheets/goals.css
Normal file
27
www/analytics/plugins/Goals/stylesheets/goals.css
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
.goalTopElement {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
|
||||
.goalEntry {
|
||||
margin: 0 0 20px 0;
|
||||
padding: 0 0 10px 0;
|
||||
border-bottom: 1px solid #7e7363;
|
||||
width: 614px;
|
||||
}
|
||||
|
||||
/* dimension selector */
|
||||
#titleGoalsByDimension {
|
||||
padding-top: 30px;
|
||||
}
|
||||
|
||||
ul.ulGoalTopElements {
|
||||
list-style-type: circle;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.ulGoalTopElements a {
|
||||
text-decoration: none;
|
||||
color: #0033CC;
|
||||
border-bottom: 1px dotted #0033CC;
|
||||
line-height: 2em;
|
||||
}
|
||||
81
www/analytics/plugins/Goals/templates/_addEditGoal.twig
Normal file
81
www/analytics/plugins/Goals/templates/_addEditGoal.twig
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
{% if onlyShowAddNewGoal is defined %}
|
||||
<h2 piwik-enriched-headline>{{ 'Goals_AddNewGoal'|translate }}</h2>
|
||||
<p>{{ 'Goals_NewGoalIntro'|translate }}</p>
|
||||
<p>{{ 'Goals_NewGoalDescription'|translate }}
|
||||
{{ 'Goals_NewWhatDoYouWantUsersToDo'|translate }}
|
||||
{{ 'Goals_NewGoalYouWillBeAbleTo'|translate }}</p>
|
||||
<p>{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}
|
||||
</p>
|
||||
{% else %}
|
||||
<div class="clear"></div>
|
||||
<h2 piwik-enriched-headline>{{ 'Goals_GoalsManagement'|translate }}</h2>
|
||||
<div class="entityList">
|
||||
<ul class='listCircle'>
|
||||
<li><a onclick='' name='linkAddNewGoal'>{{ 'Goals_CreateNewGOal'|translate }}</a></li>
|
||||
<li><a onclick='' name='linkEditGoals'>{{ 'Goals_ViewAndEditGoals'|translate }}</a></li>
|
||||
<li>{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}</li>
|
||||
|
||||
<li>
|
||||
{% if not ecommerceEnabled %}
|
||||
{% set websiteManageText %}
|
||||
<a href='{{ linkTo({'module':'SitesManager','action':'index' }) }}'>{{ 'SitesManager_WebsitesManagement'|translate }}</a>
|
||||
{% endset %}
|
||||
{% set ecommerceReportText %}
|
||||
<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">{{ 'Goals_EcommerceReports'|translate }}</a>
|
||||
{% endset %}
|
||||
{{ 'Goals_Optional'|translate }} {{ 'Goals_Ecommerce'|translate }}: {{ 'Goals_YouCanEnableEcommerceReports'|translate(ecommerceReportText,websiteManageText)|raw }}
|
||||
{% else %}
|
||||
{{ 'SitesManager_PiwikOffersEcommerceAnalytics'|translate('<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">',"</a>")|raw }}
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<br/>
|
||||
{% endif %}
|
||||
|
||||
{% import 'ajaxMacros.twig' as ajax %}
|
||||
{{ ajax.errorDiv() }}
|
||||
{{ ajax.loadingDiv('goalAjaxLoading') }}
|
||||
|
||||
<div class="entityContainer">
|
||||
{% if onlyShowAddNewGoal is not defined %}
|
||||
{% include "@Goals/_listGoalEdit.twig" %}
|
||||
{% endif %}
|
||||
{% include "@Goals/_formAddGoal.twig" %}
|
||||
{% if onlyShowAddNewGoal is not defined %}
|
||||
<div class='entityCancel' style='display:none;'>
|
||||
{{ 'General_OrCancel'|translate("<a class='entityCancelLink'>","</a>")|raw }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<a id='bottom'></a>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<script type="text/javascript">
|
||||
var mappingMatchTypeName = {
|
||||
"url": "{{ 'Goals_URL'|translate }}",
|
||||
"title": "{{ 'Goals_PageTitle'|translate }}",
|
||||
"file": "{{ 'Goals_Filename'|translate }}",
|
||||
"external_website": "{{ 'Goals_ExternalWebsiteUrl'|translate }}"
|
||||
};
|
||||
var mappingMatchTypeExamples = {
|
||||
"url": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'checkout/confirmation'") }} \
|
||||
<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://example.com/thank-you.html'") }} \
|
||||
<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\/demo\\\/(.*)'") }}",
|
||||
"title": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'Order confirmation'") }}",
|
||||
"file": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'files/brochure.pdf'") }} \
|
||||
<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://example.com/files/brochure.pdf'") }} \
|
||||
<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'(.*)\\\.zip'") }}",
|
||||
"external_website": "{{ 'General_ForExampleShort'|translate }} {{ 'Goals_Contains'|translate("'amazon.com'") }} \
|
||||
<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_IsExactly'|translate("'http://mypartner.com/landing.html'") }} \
|
||||
<br />{{ 'General_ForExampleShort'|translate }} {{ 'Goals_MatchesExpression'|translate("'http://www.amazon.com\\\/(.*)\\\/yourAffiliateId'") }}"
|
||||
};
|
||||
bindGoalForm();
|
||||
|
||||
{% if onlyShowAddNewGoal is not defined %}
|
||||
piwik.goals = {{ goalsJSON|raw }};
|
||||
bindListGoalEdit();
|
||||
{% else %}
|
||||
initAndShowAddGoalForm();
|
||||
{% endif %}
|
||||
|
||||
</script>
|
||||
97
www/analytics/plugins/Goals/templates/_formAddGoal.twig
Normal file
97
www/analytics/plugins/Goals/templates/_formAddGoal.twig
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
<div class='entityAddContainer' style="display:none;">
|
||||
<form>
|
||||
<table class="dataTable entityTable">
|
||||
<thead>
|
||||
<tr class="first">
|
||||
<th colspan="2">{{ 'Goals_AddNewGoal'|translate }}</th>
|
||||
<tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="first">{{ 'Goals_GoalName'|translate }} </td>
|
||||
<td><input type="text" name="name" value="" size="28" id="goal_name" class="inp"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style='width:260px;' class="first">{{ 'Goals_GoalIsTriggered'|translate }}
|
||||
<select name="trigger_type" class="inp">
|
||||
<option value="visitors">{{ 'Goals_WhenVisitors'|translate }}</option>
|
||||
<option value="manually">{{ 'Goals_Manually'|translate }}</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<input type="radio" id="match_attribute_url" value="url" name="match_attribute"/>
|
||||
<label for="match_attribute_url">{{ 'Goals_VisitUrl'|translate }}</label>
|
||||
<br/>
|
||||
<input type="radio" id="match_attribute_title" value="title" name="match_attribute"/>
|
||||
<label for="match_attribute_title">{{ 'Goals_VisitPageTitle'|translate }}</label>
|
||||
<br/>
|
||||
<input type="radio" id="match_attribute_file" value="file" name="match_attribute"/>
|
||||
<label for="match_attribute_file">{{ 'Goals_Download'|translate }}</label>
|
||||
<br/>
|
||||
<input type="radio" id="match_attribute_external_website" value="external_website" name="match_attribute"/>
|
||||
<label for="match_attribute_external_website">{{ 'Goals_ClickOutlink'|translate }}</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody id="match_attribute_section">
|
||||
<tr>
|
||||
<td class="first">{{ 'Goals_WhereThe'|translate }} <span id="match_attribute_name"></span></td>
|
||||
<td>
|
||||
<select name="pattern_type" class="inp">
|
||||
<option value="contains">{{ 'Goals_Contains'|translate("") }}</option>
|
||||
<option value="exact">{{ 'Goals_IsExactly'|translate("") }}</option>
|
||||
<option value="regex">{{ 'Goals_MatchesExpression'|translate("") }}</option>
|
||||
</select>
|
||||
|
||||
<input type="text" name="pattern" value="" size="16" class="inp"/>
|
||||
<br/>
|
||||
|
||||
<div id="examples_pattern" class="entityInlineHelp"></div>
|
||||
<br/>
|
||||
<span style="float:right;">
|
||||
{{ 'Goals_Optional'|translate }} <input type="checkbox" id="case_sensitive"/>
|
||||
<label for="case_sensitive">{{ 'Goals_CaseSensitive'|translate }}</label>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody id="manual_trigger_section" style="display:none;">
|
||||
<tr>
|
||||
<td colspan="2" class="first">
|
||||
{{ 'Goals_WhereVisitedPageManuallyCallsJavascriptTrackerLearnMore'|translate("<a target='_blank' href='?module=Proxy&action=redirect&url=http://piwik.org/docs/javascript-tracking/%23toc-manually-trigger-a-conversion-for-a-goal'>","</a>")|raw }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="first"> {{ 'Goals_AllowMultipleConversionsPerVisit'|translate }} </td>
|
||||
<td>
|
||||
<input type="radio" id="allow_multiple_0" value="0" name="allow_multiple"/>
|
||||
<label for="allow_multiple_0">{{ 'Goals_DefaultGoalConvertedOncePerVisit'|translate }}</label>
|
||||
|
||||
<div class="entityInlineHelp">
|
||||
{{ 'Goals_HelpOneConversionPerVisit'|translate }}
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
<input type="radio" id="allow_multiple_1" value="1" name="allow_multiple"/>
|
||||
<label for="allow_multiple_1">{{ 'Goals_AllowGoalConvertedMoreThanOncePerVisit'|translate }}</label>
|
||||
<br/><br/>
|
||||
</tr>
|
||||
<tr>
|
||||
</tbody>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="first">{{ 'Goals_Optional'|translate }} {{ 'Goals_DefaultRevenue'|translate }}</td>
|
||||
<td>{{ ' <input type="text" name="revenue" size="2" value="0" class="inp" /> '|money(idSite)|raw }}
|
||||
<div class="entityInlineHelp"> {{ 'Goals_DefaultRevenueHelp'|translate }} </div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<input type="hidden" name="methodGoalAPI" value=""/>
|
||||
<input type="hidden" name="goalIdUpdate" value=""/>
|
||||
<input type="submit" value="" name="submit" id="goal_submit" class="submit"/>
|
||||
</form>
|
||||
</div>
|
||||
64
www/analytics/plugins/Goals/templates/_listGoalEdit.twig
Normal file
64
www/analytics/plugins/Goals/templates/_listGoalEdit.twig
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
<div id='entityEditContainer' style="display:none;">
|
||||
<table class="dataTable entityTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="first">Id</th>
|
||||
<th>{{ 'Goals_GoalName'|translate }}</th>
|
||||
<th>{{ 'Goals_GoalIsTriggeredWhen'|translate }}</th>
|
||||
<th>{{ 'General_ColumnRevenue'|translate }}</th>
|
||||
<th>{{ 'General_Edit'|translate }}</th>
|
||||
<th>{{ 'General_Delete'|translate }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for goal in goals %}
|
||||
<tr>
|
||||
<td class="first">{{ goal.idgoal }}</td>
|
||||
<td>{{ goal.name|raw }}</td>{# NOTE: goal names are escaped in the DB #}
|
||||
<td><span class='matchAttribute'>{{ goal.match_attribute }}</span>
|
||||
{% if goal.pattern_type is defined %}
|
||||
<br/>
|
||||
{{ 'Goals_Pattern'|translate }} {{ goal.pattern_type }}: {{ goal.pattern|raw }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{% if goal.revenue==0 %}-{% else %}{{ goal.revenue|money(idSite)|raw }}{% endif %}</td>
|
||||
<td>
|
||||
<a href='#' name="linkEditGoal" id="{{ goal.idgoal }}" class="link_but">
|
||||
<img src='plugins/Zeitgeist/images/ico_edit.png' border="0"/>
|
||||
{{ 'General_Edit'|translate }}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<a href='#' name="linkDeleteGoal" id="{{ goal.idgoal }}" class="link_but">
|
||||
<img src='plugins/Zeitgeist/images/ico_delete.png' border="0"/>
|
||||
{{ 'General_Delete'|translate }}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="ui-confirm" id="confirm">
|
||||
<h2></h2>
|
||||
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
|
||||
<input role="no" type="button" value="{{ 'General_No'|translate }}"/>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
var goalTypeToTranslation = {
|
||||
"manually": "{{ 'Goals_ManuallyTriggeredUsingJavascriptFunction'|translate }}",
|
||||
"file": "{{ 'Goals_Download'|translate }}",
|
||||
"url": "{{ 'Goals_VisitUrl'|translate }}",
|
||||
"title": "{{ 'Goals_VisitPageTitle'|translate }}",
|
||||
"external_website": "{{ 'Goals_ClickOutlink'|translate }}"
|
||||
};
|
||||
|
||||
$(document).ready(function () {
|
||||
// translation of the goal "match attribute" to human readable description
|
||||
$('.matchAttribute').each(function () {
|
||||
var matchAttribute = $(this).text();
|
||||
var translation = goalTypeToTranslation[matchAttribute];
|
||||
$(this).text(translation);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
16
www/analytics/plugins/Goals/templates/_listTopDimension.twig
Normal file
16
www/analytics/plugins/Goals/templates/_listTopDimension.twig
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% for element in topDimension %}
|
||||
{% set goal_nb_conversion=element.nb_conversions %}
|
||||
{% set goal_conversion_rate=element.conversion_rate %}
|
||||
<span class='goalTopElement' title='{{ 'Goals_Conversions'|translate("<b>"~goal_nb_conversion~"</b>")|raw }},
|
||||
{{ 'Goals_ConversionRate'|translate("<b>"~goal_conversion_rate~"</b>")|raw }}'>
|
||||
{{ element.name }}
|
||||
</span>
|
||||
|
||||
{% import 'macros.twig' as piwik %}
|
||||
{{ piwik.logoHtml(element.metadata, element.name) }}
|
||||
{% if loop.index == loop.length-1 %}
|
||||
and
|
||||
{% elseif loop.index < loop.length-1 %}
|
||||
,
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
<span data-graph-id="{{ nameGraphEvolution }}"></span>
|
||||
|
||||
{% if displayFullReport %}
|
||||
<h2 piwik-enriched-headline>{% if goalName is defined %}{{ 'Goals_GoalX'|translate(goalName)|raw }}{% else %}{{ 'Goals_GoalsOverview'|translate }}{% endif %}</h2>
|
||||
{% endif %}
|
||||
{{ graphEvolution|raw }}
|
||||
|
||||
<div id='leftcolumn' {% if not isWidget %}style='width:33%;'{% endif %}>
|
||||
<div class="sparkline">{{ sparkline(urlSparklineConversions) }}
|
||||
{% if ecommerce is defined %}
|
||||
<strong>{{ nb_conversions }}</strong>
|
||||
{{ 'General_EcommerceOrders'|translate }}
|
||||
<img src='plugins/Zeitgeist/images/ecommerceOrder.gif'>
|
||||
{% else %}
|
||||
{{ 'Goals_Conversions'|translate("<strong>"~nb_conversions~"</strong>")|raw }}
|
||||
{% endif %}
|
||||
{% if goalAllowMultipleConversionsPerVisit is defined and goalAllowMultipleConversionsPerVisit %}
|
||||
({{ 'General_NVisits'|translate("<strong>"~nb_visits_converted~"</strong>")|raw }})
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if revenue != 0 or ecommerce is defined %}
|
||||
<div class="sparkline">
|
||||
{{ sparkline(urlSparklineRevenue) }}
|
||||
{% set revenue=revenue|money(idSite) %}
|
||||
{% if ecommerce is defined %}
|
||||
<strong>{{ revenue|raw }}</strong> {{ 'General_TotalRevenue'|translate }}
|
||||
{% else %}
|
||||
{{ 'Goals_OverallRevenue'|translate("<strong>"~revenue~"</strong>")|raw }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if ecommerce is defined %}
|
||||
<div class="sparkline">{{ sparkline(urlSparklineAverageOrderValue) }}
|
||||
<strong>{{ avg_order_revenue|money(idSite)|raw }}</strong>
|
||||
{{ 'General_AverageOrderValue'|translate }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<div id='leftcolumn' {% if not isWidget %}style='width:33%;'{% endif %}>
|
||||
<div class="sparkline">{{ sparkline(urlSparklineConversionRate) }}
|
||||
{% if ecommerce is defined %}
|
||||
{% set ecommerceOrdersText %}{{ 'General_EcommerceOrders'|translate }}{% endset %}
|
||||
{{ 'Goals_ConversionRate'|translate("<strong>"~conversion_rate~"</strong> "~ecommerceOrdersText)|raw }}
|
||||
{% else %}
|
||||
{{ 'Goals_OverallConversionRate'|translate("<strong>"~conversion_rate~"</strong>")|raw }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if ecommerce is defined %}
|
||||
<div class="sparkline">{{ sparkline(urlSparklinePurchasedProducts) }}
|
||||
<strong>{{ items }}</strong> {{ 'General_PurchasedProducts'|translate }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if ecommerce is defined %}
|
||||
<div id='rightcolumn' {% if not isWidget %}style='width:30%;'{% endif %}>
|
||||
<div>
|
||||
<img src='plugins/Zeitgeist/images/ecommerceAbandonedCart.gif'> <em>{{ 'General_AbandonedCarts'|translate }}</em>
|
||||
</div>
|
||||
|
||||
<div class="sparkline">
|
||||
{{ sparkline(cart_urlSparklineConversions) }}
|
||||
{% set ecommerceAbandonedCartsText %}{{ 'Goals_AbandonedCart'|translate }}{% endset %}
|
||||
<strong>{{ cart_nb_conversions }}</strong> {{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }}
|
||||
</div>
|
||||
|
||||
<div class="sparkline">
|
||||
{{ sparkline(cart_urlSparklineRevenue) }}
|
||||
{% set revenue %}{{ cart_revenue|money(idSite)|raw }}{% endset %}
|
||||
{% set revenueText %}{{ 'General_ColumnRevenue'|translate }}{% endset %}
|
||||
<strong>{{ revenue }}</strong> {{ 'Goals_LeftInCart'|translate(revenueText) }}
|
||||
</div>
|
||||
|
||||
<div class="sparkline">
|
||||
{{ sparkline(cart_urlSparklineConversionRate) }}
|
||||
<strong>{{ cart_conversion_rate }}</strong>
|
||||
{{ 'General_VisitsWith'|translate(ecommerceAbandonedCartsText) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "_sparklineFooter.twig" %}
|
||||
|
||||
11
www/analytics/plugins/Goals/templates/addNewGoal.twig
Normal file
11
www/analytics/plugins/Goals/templates/addNewGoal.twig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{% if userCanEditGoals %}
|
||||
{% include "@Goals/_addEditGoal.twig" %}
|
||||
{% else %}
|
||||
<h2>{{ 'Goals_CreateNewGOal'|translate }}</h2>
|
||||
<p>
|
||||
{{ 'Goals_NoGoalsNeedAccess'|translate|raw }}
|
||||
</p>
|
||||
<p>
|
||||
{{ 'Goals_LearnMoreAboutGoalTrackingDocumentation'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/docs/tracking-goals-web-analytics/' target='_blank'>","</a>")|raw }}
|
||||
</p>
|
||||
{% endif %}
|
||||
66
www/analytics/plugins/Goals/templates/getGoalReportView.twig
Normal file
66
www/analytics/plugins/Goals/templates/getGoalReportView.twig
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<link rel="stylesheet" type="text/css" href="plugins/Goals/stylesheets/goals.css"/>
|
||||
{% include "@Goals/_titleAndEvolutionGraph.twig" | raw %}
|
||||
|
||||
<div class="clear"></div>
|
||||
{% if nb_conversions > 0 %}
|
||||
<h2>{{ 'Goals_ConversionsOverview'|translate }}</h2>
|
||||
<ul class="ulGoalTopElements">
|
||||
{% if ecommerce is not defined %}
|
||||
{% if topDimensions.country is defined %}
|
||||
<li>{{ 'Goals_BestCountries'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.country} %}</li>
|
||||
{% endif %}
|
||||
{% if topDimensions.keyword is defined and topDimensions.keyword|length > 0 %}
|
||||
<li>{{ 'Goals_BestKeywords'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.keyword} %}</li>
|
||||
{% endif %}
|
||||
{% if topDimensions.website is defined and topDimensions.website|length > 0 %}
|
||||
<li>{{ 'Goals_BestReferrers'|translate }} {% include '@Goals/_listTopDimension.twig' with {'topDimension':topDimensions.website} %}</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
{{ 'Goals_ReturningVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_returning~"</strong>")|raw }}
|
||||
, {{ 'Goals_NewVisitorsConversionRateIs'|translate("<strong>"~conversion_rate_new~"</strong>")|raw }}
|
||||
</li>
|
||||
{% else %}
|
||||
<li>
|
||||
{{ 'General_ColumnRevenue'|translate }}: {{ revenue|money(idSite)|raw -}}
|
||||
{% if revenue_subtotal is not empty %},
|
||||
{{ 'General_Subtotal'|translate }}: {{ revenue_subtotal|money(idSite)|raw -}}
|
||||
{% endif %}
|
||||
{%- if revenue_tax is not empty -%},
|
||||
{{ 'General_Tax'|translate }}: {{ revenue_tax|money(idSite)|raw -}}
|
||||
{% endif %}
|
||||
{%- if revenue_shipping is not empty -%},
|
||||
{{ 'General_Shipping'|translate }}: {{ revenue_shipping|money(idSite)|raw -}}
|
||||
{% endif %}
|
||||
{%- if revenue_discount is not empty -%},
|
||||
{{ 'General_Discount'|translate }}: {{ revenue_discount|money(idSite)|raw -}}
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.goalTopElement').tooltip({
|
||||
track: true,
|
||||
content: function () {
|
||||
return $(this).attr("title");
|
||||
},
|
||||
show: false,
|
||||
hide: false
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
{% if displayFullReport %}
|
||||
{% if nb_conversions > 0 or cart_nb_conversions is defined %}
|
||||
<h2 id='titleGoalsByDimension'>
|
||||
{% if idGoal is defined %}
|
||||
{{ 'Goals_GoalConversionsBy'|translate(goalName)|raw }}
|
||||
{% else %}
|
||||
{{ 'Goals_ConversionsOverviewBy'|translate }}
|
||||
{% endif %}
|
||||
</h2>
|
||||
{{ goalReportsByDimension|raw }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
50
www/analytics/plugins/Goals/templates/getOverviewView.twig
Normal file
50
www/analytics/plugins/Goals/templates/getOverviewView.twig
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
<link rel="stylesheet" type="text/css" href="plugins/Goals/stylesheets/goals.css"/>
|
||||
|
||||
{% include "@Goals/_titleAndEvolutionGraph.twig" %}
|
||||
{% set sum_nb_conversions=nb_conversions %}
|
||||
|
||||
{% for goal in goalMetrics %}
|
||||
{% set nb_conversions=goal.nb_conversions %}
|
||||
{% set nb_visits_converted=goal.nb_visits_converted %}
|
||||
{% set conversion_rate=goal.conversion_rate %}
|
||||
{% set name=goal.name %}
|
||||
<div class="goalEntry">
|
||||
<h2>
|
||||
<a href="javascript:broadcast.propagateAjax('module=Goals&action=goalReport&idGoal={{ goal.id }}')">
|
||||
{{ 'Goals_GoalX'|translate("'"~name~"'")|raw }}
|
||||
</a>
|
||||
</h2>
|
||||
|
||||
<div id='leftcolumn'>
|
||||
<div class="sparkline">{{ sparkline(goal.urlSparklineConversions) }}
|
||||
{{ 'Goals_Conversions'|translate("<strong>"~nb_conversions~"</strong>")|raw }}
|
||||
{% if goal.goalAllowMultipleConversionsPerVisit %}
|
||||
({{ 'General_NVisits'|translate("<strong>"~nb_visits_converted~"</strong>") | raw }})
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div id='rightcolumn'>
|
||||
<div class="sparkline">{{ sparkline(goal.urlSparklineConversionRate) }}
|
||||
{{ 'Goals_ConversionRate'|translate("<strong>"~conversion_rate~"</strong>")|raw }}
|
||||
</div>
|
||||
</div>
|
||||
<br class="clear"/>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% if displayFullReport %}
|
||||
{% if sum_nb_conversions != 0 %}
|
||||
<h2 id='titleGoalsByDimension'>
|
||||
{% if idGoal is defined %}
|
||||
{{ 'Goals_GoalConversionsBy'|translate(goalName)|raw }}
|
||||
{% else %}
|
||||
{{ 'Goals_ConversionsOverviewBy'|translate }}
|
||||
{% endif %}
|
||||
</h2>
|
||||
{{ goalReportsByDimension|raw }}
|
||||
{% endif %}
|
||||
|
||||
{% if userCanEditGoals %}
|
||||
{% include "@Goals/_addEditGoal.twig" %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue