diff options
author | matt <matt@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2008-12-26 17:37:46 +0300 |
---|---|---|
committer | matt <matt@59fd770c-687e-43c8-a1e3-f5a4ff64c105> | 2008-12-26 17:37:46 +0300 |
commit | d389561c0ccb76536d18239c9b7f81cce3d02fc1 (patch) | |
tree | 2bac760977afe8e7f3ae7890d7cd286e37109d88 /plugins/Goals | |
parent | 47f4495a2adba4cbce5d1c3d3510ce26f8650f69 (diff) | |
parent | b86d75c79c557a7c89899939055ee7306f40d28a (diff) |
Diffstat (limited to 'plugins/Goals')
-rw-r--r-- | plugins/Goals/API.php | 227 | ||||
-rw-r--r-- | plugins/Goals/Controller.php | 150 | ||||
-rw-r--r-- | plugins/Goals/Goals.php | 206 | ||||
-rw-r--r-- | plugins/Goals/templates/GoalForm.js | 124 | ||||
-rw-r--r-- | plugins/Goals/templates/add_edit_goal.tpl | 49 | ||||
-rw-r--r-- | plugins/Goals/templates/add_new_goal.tpl | 8 | ||||
-rw-r--r-- | plugins/Goals/templates/form_add_goal.tpl | 67 | ||||
-rw-r--r-- | plugins/Goals/templates/list_goal_edit.tpl | 22 | ||||
-rw-r--r-- | plugins/Goals/templates/list_top_segment.tpl | 5 | ||||
-rw-r--r-- | plugins/Goals/templates/overview.tpl | 27 | ||||
-rw-r--r-- | plugins/Goals/templates/release_notes.tpl | 20 | ||||
-rw-r--r-- | plugins/Goals/templates/single_goal.tpl | 37 | ||||
-rw-r--r-- | plugins/Goals/templates/title_and_evolution_graph.tpl | 20 |
13 files changed, 962 insertions, 0 deletions
diff --git a/plugins/Goals/API.php b/plugins/Goals/API.php new file mode 100644 index 0000000000..5b1b849f01 --- /dev/null +++ b/plugins/Goals/API.php @@ -0,0 +1,227 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id$ + * + * @package Piwik_VisitsSummary + */ + +/** + * @package Piwik_Goals + */ +class Piwik_Goals_API +{ + static private $instance = null; + static public function getInstance() + { + if (self::$instance == null) + { + $c = __CLASS__; + self::$instance = new $c(); + } + return self::$instance; + } + + static public function getGoals( $idSite ) + { + $goals = Zend_Registry::get('db')->fetchAll("SELECT * + FROM ".Piwik_Common::prefixTable('goal')." + WHERE idsite = ? + AND deleted = 0", $idSite); + $cleanedGoals = array(); + foreach($goals as &$goal) + { + unset($goal['idsite']); + $cleanedGoals[$goal['idgoal']] = $goal; + } + return $cleanedGoals; + } + + public function addGoal( $idSite, $name, $matchAttribute, $pattern, $patternType, $caseSensitive, $revenue ) + { + Piwik::checkUserHasAdminAccess($idSite); + // save in db + $db = Zend_Registry::get('db'); + $idGoal = $db->fetchOne("SELECT max(idgoal) + 1 + FROM ".Piwik::prefixTable('goal')." + WHERE idsite = ?", $idSite); + if($idGoal == false) + { + $idGoal = 1; + } + self::checkPatternIsValid($patternType, $pattern); + $name = self::checkName($name); + $pattern = self::checkPattern($pattern); + $db->insert(Piwik::prefixTable('goal'), + array( + 'idsite' => $idSite, + 'idgoal' => $idGoal, + 'name' => $name, + 'match_attribute' => $matchAttribute, + 'pattern' => $pattern, + 'pattern_type' => $patternType, + 'case_sensitive' => $caseSensitive, + 'revenue' => $revenue, + 'deleted' => 0, + )); + Piwik_Common::regenerateCacheWebsiteAttributes($idSite); + return $idGoal; + } + + public function updateGoal( $idSite, $idGoal, $name, $matchAttribute, $pattern, $patternType, $caseSensitive, $revenue ) + { + Piwik::checkUserHasAdminAccess($idSite); + $name = self::checkName($name); + $pattern = self::checkPattern($pattern); + self::checkPatternIsValid($patternType, $pattern); + Zend_Registry::get('db')->update( Piwik::prefixTable('goal'), + array( + 'name' => $name, + 'match_attribute' => $matchAttribute, + 'pattern' => $pattern, + 'pattern_type' => $patternType, + 'case_sensitive' => $caseSensitive, + 'revenue' => $revenue, + ), + "idsite = '$idSite' AND idgoal = '$idGoal'" + ); + Piwik_Common::regenerateCacheWebsiteAttributes($idSite); + } + + private function checkPatternIsValid($patternType, $pattern) + { + if($patternType == 'exact' + && substr($pattern, 0, 4) != 'http') + { + throw new Exception("If you choose 'exact match', the matching string must be a + URL starting with http:// or https://. For example, 'http://www.yourwebsite.com/newsletter/subscribed.html'."); + } + } + + private function checkName($name) + { + return urldecode($name); + } + + private function checkPattern($pattern) + { + return urldecode($pattern); + } + + public function deleteGoal( $idSite, $idGoal ) + { + Piwik::checkUserHasAdminAccess($idSite); + Zend_Registry::get('db')->query("UPDATE ".Piwik::prefixTable('goal')." + SET deleted = 1 + WHERE idsite = ? + AND idgoal = ?", + array($idSite, $idGoal)); + $db->query("DELETE FROM ".Piwik::prefixTable("log_conversion")." WHERE idgoal = ?", $idGoal); + Piwik_Common::regenerateCacheWebsiteAttributes($idSite); + } + +// public function getConversionsReturningVisitors( $idSite, $period, $date, $idGoal = false ) +// { +// +// } +// +// public function getConversionsNewVisitors( $idSite, $period, $date, $idGoal = false ) +// { +// +// } + + // TODO + public function getConversionRateReturningVisitors( $idSite, $period, $date, $idGoal = false ) + { + // visits converted for returning for all goals = call Frequency API + if($idGoal === false) + { + $request = new Piwik_API_Request("method=VisitFrequency.getConvertedVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original"); + $nbVisitsConvertedReturningVisitors = $request->process(); + } + // visits converted for returning = nb conversion for this goal + else + { + $nbVisitsConvertedReturningVisitors = $this->getNumeric($idSite, $period, $date, Piwik_Goals::getRecordName('nb_conversions', $idGoal, 1)); + } + // all returning visits + $request = new Piwik_API_Request("method=VisitFrequency.getVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original"); + $nbVisitsReturning = $request->process(); +// echo $nbVisitsConvertedReturningVisitors; +// echo "<br>". $nbVisitsReturning;exit; + return $this->getPercentage($nbVisitsConvertedReturningVisitors, $nbVisitsReturning); + } + + public function getConversionRateNewVisitors( $idSite, $period, $date, $idGoal = false ) + { + // new visits converted for all goals = nb visits converted - nb visits converted for returning + if($idGoal == false) + { + $request = new Piwik_API_Request("method=VisitsSummary.getVisitsConverted&idSite=$idSite&period=$period&date=$date&format=original"); + $convertedVisits = $request->process(); + $request = new Piwik_API_Request("method=VisitFrequency.getConvertedVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original"); + $convertedReturningVisits = $request->process(); + $convertedNewVisits = $convertedVisits - $convertedReturningVisits; + } + // new visits converted for a given goal = nb conversion for this goal for new visits + else + { + $convertedNewVisits = $this->getNumeric($idSite, $period, $date, Piwik_Goals::getRecordName('nb_conversions', $idGoal, 0)); + } + // all new visits = all visits - all returning visits + $request = new Piwik_API_Request("method=VisitFrequency.getVisitsReturning&idSite=$idSite&period=$period&date=$date&format=original"); + $nbVisitsReturning = $request->process(); + $request = new Piwik_API_Request("method=VisitsSummary.getVisits&idSite=$idSite&period=$period&date=$date&format=original"); + $nbVisits = $request->process(); + $newVisits = $nbVisits - $nbVisitsReturning; + return $this->getPercentage($convertedNewVisits, $newVisits); + } + + protected function getPercentage($a, $b) + { + if($b == 0) + { + return 0; + } + return round(100 * $a / $b, Piwik_Goals::ROUNDING_PRECISION); + } + + public function get( $idSite, $period, $date, $idGoal = false ) + { + Piwik::checkUserHasViewAccess( $idSite ); + $archive = Piwik_Archive::build($idSite, $period, $date ); + $toFetch = array( Piwik_Goals::getRecordName('nb_conversions', $idGoal), + Piwik_Goals::getRecordName('conversion_rate', $idGoal), + Piwik_Goals::getRecordName('revenue', $idGoal), + ); + $dataTable = $archive->getDataTableFromNumeric($toFetch); + return $dataTable; + } + + protected static function getNumeric( $idSite, $period, $date, $toFetch ) + { + Piwik::checkUserHasViewAccess( $idSite ); + $archive = Piwik_Archive::build($idSite, $period, $date ); + $dataTable = $archive->getNumeric($toFetch); + return $dataTable; + } + + public function getConversions( $idSite, $period, $date, $idGoal = false ) + { + return self::getNumeric( $idSite, $period, $date, Piwik_Goals::getRecordName('nb_conversions', $idGoal)); + } + + public function getConversionRate( $idSite, $period, $date, $idGoal = false ) + { + return self::getNumeric( $idSite, $period, $date, Piwik_Goals::getRecordName('conversion_rate', $idGoal)); + } + + public function getRevenue( $idSite, $period, $date, $idGoal = false ) + { + return self::getNumeric( $idSite, $period, $date, Piwik_Goals::getRecordName('revenue', $idGoal)); + } + +} diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php new file mode 100644 index 0000000000..935d616258 --- /dev/null +++ b/plugins/Goals/Controller.php @@ -0,0 +1,150 @@ +<?php +require_once "Goals/API.php"; + +class Piwik_Goals_Controller extends Piwik_Controller +{ + const CONVERSION_RATE_PRECISION = 1; + function goalReport() + { + $idGoal = Piwik_Common::getRequestVar('idGoal', null, 'int'); + $idSite = Piwik_Common::getRequestVar('idSite'); + $goals = Piwik_Goals_API::getGoals($idSite); + if(!isset($goals[$idGoal])) + { + throw new Exception("idgoal $idGoal not valid."); + } + $goalDefinition = $goals[$idGoal]; + + $view = new Piwik_View('Goals/templates/single_goal.tpl'); + $view->currency = Piwik::getCurrency(); + $goal = $this->getMetricsForGoal($idGoal); + foreach($goal as $name => $value) + { + $view->$name = $value; + } + $view->name = $goalDefinition['name']; + $view->title = $goalDefinition['name'] . ' - Conversions'; + $view->graphEvolution = $this->getLastNbConversionsGraph(true); + $view->nameGraphEvolution = 'GoalsgetLastNbConversionsGraph'; // must be the function name used above + + $columnNbConversions = 'goal_'.$idGoal.'_nb_conversions'; + $columnConversionRate = 'goal_'.$idGoal.'_conversion_rate'; + + $topSegmentsToLoad = array( + 'country' => 'UserCountry.getCountry', + 'keyword' => 'Referers.getKeywords', + 'website' => 'Referers.getWebsites', + ); + + $topSegments = array(); + foreach($topSegmentsToLoad as $segmentName => $apiMethod) + { + $request = new Piwik_API_Request("method=$apiMethod + &format=original + &filter_update_columns_when_show_all_goals=1 + &filter_sort_order=desc + &filter_sort_column=$columnNbConversions + &filter_limit=3"); + $datatable = $request->process(); + $topSegment = array(); + foreach($datatable->getRows() as $row) + { + $topSegment[] = array ( + 'name' => $row->getColumn('label'), + 'nb_conversions' => $row->getColumn($columnNbConversions), + 'conversion_rate' => $row->getColumn($columnConversionRate), + 'metadata' => $row->getMetadata(), + ); + } + $topSegments[$segmentName] = $topSegment; +// echo $datatable; + } + + $request = new Piwik_API_Request("method=Goals.getConversionRateReturningVisitors&format=original"); + $view->conversion_rate_returning = round( $request->process(), self::CONVERSION_RATE_PRECISION ); + $request = new Piwik_API_Request("method=Goals.getConversionRateNewVisitors&format=original"); + $view->conversion_rate_new = round( $request->process(), self::CONVERSION_RATE_PRECISION ); + + $view->topSegments = $topSegments; + echo $view->render(); + //todo next: nice legends for graphs + } + + protected function getMetricsForGoal($goalId) + { + $request = new Piwik_API_Request("method=Goals.get&format=original&idGoal=$goalId"); + $datatable = $request->process(); + return array ( + 'id' => $goalId, + 'nb_conversions' => $datatable->getRowFromLabel(Piwik_Goals::getRecordName('nb_conversions', $goalId))->getColumn('value'), + 'conversion_rate' => round($datatable->getRowFromLabel(Piwik_Goals::getRecordName('conversion_rate', $goalId))->getColumn('value'), 1), + 'revenue' => $datatable->getRowFromLabel(Piwik_Goals::getRecordName('revenue', $goalId))->getColumn('value'), + 'urlSparklineConversions' => $this->getUrlSparkline('getLastNbConversionsGraph', $goalId) . "&idGoal=".$goalId, + 'urlSparklineConversionRate' => $this->getUrlSparkline('getLastConversionRateGraph', $goalId) . "&idGoal=".$goalId, + 'urlSparklineRevenue' => $this->getUrlSparkline('getLastRevenueGraph', $goalId) . "&idGoal=".$goalId, + ); + } + + function index() + { + $view = new Piwik_View('Goals/templates/overview.tpl'); + $view->currency = Piwik::getCurrency(); + + $view->title = 'All goals - evolution'; + $view->graphEvolution = $this->getLastNbConversionsGraph(true); + $view->nameGraphEvolution = 'GoalsgetLastNbConversionsGraph'; // must be the function name used above + + // sparkline for the historical data of the above values + $view->urlSparklineConversions = $this->getUrlSparkline('getLastNbConversionsGraph'); + $view->urlSparklineConversionRate = $this->getUrlSparkline('getLastConversionRateGraph'); + $view->urlSparklineRevenue = $this->getUrlSparkline('getLastRevenueGraph'); + + $request = new Piwik_API_Request("method=Goals.get&format=original"); + $datatable = $request->process(); + $view->nb_conversions = $datatable->getRowFromLabel('Goal_nb_conversions')->getColumn('value'); + $view->conversion_rate = $datatable->getRowFromLabel('Goal_conversion_rate')->getColumn('value'); + $view->revenue = $datatable->getRowFromLabel('Goal_revenue')->getColumn('value'); + + $goalMetrics = array(); + + $idSite = Piwik_Common::getRequestVar('idSite'); + $goals = Piwik_Goals_API::getGoals($idSite); + foreach($goals as $idGoal => $goal) + { + $goalMetrics[$idGoal] = $this->getMetricsForGoal($idGoal); + $goalMetrics[$idGoal]['name'] = $goal['name']; + } + + $view->goalMetrics = $goalMetrics; + $view->goals = $goals; + $view->goalsJSON = json_encode($goals); + $view->userCanEditGoals = Piwik::isUserHasAdminAccess($idSite); + echo $view->render(); + } + + function addNewGoal() + { + $view = new Piwik_View('Goals/templates/add_new_goal.tpl'); + $idSite = Piwik_Common::getRequestVar('idSite'); + $view->userCanEditGoals = Piwik::isUserHasAdminAccess($idSite); + $view->currency = Piwik::getCurrency(); + $view->onlyShowAddNewGoal = true; + echo $view->render(); + } + + function getLastNbConversionsGraph( $fetch = false ) + { + $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversions'); + return $this->renderView($view, $fetch); + } + function getLastConversionRateGraph( $fetch = false ) + { + $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getConversionRate'); + return $this->renderView($view, $fetch); + } + function getLastRevenueGraph( $fetch = false ) + { + $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, 'Goals.getRevenue'); + return $this->renderView($view, $fetch); + } +} diff --git a/plugins/Goals/Goals.php b/plugins/Goals/Goals.php new file mode 100644 index 0000000000..550f0e7288 --- /dev/null +++ b/plugins/Goals/Goals.php @@ -0,0 +1,206 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html Gpl v3 or later + * @version $Id$ + * + * @package Piwik_Referers + */ + +require_once "core/Tracker/GoalManager.php"; + +/** + * @package Piwik_Referers + */ +class Piwik_Goals extends Piwik_Plugin +{ + const ROUNDING_PRECISION = 2; + + public function getInformation() + { + $info = array( + 'name' => '(ALPHA) Goal Tracking', + 'description' => 'Create Goals and see reports about your goal conversions: evolution over time, revenue per visit, conversions per referer, per keyword, etc.', + 'author' => 'Piwik', + 'homepage' => 'http://piwik.org/', + 'version' => '0.1', + 'TrackerPlugin' => true + ); + + return $info; + } + + function getListHooksRegistered() + { + $hooks = array( + 'Common.fetchWebsiteAttributes' => 'fetchGoalsFromDb', + 'ArchiveProcessing_Day.compute' => 'archiveDay', + 'ArchiveProcessing_Period.compute' => 'archivePeriod', + 'WidgetsList.add' => 'addWidgets', + 'Menu.add' => 'addMenus', + ); + return $hooks; + } + + function fetchGoalsFromDb($notification) + { + require_once "Goals/API.php"; + $info = $notification->getNotificationInfo(); + $idsite = $info['idsite']; + + // add the 'goal' entry in the website array + $array =& $notification->getNotificationObject(); + $array['goals'] = Piwik_Goals_API::getGoals($idsite); + } + + function addWidgets() + { +// Piwik_AddWidget( 'Referers', 'getKeywords', Piwik_Translate('Referers_WidgetKeywords')); + } + + function addMenus() + { + $goals = Piwik_Tracker_GoalManager::getGoalDefinitions(Piwik_Common::getRequestVar('idSite')); + if(count($goals)==0) + { + Piwik_AddMenu('Goals', 'Add a new Goal', array('module' => 'Goals', 'action' => 'addNewGoal')); + } + else + { + Piwik_AddMenu('Goals', 'Overview', array('module' => 'Goals')); + foreach($goals as $goal) + { + Piwik_AddMenu('Goals', str_replace('%', '%%', $goal['name']), array('module' => 'Goals', 'action' => 'goalReport', 'idGoal' => $goal['idgoal'])); + } + } + } + + /** + * @param string $recordName 'nb_conversions' + * @param int $idGoal idGoal to return the metrics for, or false to return overall + * @param int $visitorReturning 0 for new visitors, 1 for returning visitors, false for all + * @return unknown + */ + static public function getRecordName($recordName, $idGoal = false, $visitorReturning = false) + { + $idGoalStr = $returningStr = ''; + if($idGoal !== false) + { + $idGoalStr = $idGoal . "_"; + } + if($visitorReturning !== false) + { + $returningStr = 'visitor_returning_' . $visitorReturning . '_'; + } + return 'Goal_' . $returningStr . $idGoalStr . $recordName; + } + + function archivePeriod($notification ) + { + /** + * @var Piwik_ArchiveProcessing_Period + */ + $archiveProcessing = $notification->getNotificationObject(); + + $metricsToSum = array( 'nb_conversions', 'revenue'); + $goalIdsToSum = Piwik_Tracker_GoalManager::getGoalIds($archiveProcessing->idsite); + + $fieldsToSum = array(); + foreach($metricsToSum as $metricName) + { + foreach($goalIdsToSum as $goalId) + { + $fieldsToSum[] = self::getRecordName($metricName, $goalId); + $fieldsToSum[] = self::getRecordName($metricName, $goalId, 0); + $fieldsToSum[] = self::getRecordName($metricName, $goalId, 1); + } + $fieldsToSum[] = self::getRecordName($metricName); + } + $records = $archiveProcessing->archiveNumericValuesSum($fieldsToSum); + + // also recording conversion_rate for each goal + foreach($goalIdsToSum as $goalId) + { + $nb_conversions = $records[self::getRecordName('nb_conversions', $goalId)]->value; + $conversion_rate = $this->getConversionRate($nb_conversions, $archiveProcessing); + $record = new Piwik_ArchiveProcessing_Record_Numeric(self::getRecordName('conversion_rate', $goalId), $conversion_rate); + } + + // global conversion rate + $nb_conversions = $records[self::getRecordName('nb_conversions')]->value; + $conversion_rate = $this->getConversionRate($nb_conversions, $archiveProcessing); + $record = new Piwik_ArchiveProcessing_Record_Numeric(self::getRecordName('conversion_rate'), $conversion_rate); + + } + + function archiveDay( $notification ) + { + /** + * @var Piwik_ArchiveProcessing_Day + */ + $archiveProcessing = $notification->getNotificationObject(); + + // by processing visitor_returning segment, we can also simply sum and get stats for all goals. + $query = $archiveProcessing->queryConversionsBySegment('visitor_returning'); + + $nb_conversions = $revenue = 0; + $goals = $goalsByVisitorReturning = array(); + while($row = $query->fetch() ) + { + $goalsByVisitorReturning[$row['idgoal']][$row['visitor_returning']] = $archiveProcessing->getGoalRowFromQueryRow($row); + + if(!isset($goals[$row['idgoal']])) $goals[$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + $archiveProcessing->updateGoalStats($row, $goals[$row['idgoal']]); + + $revenue += $row['revenue']; + $nb_conversions += $row['nb_conversions']; + } + + // 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); + $record = new Piwik_ArchiveProcessing_Record_Numeric($recordName, $value); + } + $conversion_rate = $this->getConversionRate($values[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS], $archiveProcessing); + $recordName = self::getRecordName('conversion_rate', $idgoal); + $record = new Piwik_ArchiveProcessing_Record_Numeric($recordName, $conversion_rate); + } + + // Stats by goal, for visitor returning / non returning + foreach($goalsByVisitorReturning as $idgoal => $values) + { + foreach($values as $visitor_returning => $goalValues) + { + foreach($goalValues as $metricId => $value) + { + $metricName = Piwik_Archive::$mappingFromIdToNameGoal[$metricId]; + $recordName = self::getRecordName($metricName, $idgoal, $visitor_returning); + $record = new Piwik_ArchiveProcessing_Record_Numeric($recordName, $value); +// echo $record . "<br>"; + } + } + } + + // Stats for all goals + $totalAllGoals = array( + self::getRecordName('conversion_rate') => round(100 * $archiveProcessing->getNumberOfVisitsConverted() / $archiveProcessing->getNumberOfVisits(), self::ROUNDING_PRECISION), + self::getRecordName('nb_conversions') => $nb_conversions, + self::getRecordName('revenue') => $revenue, + ); + foreach($totalAllGoals as $recordName => $value) + { + $record = new Piwik_ArchiveProcessing_Record_Numeric($recordName, $value); + } + } + + function getConversionRate($count, $archiveProcessing) + { + return round(100 * $count / $archiveProcessing->getNumberOfVisits(), self::ROUNDING_PRECISION); + } +} diff --git a/plugins/Goals/templates/GoalForm.js b/plugins/Goals/templates/GoalForm.js new file mode 100644 index 0000000000..4345bc8958 --- /dev/null +++ b/plugins/Goals/templates/GoalForm.js @@ -0,0 +1,124 @@ + +function showAddNewGoal() +{ + $("#GoalForm").show(); + $("#EditGoals").hide(); + $.scrollTo("#AddEditGoals", 400); + return false; +} + +function showEditGoals() +{ + $("#EditGoals").show(); + $("#GoalForm").hide(); + $.scrollTo("#AddEditGoals", 400); + return false; +} + +// init the goal form with existing goal value, if any +function initGoalForm(goalMethodAPI, submitText, goalName, matchAttribute, pattern, patternType, caseSensitive, revenue, goalId) +{ + $('#goal_name').val(goalName); + $('input[@name=match_attribute][value='+matchAttribute+']').attr('checked', true); + $('#match_attribute_name').html(mappingMatchTypeName[matchAttribute]); + $('#examples_pattern').html(mappingMatchTypeExamples[matchAttribute]); + $('option[value='+patternType+']').attr('selected', true); + $('input[name=pattern]').val(pattern); + $('#case_sensitive').attr('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 initAndShowAddGoalForm() +{ + initGoalForm('Goals.addGoal', 'Add Goal', '', 'url', '', 'contains', false, '0'); + return showAddNewGoal(); +} +function bindGoalForm() +{ + $('input[@name=match_attribute]').click( function() { + var matchTypeId = $(this).attr('value'); + $('#match_attribute_name').html(mappingMatchTypeName[matchTypeId]); + $('#examples_pattern').html(mappingMatchTypeExamples[matchTypeId]); + }); + + $('#goal_submit').click( function() { + // prepare ajax query to API to add goal + ajaxRequestAddGoal = getAjaxAddGoal(); + $.ajax( ajaxRequestAddGoal ); + return false; + }); + + $('a[name=linkAddNewGoal]').click( function(){ + initAndShowAddGoalForm(); + } ); +} + +function bindListGoalEdit() +{ + $('a[name=linkEditGoal]').click( function() { + var goalId = $(this).attr('id'); + var goal = piwik.goals[goalId]; + initGoalForm("Goals.updateGoal", "Update Goal", goal.name, goal.match_attribute, goal.pattern, goal.pattern_type, (goal.case_sensitive=='0' ? false : true), goal.revenue, goalId); + showAddNewGoal(); + return false; + }); + + $('a[name=linkDeleteGoal]').click( function() { + var goalId = $(this).attr('id'); + var goalName = 'test goal';//piwik.goals[goalId][name] + if(confirm(sprintf('Are you sure you want to delete the Goal %s?','"'+goalName+'"'))) + { + $.ajax( getAjaxDeleteGoal( goalId ) ); + return false; + } + }); + + $('a[name=linkEditGoals]').click( function(){ + return showEditGoals(); + } ); +} +function getAjaxDeleteGoal(idGoal) +{ + var ajaxRequest = getStandardAjaxConf(); + toggleAjaxLoading(); + + var parameters = new Object; + parameters.idSite = piwik.idSite; + parameters.idGoal = idGoal; + parameters.method = 'Goals.deleteGoal'; + parameters.module = 'API'; + parameters.format = 'json'; + parameters.token_auth = piwik.token_auth; + ajaxRequest.data = parameters; + return ajaxRequest; +} + +function getAjaxAddGoal() +{ + var ajaxRequest = getStandardAjaxConf(); + toggleAjaxLoading(); + + var parameters = new Object; + + parameters.idSite = piwik.idSite; + parameters.name = encodeURIComponent( $('#goal_name').val() ); + 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').attr('checked') == true ? 1: 0; + parameters.revenue = $('input[name=revenue]').val(); + + parameters.idGoal = $('input[name=goalIdUpdate]').val(); + parameters.method = $('input[name=methodGoalAPI]').val(); + parameters.module = 'API'; + parameters.format = 'json'; + parameters.token_auth = piwik.token_auth; + + ajaxRequest.data = parameters; + return ajaxRequest; +}
\ No newline at end of file diff --git a/plugins/Goals/templates/add_edit_goal.tpl b/plugins/Goals/templates/add_edit_goal.tpl new file mode 100644 index 0000000000..2ca55ca102 --- /dev/null +++ b/plugins/Goals/templates/add_edit_goal.tpl @@ -0,0 +1,49 @@ + +<div id="AddEditGoals"> +{if isset($onlyShowAddNewGoal)} + <h2>Add a new Goal</h2> +{else} + <h2><a onclick='' name="linkAddNewGoal">+ Add a new Goal</a> + or <a onclick='' name="linkEditGoals">Edit</a> existing Goals</h2> +{/if} + + <div> + <div id="ajaxError" style="display:none"></div> + <div id="ajaxLoading" style="display:none"><div id="loadingPiwik"><img src="themes/default/images/loading-blue.gif" alt="" /> {'General_LoadingData'|translate}</div></div> + </div> + +{if !isset($onlyShowAddNewGoal)} + {include file="Goals/templates/list_goal_edit.tpl"} +{/if} + {include file="Goals/templates/form_add_goal.tpl"} + + <a id='bottom'></a> +</div> + +{literal} +<script type="text/javascript" src="plugins/Goals/templates/GoalForm.js"></script> +<script language="javascript"> + +var mappingMatchTypeName = { + "url": "URL", + "file": "filename", + "external_website": "external website URL" +}; +var mappingMatchTypeExamples = { + "url": "eg. contains 'checkout/confirmation'<br>eg. is exactly 'http://example.com/thank-you.html'<br>eg. matches the expression '[.*]\\\/demo\\\/[.*]'", + "file": "eg. contains 'files/brochure.pdf'<br>eg. is exactly 'http://example.com/files/brochure.pdf'<br>eg. matches the expression '[.*]\\\.zip'", + "external_website": "eg. contains 'amazon.com'<br>eg. is exactly 'http://mypartner.com/landing.html'<br>eg. matches the expression 'http://www.amazon.com\\\/[.*]\\\/yourAffiliateId'" +}; + +bindGoalForm(); + +{/literal} + +{if !isset($onlyShowAddNewGoal)} +piwik.goals = {$goalsJSON}; +bindListGoalEdit(); +{else} +initAndShowAddGoalForm(); +{/if} + +</script> diff --git a/plugins/Goals/templates/add_new_goal.tpl b/plugins/Goals/templates/add_new_goal.tpl new file mode 100644 index 0000000000..05a7f66e8d --- /dev/null +++ b/plugins/Goals/templates/add_new_goal.tpl @@ -0,0 +1,8 @@ + +{if $userCanEditGoals} + {include file=Goals/templates/add_edit_goal.tpl} +{else} +Only an Administrator or the Super User can add Goals for a given website. +Please ask your Piwik administrator to set up a Goal for your website. +<br>Tracking Goals are a great tool to help you maximize your website performance! +{/if} diff --git a/plugins/Goals/templates/form_add_goal.tpl b/plugins/Goals/templates/form_add_goal.tpl new file mode 100644 index 0000000000..95c4f6d3d9 --- /dev/null +++ b/plugins/Goals/templates/form_add_goal.tpl @@ -0,0 +1,67 @@ +{literal} +<style> +.goalInlineHelp{ +color:#9B9B9B; +} +</style> +{/literal} +<span id='GoalForm' style="display:none;"> +<form> + <table class="tableForm"> + <tr> + <td>Goal Name </td> + <td><input type="text" name="name" value="" id="goal_name" /></td> + </tr> + <tr> + <td>Goal is triggered when visitors:</td> + <td> + <input type="radio" id="match_attribute_url" value="url" name="match_attribute"/> + <label for="match_attribute_url">Visit a given URL (page or group of pages)</label> + <br> + <input type="radio" id="match_attribute_file" value="file" name="match_attribute"/> + <label for="match_attribute_file">Download a file</label> + <br> + <input type="radio" id="match_attribute_external_website" value="external_website" name="match_attribute"/> + <label for="match_attribute_external_website">Click on a Link to an external website </label> + </td> + </tr> + <tr> + <td>where the <span id="match_attribute_name"></span></td> + <td> + <select name="pattern_type"> + <option value="contains">contains</option> + <option value="exact">is exactly</option> + <option value="regex">matches the expression</option> + </select> + + <input type="text" name="pattern" value=""/> + <br> + <div id="examples_pattern" class="goalInlineHelp"></div> + <br> + <span style="float:right"> + (optional) <input type="checkbox" id="case_sensitive"/> + <label for="case_sensitive">Case sensitive match</label> + </span> + </td> + </tr> + <tr> + <td>(optional) Goal default value is </td> + <td>{$currency} <input type="text" name="revenue" size="1" value="0"/> + <div class="goalInlineHelp"> + For example, a Contact Form submitted by a visitor <br> + may be worth $10 on average. Piwik will help you understand <br> + how well your visitors segments are performing.</div> + </td> + </tr> + <tr> + <td colspan="2" style="border:0"> + <div class="submit"> + <input type="hidden" name="methodGoalAPI" value=""> + <input type="hidden" name="goalIdUpdate" value=""> + <input type="submit" value="Add Goal" name="submit" id="goal_submit" class="submit" /> + </div> + </td> + </tr> + </table> +</form> +</span>
\ No newline at end of file diff --git a/plugins/Goals/templates/list_goal_edit.tpl b/plugins/Goals/templates/list_goal_edit.tpl new file mode 100644 index 0000000000..081497cade --- /dev/null +++ b/plugins/Goals/templates/list_goal_edit.tpl @@ -0,0 +1,22 @@ +<span id='EditGoals' style="display:none;"> + <table class="tableForm"> + <thead style="font-weight:bold"> + <td>Id</td> + <td>Goal Name</td> + <td>Goal is Triggered when</td> + <td>Revenue</td> + <td>Edit</td> + <td>Delete</td> + </thead> + {foreach from=$goals item=goal} + <tr> + <td>{$goal.idgoal}</td> + <td>{$goal.name}</td> + <td>{$goal.match_attribute} <br>Pattern {$goal.pattern_type}: {$goal.pattern}</b></td> + <td>{if $goal.revenue==0}-{else}{$currency}{$goal.revenue}{/if}</td> + <td><a href='#' name="linkEditGoal" id="{$goal.idgoal}"><img src='plugins/UsersManager/images/edit.png' border=0> Edit</a></td> + <td><a href='#' name="linkDeleteGoal" id="{$goal.idgoal}"><img src='plugins/UsersManager/images/remove.png' border=0> Delete</a></td> + </tr> + {/foreach} + </table> +</span>
\ No newline at end of file diff --git a/plugins/Goals/templates/list_top_segment.tpl b/plugins/Goals/templates/list_top_segment.tpl new file mode 100644 index 0000000000..6dbe69813f --- /dev/null +++ b/plugins/Goals/templates/list_top_segment.tpl @@ -0,0 +1,5 @@ + +{foreach from=$topSegment item=element name=topGoalElements} +<span class='goalTopElement' title='<b>{$element.nb_conversions}</b> conversions, <b>{$element.conversion_rate}%</b> conversion rate'> +{$element.name}</span>{logoHtml metadata=$element.metadata alt=$element.name}{if $smarty.foreach.topGoalElements.iteration == $smarty.foreach.topGoalElements.total-1} and {elseif $smarty.foreach.topGoalElements.iteration < $smarty.foreach.topGoalElements.total-1}, {else}{/if} +{/foreach} {* (<a href=''>more</a>) *} diff --git a/plugins/Goals/templates/overview.tpl b/plugins/Goals/templates/overview.tpl new file mode 100644 index 0000000000..04bf1af90d --- /dev/null +++ b/plugins/Goals/templates/overview.tpl @@ -0,0 +1,27 @@ + +{include file="Goals/templates/title_and_evolution_graph.tpl"} + +{foreach from=$goalMetrics item=goal} +{assign var=nb_conversions value=$goal.nb_conversions} +{assign var=conversion_rate value=$goal.conversion_rate} +<h2 style="padding-top: 30px;">{$goal.name} (goal)</h3> +<table width=700px> + <tr><td> + <p>{sparkline src=$goal.urlSparklineConversions}<span> + {'%s conversions'|translate:"<strong>$nb_conversions</strong>"}</span></p> + </td><td> + <p>{sparkline src=$goal.urlSparklineConversionRate}<span> + {'%s conversion rate'|translate:"<strong>$conversion_rate%</strong>"}</span></p> + </td><td> + {* (<a href=''>more</a>) *} + </td></tr> +</table> + +{/foreach} + +{if $userCanEditGoals} + <hr style="margin:30px 0px"> + {include file=Goals/templates/add_edit_goal.tpl} +{/if} + +{include file="Goals/templates/release_notes.tpl}
\ No newline at end of file diff --git a/plugins/Goals/templates/release_notes.tpl b/plugins/Goals/templates/release_notes.tpl new file mode 100644 index 0000000000..dce9ec63b2 --- /dev/null +++ b/plugins/Goals/templates/release_notes.tpl @@ -0,0 +1,20 @@ +<hr> +<b>About the Goal Tracking Plugin</b><br> +<pre> +The Goal Tracking Plugin is in alpha release. There is more coming soon! +- The Goal Report page will display conversion table by search engines, country, keyword, campaign, etc. +- The Goal Overview page will link to a Goal Report page with a "(more)" link that will ajax reload the page +- Goals could be triggered using javascript event, with custom revenue +- internationalization of all strings +- provide documentation, screenshots, blog post + add screenshot and inline help in "Add a New Goal" +- provide widgets for the dashboard, general goal overview, and one widget for each goal. With: graph evolution, sparklines. Widget with top segments for each goal. + +Known bugs +- The Goal total nb conversions should be sum of all goal conversions (wrong number when deleting a Goal) +- After adding goal, the window should refresh to the goal report page, and not to the dashboard +- Outlink trailing slash is automatically deleted from the URL, there would be a problem when trying to exact match a URL with trailing slash +- All graph labelling are not correct (always printing nb_uniq_visitors even when showing conversion or conversion_rate) see <a href='http://dev.piwik.org/trac/ticket/322'>#322</a> + +Give us Feedback! +If you find any other bug, or if you have suggestions, please send us a message using the "Give us feedback" link at the top of the Piwik pages. +</pre>
\ No newline at end of file diff --git a/plugins/Goals/templates/single_goal.tpl b/plugins/Goals/templates/single_goal.tpl new file mode 100644 index 0000000000..b605c17490 --- /dev/null +++ b/plugins/Goals/templates/single_goal.tpl @@ -0,0 +1,37 @@ +{include file="Goals/templates/title_and_evolution_graph.tpl"} + +{if $nb_conversions > 0} + <h2>Conversions Overview</h2> + <ul class="ulGoalTopElements"> + <li>Your best converting countries are: {include file='Goals/templates/list_top_segment.tpl' topSegment=$topSegments.country}</li> + {if count($topSegments.keyword)>0}<li>Your top converting keywords are: {include file='Goals/templates/list_top_segment.tpl' topSegment=$topSegments.keyword}</li>{/if} + {if count($topSegments.website)>0}<li>Your best converting websites referers are: {include file='Goals/templates/list_top_segment.tpl' topSegment=$topSegments.website}</li>{/if} + <li>Returning visitors conversion rate is <b>{$conversion_rate_returning}%</b>, New Visitors conversion rate is <b>{$conversion_rate_new}%</b></li> + </ul> +{/if} + + +{literal} +<style> +ul.ulGoalTopElements { + list-style-type:circle; + margin-left:30px; +} +.ulGoalTopElements a { + text-decoration:none; + color:#0033CC; + border-bottom:1px dotted #0033CC; + line-height:2em; +} +.goalTopElement { + border-bottom:1px dotted; +} +</style> +<script> +$(document).ready( function() { + $('.goalTopElement') + .Tooltip() + ; + }); +</script> +{/literal}
\ No newline at end of file diff --git a/plugins/Goals/templates/title_and_evolution_graph.tpl b/plugins/Goals/templates/title_and_evolution_graph.tpl new file mode 100644 index 0000000000..0b1b8d2c7b --- /dev/null +++ b/plugins/Goals/templates/title_and_evolution_graph.tpl @@ -0,0 +1,20 @@ +<script type="text/javascript" src="plugins/CoreHome/templates/sparkline.js"></script> + +<a name="evolutionGraph" graphId="{$nameGraphEvolution}"></a> +<h2>{$title}</h2> +{$graphEvolution} + +<table> + <tr><td> + <p>{sparkline src=$urlSparklineConversions}<span> + {'%s conversions'|translate:"<strong>$nb_conversions</strong>"}</span></p> + {if $revenue != 0 } + <p>{sparkline src=$urlSparklineRevenue}<span> + {'%s overall revenue'|translate:"<strong>$currency$revenue</strong>"}</span></p> + {/if} + </td><td valign="top"> + <p>{sparkline src=$urlSparklineConversionRate}<span> + {'%s overall conversion rate (visits with a completed goal)'|translate:"<strong>$conversion_rate%</strong>"}</span></p> + </td></tr> +</table> + |