Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/Goals/Goals.php')
-rw-r--r--plugins/Goals/Goals.php1738
1 files changed, 848 insertions, 890 deletions
diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php
index ee4af3a197..5e0fe62728 100644
--- a/plugins/Goals/Goals.php
+++ b/plugins/Goals/Goals.php
@@ -15,897 +15,855 @@
*/
class Piwik_Goals extends Piwik_Plugin
{
- const VISITS_UNTIL_RECORD_NAME = 'visits_until_conv';
- const DAYS_UNTIL_CONV_RECORD_NAME = 'days_until_conv';
-
- /**
- * 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)
- );
+ const VISITS_UNTIL_RECORD_NAME = 'visits_until_conv';
+ const DAYS_UNTIL_CONV_RECORD_NAME = 'days_until_conv';
+
+ /**
+ * 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)
+ );
public function getInformation()
- {
- $info = array(
- 'description' => Piwik_Translate('Goals_PluginDescription') . ' '. Piwik_Translate('SitesManager_PiwikOffersEcommerceAnalytics', array('<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">','</a>')),
- 'author' => 'Piwik',
- 'author_homepage' => 'http://piwik.org/',
- 'version' => Piwik_Version::VERSION,
- 'TrackerPlugin' => true, // this plugin must be loaded during the stats logging
- );
- return $info;
- }
-
- function getListHooksRegistered()
- {
- $hooks = array(
- 'AssetManager.getJsFiles' => 'getJsFiles',
- 'AssetManager.getCssFiles' => 'getCssFiles',
- 'Common.fetchWebsiteAttributes' => 'fetchGoalsFromDb',
- 'ArchiveProcessing_Day.compute' => 'archiveDay',
- 'ArchiveProcessing_Period.compute' => 'archivePeriod',
- 'API.getReportMetadata.end' => 'getReportMetadata',
- 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
- 'WidgetsList.add' => 'addWidgets',
- 'Menu.add' => 'addMenus',
- 'SitesManager.deleteSite' => 'deleteSiteGoals',
- 'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics',
- );
- return $hooks;
- }
-
- /**
- * Delete goals recorded for this site
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function deleteSiteGoals($notification)
- {
- $idSite = &$notification->getNotificationObject();
- Piwik_Query("DELETE FROM ".Piwik_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
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getReportMetadata($notification)
- {
- $info = $notification->getNotificationInfo();
- $idSites = $info['idSites'];
- $reports = &$notification->getNotificationObject();
-
- // 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('Goals_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 = Piwik_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
- $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 Piwik_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_Archive::LABEL_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_Archive::LABEL_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_Archive::LABEL_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('Goals_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_Archive::LABEL_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_Archive::LABEL_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_Archive::LABEL_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']);
-
- /*
- * Add the metricsGoal and processedMetricsGoal entry
- * to all reports that have Goal segmentation
- */
- $reportsWithGoals = array();
- Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $reportsWithGoals);
- 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;
- }
- }
- }
-
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- public function getSegmentsMetadata($notification)
- {
- $segments =& $notification->getNotificationObject();
- $segments[] = array(
- 'type' => 'dimension',
- 'category' => 'Visit',
- 'name' => 'General_VisitConvertedGoalId',
- 'segment' => 'visitConvertedGoalId',
- 'sqlSegment' => 'log_conversion.idgoal',
- 'acceptedValues' => '1, 2, 3, etc.',
+ {
+ $info = array(
+ 'description' => Piwik_Translate('Goals_PluginDescription') . ' ' . Piwik_Translate('SitesManager_PiwikOffersEcommerceAnalytics', array('<a href="http://piwik.org/docs/ecommerce-analytics/" target="_blank">', '</a>')),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ 'TrackerPlugin' => true, // this plugin must be loaded during the stats logging
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'AssetManager.getJsFiles' => 'getJsFiles',
+ 'AssetManager.getCssFiles' => 'getCssFiles',
+ 'Common.fetchWebsiteAttributes' => 'fetchGoalsFromDb',
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'API.getReportMetadata.end' => 'getReportMetadata',
+ 'API.getSegmentsMetadata' => 'getSegmentsMetadata',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenus',
+ 'SitesManager.deleteSite' => 'deleteSiteGoals',
+ 'Goals.getReportsWithGoalMetrics' => 'getActualReportsWithGoalMetrics',
+ );
+ return $hooks;
+ }
+
+ /**
+ * Delete goals recorded for this site
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function deleteSiteGoals($notification)
+ {
+ $idSite = & $notification->getNotificationObject();
+ Piwik_Query("DELETE FROM " . Piwik_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
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getReportMetadata($notification)
+ {
+ $info = $notification->getNotificationInfo();
+ $idSites = $info['idSites'];
+ $reports = & $notification->getNotificationObject();
+
+ // 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('Goals_ColumnRevenue')
+ );
+
+ $conversionReportMetrics = array(
+ 'nb_conversions' => Piwik_Translate('Goals_ColumnConversions')
);
- }
-
- protected $ecommerceReports = array(
- array('Goals_ProductSKU', 'Goals', 'getItemsSku'),
- array('Goals_ProductName', 'Goals', 'getItemsName'),
- array('Goals_ProductCategory', 'Goals', 'getItemsCategory')
- );
-
- 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'),
- );
- }
-
- static public function getReportsWithGoalMetrics()
- {
- $dimensions = array();
- Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $dimensions);
- $dimensionsByGroup = array();
- foreach($dimensions as $dimension)
- {
- $group = $dimension['category'];
- unset($dimension['category']);
- $dimensionsByGroup[$group][] = $dimension;
- }
- return $dimensionsByGroup;
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getJsFiles( $notification )
- {
- $jsFiles = &$notification->getNotificationObject();
- $jsFiles[] = "plugins/Goals/templates/GoalForm.js";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function getCssFiles( $notification )
- {
- $cssFiles = &$notification->getNotificationObject();
- $cssFiles[] = "plugins/Goals/templates/goals.css";
- }
-
- /**
- * @param Piwik_Event_Notification $notification notification object
- */
- function fetchGoalsFromDb($notification)
- {
- $idsite = $notification->getNotificationInfo();
-
- // add the 'goal' entry in the website array
- $array =& $notification->getNotificationObject();
- $array['goals'] = Piwik_Goals_API::getInstance()->getGoals($idsite);
- }
-
- function addWidgets()
- {
- $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
-
- // Ecommerce widgets
- $site = new Piwik_Site($idSite);
- if($site->isEcommerceEnabled())
- {
- Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceOverview', 'Goals', 'widgetGoalReport', array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER));
- Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceLog', 'Goals', 'getEcommerceLog');
- foreach($this->ecommerceReports as $widget)
- {
- Piwik_AddWidget('Goals_Ecommerce', $widget[0], $widget[1], $widget[2]);
- }
- }
-
- // Goals widgets
- Piwik_AddWidget('Goals_Goals', 'Goals_GoalsOverview', 'Goals', 'widgetGoalsOverview');
- $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
- if(count($goals) > 0)
- {
- foreach($goals as $goal)
- {
- Piwik_AddWidget('Goals_Goals', Piwik_Common::sanitizeInputValue($goal['name']), 'Goals', 'widgetGoalReport', array('idGoal' => $goal['idgoal']));
- }
- }
- }
-
- protected function getGoalCategoryName($idSite)
- {
- $site = new Piwik_Site($idSite);
- return $site->isEcommerceEnabled() ? 'Goals_EcommerceAndGoalsMenu' : 'Goals_Goals';
- }
-
-
- function addMenus()
- {
- $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
- $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
- $mainGoalMenu = $this->getGoalCategoryName($idSite);
- $site = new Piwik_Site($idSite);
- if(count($goals)==0)
- {
- Piwik_AddMenu($mainGoalMenu, '', array(
- 'module' => 'Goals',
- 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'addNewGoal'),
- 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
- true,
- 25);
- if($site->isEcommerceEnabled())
- {
- Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
- }
- Piwik_AddMenu($mainGoalMenu, 'Goals_AddNewGoal', array('module' => 'Goals', 'action' => 'addNewGoal'));
- }
- else
- {
- Piwik_AddMenu($mainGoalMenu, '', array(
- 'module' => 'Goals',
- 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'index'),
- 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
- true,
- 25);
-
- if($site->isEcommerceEnabled())
- {
- Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
- }
- Piwik_AddMenu($mainGoalMenu, 'Goals_GoalsOverview', array('module' => 'Goals', 'action' => 'index'), true, 2);
- foreach($goals as $goal)
- {
- Piwik_AddMenu($mainGoalMenu, str_replace('%', '%%', Piwik_TranslationWriter::clean($goal['name'])), array('module' => 'Goals', 'action' => 'goalReport', 'idGoal' => $goal['idgoal']));
- }
- }
- }
-
- /**
- * @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;
- }
-
- /**
- * Hooks on Period archiving.
- * Sums up Goal conversions stats, and processes overall conversion rate
- *
- * @param Piwik_Event_Notification $notification
- * @return void
- */
- function archivePeriod($notification )
- {
- /**
- * @var Piwik_ArchiveProcessing
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- /*
- * Archive Ecommerce Items
- */
- if($this->shouldArchiveEcommerceItems($archiveProcessing))
- {
- $dataTableToSum = $this->dimensions;
- foreach($this->dimensions as $recordName)
- {
- $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
- }
- $archiveProcessing->archiveDataTable($dataTableToSum);
- }
-
- /*
- * Archive General Goal metrics
- */
- $goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite);
-
- //Ecommerce
- $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_ORDER;
- $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_CART; //bug here if idgoal=1
- // Overall goal metrics
- $goalIdsToSum[] = false;
-
- $fieldsToSum = array();
- foreach($goalIdsToSum as $goalId)
- {
- $metricsToSum = Piwik_Goals::getGoalColumns($goalId);
- unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
- foreach($metricsToSum as $metricName)
- {
- $fieldsToSum[] = self::getRecordName($metricName, $goalId);
- }
- }
- $records = $archiveProcessing->archiveNumericValuesSum($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, $archiveProcessing);
- $archiveProcessing->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
-
- // sum up the visits to conversion data table & the days to conversion data table
- $archiveProcessing->archiveDataTable(array(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
- }
-
- // sum up goal overview reports
- $archiveProcessing->archiveDataTable(array(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
- }
-
- static public function getGoalColumns($idGoal)
- {
- $columns = array(
- 'nb_conversions',
- 'nb_visits_converted',
- 'conversion_rate',
- 'revenue',
- );
- if($idGoal === false)
- {
- return $columns;
- }
- // Orders
- if($idGoal === Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $columns = array_merge($columns, array(
- 'revenue_subtotal',
- 'revenue_tax',
- 'revenue_shipping',
- 'revenue_discount',
- ));
- }
- // Abandoned carts & orders
- if($idGoal <= Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $columns[] = 'items';
- }
- return $columns;
- }
-
- /**
- * Hooks on the Daily archiving.
- * Will process Goal stats overall and for each Goal.
- * Also processes the New VS Returning visitors conversion stats.
- *
- * @param Piwik_Event_Notification $notification
- * @return void
- */
- function archiveDay( $notification )
- {
- /**
- * @var Piwik_ArchiveProcessing_Day
- */
- $archiveProcessing = $notification->getNotificationObject();
-
- if(!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
-
- $this->archiveGeneralGoalMetrics($archiveProcessing);
- $this->archiveEcommerceItems($archiveProcessing);
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- function archiveGeneralGoalMetrics($archiveProcessing)
- {
- // extra aggregate selects for the visits to conversion report
- $visitToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_count_visits', self::$visitCountRanges, 'log_conversion', 'vcv');
-
- // extra aggregate selects for the days to conversion report
- $daysToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
- 'visitor_days_since_first', self::$daysToConvRanges, 'log_conversion', 'vdsf');
-
- $query = $archiveProcessing->queryConversionsByDimension(
- array(), '', array_merge($visitToConvExtraCols, $daysToConvExtraCols));
-
- if($query === false) { return; }
-
- $goals = array();
- $visitsToConvReport = array();
- $daysToConvReport = array();
-
- // Get a standard empty goal row
- $overall = $archiveProcessing->getNewGoalRow( $idGoal = 1);
- while($row = $query->fetch() )
- {
- $idgoal = $row['idgoal'];
-
- if(!isset($goals[$idgoal]))
- {
- $goals[$idgoal] = $archiveProcessing->getNewGoalRow($idgoal);
-
- $visitsToConvReport[$idgoal] = new Piwik_DataTable();
- $daysToConvReport[$idgoal] = new Piwik_DataTable();
- }
- $archiveProcessing->updateGoalStats($row, $goals[$idgoal]);
-
- // We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits
- // since it is a "negative conversion"
- if($idgoal != Piwik_Tracker_GoalManager::IDGOAL_CART)
- {
- $archiveProcessing->updateGoalStats($row, $overall);
- }
-
- // map the goal + visit number of a visitor with the # of conversions that happened on that visit
- $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vcv');
- $visitsToConvReport[$idgoal]->addDataTable($table);
-
- // map the goal + day number of a visit with the # of conversion that happened on that day
- $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vdsf');
- $daysToConvReport[$idgoal]->addDataTable($table);
- }
-
- // these data tables hold reports for every goal of a site
- $visitsToConvOverview = new Piwik_DataTable();
- $daysToConvOverview = new Piwik_DataTable();
-
- // Stats by goal, for all visitors
- foreach($goals as $idgoal => $values)
- {
- foreach($values as $metricId => $value)
- {
- $metricName = Piwik_Archive::$mappingFromIdToNameGoal[$metricId];
- $recordName = self::getRecordName($metricName, $idgoal);
- $archiveProcessing->insertNumericRecord($recordName, $value);
- }
- $conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED], $archiveProcessing);
- $recordName = self::getRecordName('conversion_rate', $idgoal);
- $archiveProcessing->insertNumericRecord($recordName, $conversion_rate);
-
- // if the goal is not a special goal (like ecommerce) add it to the overview report
- if ($idgoal !== Piwik_Tracker_GoalManager::IDGOAL_CART &&
- $idgoal !== Piwik_Tracker_GoalManager::IDGOAL_ORDER)
- {
- $visitsToConvOverview->addDataTable($visitsToConvReport[$idgoal]);
- $daysToConvOverview->addDataTable($daysToConvReport[$idgoal]);
- }
-
- // visit count until conversion stats
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $idgoal),
- $visitsToConvReport[$idgoal]->getSerialized());
-
- // day count until conversion stats
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $idgoal),
- $daysToConvReport[$idgoal]->getSerialized());
- }
-
- // archive overview reports
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::VISITS_UNTIL_RECORD_NAME), $visitsToConvOverview->getSerialized());
- $archiveProcessing->insertBlobRecord(
- self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME), $daysToConvOverview->getSerialized());
-
- // Stats for all goals
- $totalAllGoals = array(
- self::getRecordName('conversion_rate') => $this->getConversionRate($archiveProcessing->getNumberOfVisitsConverted(), $archiveProcessing),
- self::getRecordName('nb_conversions') => $overall[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS],
- self::getRecordName('nb_visits_converted') => $archiveProcessing->getNumberOfVisitsConverted(),
- self::getRecordName('revenue') => $overall[Piwik_Archive::INDEX_GOAL_REVENUE],
- );
- foreach($totalAllGoals as $recordName => $value)
- {
- $archiveProcessing->insertNumericRecord($recordName, $value);
- }
- }
-
- protected $dimensions = array(
- 'idaction_sku' => 'Goals_ItemsSku',
- 'idaction_name' => 'Goals_ItemsName',
- 'idaction_category' => 'Goals_ItemsCategory'
- );
-
- protected function shouldArchiveEcommerceItems($archiveProcessing)
- {
- // Per item doesn't support segment
- // Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request
- // event if it did support segment
- // (if this is implented, we should have shouldProcessReportsForPlugin() support partial archiving based on which metric is requested)
- if(!$archiveProcessing->getSegment()->isEmpty())
- {
- return false;
- }
- return true;
- }
-
- /**
- * @param Piwik_ArchiveProcessing_Day $archiveProcessing
- */
- function archiveEcommerceItems($archiveProcessing)
- {
- if(!$this->shouldArchiveEcommerceItems($archiveProcessing))
- {
- return false;
- }
- $items = array();
-
- $dimensionsToQuery = $this->dimensions;
- $dimensionsToQuery['idaction_category2'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category3'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category4'] = 'AdditionalCategory';
- $dimensionsToQuery['idaction_category5'] = 'AdditionalCategory';
-
- foreach($dimensionsToQuery as $dimension => $recordName)
- {
- $query = $archiveProcessing->queryEcommerceItems($dimension);
- if($query == false) { continue; }
-
- while($row = $query->fetch())
- {
- $label = $row['label'];
- $ecommerceType = $row['ecommerceType'];
-
- if(empty($label))
- {
- // idaction==0 case:
- // If we are querying any optional category, we do not include idaction=0
- // Otherwise we over-report in the Product Categories report
- if($recordName == 'AdditionalCategory') {
- continue;
- }
- // Product Name/Category not defined"
- if(class_exists('Piwik_CustomVariables')) {
- $label = Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED;
- } else {
- $label = "Value not defined";
- }
- }
- // For carts, idorder = 0. To count abandoned carts, we must count visits with an abandoned cart
- if($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART)
- {
- $row[Piwik_Archive::INDEX_ECOMMERCE_ORDERS] = $row[Piwik_Archive::INDEX_NB_VISITS];
- }
- unset($row[Piwik_Archive::INDEX_NB_VISITS]);
- unset($row['label']);
- unset($row['ecommerceType']);
-
- $columnsToRound = array(
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE,
- Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED,
- );
- foreach($columnsToRound as $column)
- {
- if(isset($row[$column])
- && $row[$column] == round($row[$column]))
- {
- $row[$column] = round($row[$column]);
- }
- }
- $items[$dimension][$ecommerceType][$label] = $row;
- }
- }
-
- foreach($this->dimensions as $dimension => $recordName)
- {
- foreach(array(Piwik_Tracker_GoalManager::IDGOAL_CART, Piwik_Tracker_GoalManager::IDGOAL_ORDER) as $ecommerceType)
- {
- if(!isset($items[$dimension][$ecommerceType]))
- {
- continue;
- }
- $recordNameInsert = $recordName;
- if($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART)
- {
- $recordNameInsert = self::getItemRecordNameAbandonedCart($recordName);
- }
- $table = $archiveProcessing->getDataTableFromArray($items[$dimension][$ecommerceType]);
-
- // For "category" report, we aggregate all 5 category queries into one datatable
- if($dimension == 'idaction_category')
- {
- foreach(array('idaction_category2','idaction_category3','idaction_category4','idaction_category5') as $categoryToSum)
- {
- if(!empty($items[$categoryToSum][$ecommerceType]))
- {
- $tableToSum = $archiveProcessing->getDataTableFromArray($items[$categoryToSum][$ecommerceType]);
- $table->addDataTable($tableToSum);
- }
- }
- }
- $archiveProcessing->insertBlobRecord($recordNameInsert, $table->getSerialized());
- }
- }
- }
-
- static public function getItemRecordNameAbandonedCart($recordName)
- {
- return $recordName . '_Cart';
- }
-
- function getConversionRate($count, $archiveProcessing)
- {
- $visits = $archiveProcessing->getNumberOfVisits();
- return round(100 * $count / $visits, Piwik_Tracker_GoalManager::REVENUE_PRECISION);
- }
-
- /**
- * 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.
- *
- * @param Piwik_Event_Notification $notification notification object
- */
- function getActualReportsWithGoalMetrics( $notification )
- {
- $dimensions =& $notification->getNotificationObject();
- $dimensions = array_merge($dimensions, array(
- array( 'category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
- 'module' => 'Goals',
- 'action' => 'getVisitsUntilConversion'
- ),
- array( 'category' => Piwik_Translate('General_Visit'),
- 'name' => Piwik_Translate('Goals_DaysToConv'),
- 'module' => 'Goals',
- 'action' => 'getDaysToConversion'
- )
- ));
- }
+
+ // 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 = Piwik_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
+ $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 Piwik_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_Archive::LABEL_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_Archive::LABEL_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_Archive::LABEL_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('Goals_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_Archive::LABEL_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_Archive::LABEL_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_Archive::LABEL_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']);
+
+ /*
+ * Add the metricsGoal and processedMetricsGoal entry
+ * to all reports that have Goal segmentation
+ */
+ $reportsWithGoals = array();
+ Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $reportsWithGoals);
+ 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;
+ }
+ }
+ }
+
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ public function getSegmentsMetadata($notification)
+ {
+ $segments =& $notification->getNotificationObject();
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Visit',
+ 'name' => 'General_VisitConvertedGoalId',
+ 'segment' => 'visitConvertedGoalId',
+ 'sqlSegment' => 'log_conversion.idgoal',
+ 'acceptedValues' => '1, 2, 3, etc.',
+ );
+ }
+
+ protected $ecommerceReports = array(
+ array('Goals_ProductSKU', 'Goals', 'getItemsSku'),
+ array('Goals_ProductName', 'Goals', 'getItemsName'),
+ array('Goals_ProductCategory', 'Goals', 'getItemsCategory')
+ );
+
+ 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'),
+ );
+ }
+
+ static public function getReportsWithGoalMetrics()
+ {
+ $dimensions = array();
+ Piwik_PostEvent('Goals.getReportsWithGoalMetrics', $dimensions);
+ $dimensionsByGroup = array();
+ foreach ($dimensions as $dimension) {
+ $group = $dimension['category'];
+ unset($dimension['category']);
+ $dimensionsByGroup[$group][] = $dimension;
+ }
+ return $dimensionsByGroup;
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getJsFiles($notification)
+ {
+ $jsFiles = & $notification->getNotificationObject();
+ $jsFiles[] = "plugins/Goals/templates/GoalForm.js";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getCssFiles($notification)
+ {
+ $cssFiles = & $notification->getNotificationObject();
+ $cssFiles[] = "plugins/Goals/templates/goals.css";
+ }
+
+ /**
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function fetchGoalsFromDb($notification)
+ {
+ $idsite = $notification->getNotificationInfo();
+
+ // add the 'goal' entry in the website array
+ $array =& $notification->getNotificationObject();
+ $array['goals'] = Piwik_Goals_API::getInstance()->getGoals($idsite);
+ }
+
+ function addWidgets()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+
+ // Ecommerce widgets
+ $site = new Piwik_Site($idSite);
+ if ($site->isEcommerceEnabled()) {
+ Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceOverview', 'Goals', 'widgetGoalReport', array('idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER));
+ Piwik_AddWidget('Goals_Ecommerce', 'Goals_EcommerceLog', 'Goals', 'getEcommerceLog');
+ foreach ($this->ecommerceReports as $widget) {
+ Piwik_AddWidget('Goals_Ecommerce', $widget[0], $widget[1], $widget[2]);
+ }
+ }
+
+ // Goals widgets
+ Piwik_AddWidget('Goals_Goals', 'Goals_GoalsOverview', 'Goals', 'widgetGoalsOverview');
+ $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
+ if (count($goals) > 0) {
+ foreach ($goals as $goal) {
+ Piwik_AddWidget('Goals_Goals', Piwik_Common::sanitizeInputValue($goal['name']), 'Goals', 'widgetGoalReport', array('idGoal' => $goal['idgoal']));
+ }
+ }
+ }
+
+ protected function getGoalCategoryName($idSite)
+ {
+ $site = new Piwik_Site($idSite);
+ return $site->isEcommerceEnabled() ? 'Goals_EcommerceAndGoalsMenu' : 'Goals_Goals';
+ }
+
+
+ function addMenus()
+ {
+ $idSite = Piwik_Common::getRequestVar('idSite', null, 'int');
+ $goals = Piwik_Goals_API::getInstance()->getGoals($idSite);
+ $mainGoalMenu = $this->getGoalCategoryName($idSite);
+ $site = new Piwik_Site($idSite);
+ if (count($goals) == 0) {
+ Piwik_AddMenu($mainGoalMenu, '', array(
+ 'module' => 'Goals',
+ 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'addNewGoal'),
+ 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
+ true,
+ 25);
+ if ($site->isEcommerceEnabled()) {
+ Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
+ }
+ Piwik_AddMenu($mainGoalMenu, 'Goals_AddNewGoal', array('module' => 'Goals', 'action' => 'addNewGoal'));
+ } else {
+ Piwik_AddMenu($mainGoalMenu, '', array(
+ 'module' => 'Goals',
+ 'action' => ($site->isEcommerceEnabled() ? 'ecommerceReport' : 'index'),
+ 'idGoal' => ($site->isEcommerceEnabled() ? Piwik_Archive::LABEL_ECOMMERCE_ORDER : null)),
+ true,
+ 25);
+
+ if ($site->isEcommerceEnabled()) {
+ Piwik_AddMenu($mainGoalMenu, 'Goals_Ecommerce', array('module' => 'Goals', 'action' => 'ecommerceReport', 'idGoal' => Piwik_Archive::LABEL_ECOMMERCE_ORDER), true, 1);
+ }
+ Piwik_AddMenu($mainGoalMenu, 'Goals_GoalsOverview', array('module' => 'Goals', 'action' => 'index'), true, 2);
+ foreach ($goals as $goal) {
+ Piwik_AddMenu($mainGoalMenu, str_replace('%', '%%', Piwik_TranslationWriter::clean($goal['name'])), array('module' => 'Goals', 'action' => 'goalReport', 'idGoal' => $goal['idgoal']));
+ }
+ }
+ }
+
+ /**
+ * @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;
+ }
+
+ /**
+ * Hooks on Period archiving.
+ * Sums up Goal conversions stats, and processes overall conversion rate
+ *
+ * @param Piwik_Event_Notification $notification
+ * @return void
+ */
+ function archivePeriod($notification)
+ {
+ /**
+ * @var Piwik_ArchiveProcessing
+ */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ /*
+ * Archive Ecommerce Items
+ */
+ if ($this->shouldArchiveEcommerceItems($archiveProcessing)) {
+ $dataTableToSum = $this->dimensions;
+ foreach ($this->dimensions as $recordName) {
+ $dataTableToSum[] = self::getItemRecordNameAbandonedCart($recordName);
+ }
+ $archiveProcessing->archiveDataTable($dataTableToSum);
+ }
+
+ /*
+ * Archive General Goal metrics
+ */
+ $goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite);
+
+ //Ecommerce
+ $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_ORDER;
+ $goalIdsToSum[] = Piwik_Tracker_GoalManager::IDGOAL_CART; //bug here if idgoal=1
+ // Overall goal metrics
+ $goalIdsToSum[] = false;
+
+ $fieldsToSum = array();
+ foreach ($goalIdsToSum as $goalId) {
+ $metricsToSum = Piwik_Goals::getGoalColumns($goalId);
+ unset($metricsToSum[array_search('conversion_rate', $metricsToSum)]);
+ foreach ($metricsToSum as $metricName) {
+ $fieldsToSum[] = self::getRecordName($metricName, $goalId);
+ }
+ }
+ $records = $archiveProcessing->archiveNumericValuesSum($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, $archiveProcessing);
+ $archiveProcessing->insertNumericRecord(self::getRecordName('conversion_rate', $goalId), $conversion_rate);
+
+ // sum up the visits to conversion data table & the days to conversion data table
+ $archiveProcessing->archiveDataTable(array(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $goalId),
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $goalId)));
+ }
+
+ // sum up goal overview reports
+ $archiveProcessing->archiveDataTable(array(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME),
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME)));
+ }
+
+ static public function getGoalColumns($idGoal)
+ {
+ $columns = array(
+ 'nb_conversions',
+ 'nb_visits_converted',
+ 'conversion_rate',
+ 'revenue',
+ );
+ if ($idGoal === false) {
+ return $columns;
+ }
+ // Orders
+ if ($idGoal === Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $columns = array_merge($columns, array(
+ 'revenue_subtotal',
+ 'revenue_tax',
+ 'revenue_shipping',
+ 'revenue_discount',
+ ));
+ }
+ // Abandoned carts & orders
+ if ($idGoal <= Piwik_Tracker_GoalManager::IDGOAL_ORDER) {
+ $columns[] = 'items';
+ }
+ return $columns;
+ }
+
+ /**
+ * Hooks on the Daily archiving.
+ * Will process Goal stats overall and for each Goal.
+ * Also processes the New VS Returning visitors conversion stats.
+ *
+ * @param Piwik_Event_Notification $notification
+ * @return void
+ */
+ function archiveDay($notification)
+ {
+ /**
+ * @var Piwik_ArchiveProcessing_Day
+ */
+ $archiveProcessing = $notification->getNotificationObject();
+
+ if (!$archiveProcessing->shouldProcessReportsForPlugin($this->getPluginName())) return;
+
+ $this->archiveGeneralGoalMetrics($archiveProcessing);
+ $this->archiveEcommerceItems($archiveProcessing);
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ */
+ function archiveGeneralGoalMetrics($archiveProcessing)
+ {
+ // extra aggregate selects for the visits to conversion report
+ $visitToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visitor_count_visits', self::$visitCountRanges, 'log_conversion', 'vcv');
+
+ // extra aggregate selects for the days to conversion report
+ $daysToConvExtraCols = Piwik_ArchiveProcessing_Day::buildReduceByRangeSelect(
+ 'visitor_days_since_first', self::$daysToConvRanges, 'log_conversion', 'vdsf');
+
+ $query = $archiveProcessing->queryConversionsByDimension(
+ array(), '', array_merge($visitToConvExtraCols, $daysToConvExtraCols));
+
+ if ($query === false) {
+ return;
+ }
+
+ $goals = array();
+ $visitsToConvReport = array();
+ $daysToConvReport = array();
+
+ // Get a standard empty goal row
+ $overall = $archiveProcessing->getNewGoalRow($idGoal = 1);
+ while ($row = $query->fetch()) {
+ $idgoal = $row['idgoal'];
+
+ if (!isset($goals[$idgoal])) {
+ $goals[$idgoal] = $archiveProcessing->getNewGoalRow($idgoal);
+
+ $visitsToConvReport[$idgoal] = new Piwik_DataTable();
+ $daysToConvReport[$idgoal] = new Piwik_DataTable();
+ }
+ $archiveProcessing->updateGoalStats($row, $goals[$idgoal]);
+
+ // We don't want to sum Abandoned cart metrics in the overall revenue/conversions/converted visits
+ // since it is a "negative conversion"
+ if ($idgoal != Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $archiveProcessing->updateGoalStats($row, $overall);
+ }
+
+ // map the goal + visit number of a visitor with the # of conversions that happened on that visit
+ $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vcv');
+ $visitsToConvReport[$idgoal]->addDataTable($table);
+
+ // map the goal + day number of a visit with the # of conversion that happened on that day
+ $table = $archiveProcessing->getSimpleDataTableFromRow($row, Piwik_Archive::INDEX_NB_CONVERSIONS, 'vdsf');
+ $daysToConvReport[$idgoal]->addDataTable($table);
+ }
+
+ // these data tables hold reports for every goal of a site
+ $visitsToConvOverview = new Piwik_DataTable();
+ $daysToConvOverview = new Piwik_DataTable();
+
+ // Stats by goal, for all visitors
+ foreach ($goals as $idgoal => $values) {
+ foreach ($values as $metricId => $value) {
+ $metricName = Piwik_Archive::$mappingFromIdToNameGoal[$metricId];
+ $recordName = self::getRecordName($metricName, $idgoal);
+ $archiveProcessing->insertNumericRecord($recordName, $value);
+ }
+ $conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED], $archiveProcessing);
+ $recordName = self::getRecordName('conversion_rate', $idgoal);
+ $archiveProcessing->insertNumericRecord($recordName, $conversion_rate);
+
+ // if the goal is not a special goal (like ecommerce) add it to the overview report
+ if ($idgoal !== Piwik_Tracker_GoalManager::IDGOAL_CART &&
+ $idgoal !== Piwik_Tracker_GoalManager::IDGOAL_ORDER
+ ) {
+ $visitsToConvOverview->addDataTable($visitsToConvReport[$idgoal]);
+ $daysToConvOverview->addDataTable($daysToConvReport[$idgoal]);
+ }
+
+ // visit count until conversion stats
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME, $idgoal),
+ $visitsToConvReport[$idgoal]->getSerialized());
+
+ // day count until conversion stats
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME, $idgoal),
+ $daysToConvReport[$idgoal]->getSerialized());
+ }
+
+ // archive overview reports
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::VISITS_UNTIL_RECORD_NAME), $visitsToConvOverview->getSerialized());
+ $archiveProcessing->insertBlobRecord(
+ self::getRecordName(self::DAYS_UNTIL_CONV_RECORD_NAME), $daysToConvOverview->getSerialized());
+
+ // Stats for all goals
+ $totalAllGoals = array(
+ self::getRecordName('conversion_rate') => $this->getConversionRate($archiveProcessing->getNumberOfVisitsConverted(), $archiveProcessing),
+ self::getRecordName('nb_conversions') => $overall[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS],
+ self::getRecordName('nb_visits_converted') => $archiveProcessing->getNumberOfVisitsConverted(),
+ self::getRecordName('revenue') => $overall[Piwik_Archive::INDEX_GOAL_REVENUE],
+ );
+ foreach ($totalAllGoals as $recordName => $value) {
+ $archiveProcessing->insertNumericRecord($recordName, $value);
+ }
+ }
+
+ protected $dimensions = array(
+ 'idaction_sku' => 'Goals_ItemsSku',
+ 'idaction_name' => 'Goals_ItemsName',
+ 'idaction_category' => 'Goals_ItemsCategory'
+ );
+
+ protected function shouldArchiveEcommerceItems($archiveProcessing)
+ {
+ // Per item doesn't support segment
+ // Also, when querying Goal metrics for visitorType==returning, we wouldnt want to trigger an extra request
+ // event if it did support segment
+ // (if this is implented, we should have shouldProcessReportsForPlugin() support partial archiving based on which metric is requested)
+ if (!$archiveProcessing->getSegment()->isEmpty()) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @param Piwik_ArchiveProcessing_Day $archiveProcessing
+ */
+ function archiveEcommerceItems($archiveProcessing)
+ {
+ if (!$this->shouldArchiveEcommerceItems($archiveProcessing)) {
+ return false;
+ }
+ $items = array();
+
+ $dimensionsToQuery = $this->dimensions;
+ $dimensionsToQuery['idaction_category2'] = 'AdditionalCategory';
+ $dimensionsToQuery['idaction_category3'] = 'AdditionalCategory';
+ $dimensionsToQuery['idaction_category4'] = 'AdditionalCategory';
+ $dimensionsToQuery['idaction_category5'] = 'AdditionalCategory';
+
+ foreach ($dimensionsToQuery as $dimension => $recordName) {
+ $query = $archiveProcessing->queryEcommerceItems($dimension);
+ if ($query == false) {
+ continue;
+ }
+
+ while ($row = $query->fetch()) {
+ $label = $row['label'];
+ $ecommerceType = $row['ecommerceType'];
+
+ if (empty($label)) {
+ // idaction==0 case:
+ // If we are querying any optional category, we do not include idaction=0
+ // Otherwise we over-report in the Product Categories report
+ if ($recordName == 'AdditionalCategory') {
+ continue;
+ }
+ // Product Name/Category not defined"
+ if (class_exists('Piwik_CustomVariables')) {
+ $label = Piwik_CustomVariables::LABEL_CUSTOM_VALUE_NOT_DEFINED;
+ } else {
+ $label = "Value not defined";
+ }
+ }
+ // For carts, idorder = 0. To count abandoned carts, we must count visits with an abandoned cart
+ if ($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $row[Piwik_Archive::INDEX_ECOMMERCE_ORDERS] = $row[Piwik_Archive::INDEX_NB_VISITS];
+ }
+ unset($row[Piwik_Archive::INDEX_NB_VISITS]);
+ unset($row['label']);
+ unset($row['ecommerceType']);
+
+ $columnsToRound = array(
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE,
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY,
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE,
+ Piwik_Archive::INDEX_ECOMMERCE_ITEM_PRICE_VIEWED,
+ );
+ foreach ($columnsToRound as $column) {
+ if (isset($row[$column])
+ && $row[$column] == round($row[$column])
+ ) {
+ $row[$column] = round($row[$column]);
+ }
+ }
+ $items[$dimension][$ecommerceType][$label] = $row;
+ }
+ }
+
+ foreach ($this->dimensions as $dimension => $recordName) {
+ foreach (array(Piwik_Tracker_GoalManager::IDGOAL_CART, Piwik_Tracker_GoalManager::IDGOAL_ORDER) as $ecommerceType) {
+ if (!isset($items[$dimension][$ecommerceType])) {
+ continue;
+ }
+ $recordNameInsert = $recordName;
+ if ($ecommerceType == Piwik_Tracker_GoalManager::IDGOAL_CART) {
+ $recordNameInsert = self::getItemRecordNameAbandonedCart($recordName);
+ }
+ $table = $archiveProcessing->getDataTableFromArray($items[$dimension][$ecommerceType]);
+
+ // For "category" report, we aggregate all 5 category queries into one datatable
+ if ($dimension == 'idaction_category') {
+ foreach (array('idaction_category2', 'idaction_category3', 'idaction_category4', 'idaction_category5') as $categoryToSum) {
+ if (!empty($items[$categoryToSum][$ecommerceType])) {
+ $tableToSum = $archiveProcessing->getDataTableFromArray($items[$categoryToSum][$ecommerceType]);
+ $table->addDataTable($tableToSum);
+ }
+ }
+ }
+ $archiveProcessing->insertBlobRecord($recordNameInsert, $table->getSerialized());
+ }
+ }
+ }
+
+ static public function getItemRecordNameAbandonedCart($recordName)
+ {
+ return $recordName . '_Cart';
+ }
+
+ function getConversionRate($count, $archiveProcessing)
+ {
+ $visits = $archiveProcessing->getNumberOfVisits();
+ return round(100 * $count / $visits, Piwik_Tracker_GoalManager::REVENUE_PRECISION);
+ }
+
+ /**
+ * 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.
+ *
+ * @param Piwik_Event_Notification $notification notification object
+ */
+ function getActualReportsWithGoalMetrics($notification)
+ {
+ $dimensions =& $notification->getNotificationObject();
+ $dimensions = array_merge($dimensions, array(
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('Goals_VisitsUntilConv'),
+ 'module' => 'Goals',
+ 'action' => 'getVisitsUntilConversion'
+ ),
+ array('category' => Piwik_Translate('General_Visit'),
+ 'name' => Piwik_Translate('Goals_DaysToConv'),
+ 'module' => 'Goals',
+ 'action' => 'getDaysToConversion'
+ )
+ ));
+ }
}