diff options
author | mattpiwik <matthieu.aubry@gmail.com> | 2008-12-17 20:26:15 +0300 |
---|---|---|
committer | mattpiwik <matthieu.aubry@gmail.com> | 2008-12-17 20:26:15 +0300 |
commit | 0c3ef698010708ba0484aabd5a01db3b62f47365 (patch) | |
tree | e1b592d7358d3a01a1dc39cd8c3134d5ee551b2f | |
parent | fa3877ec785daf7d6aad35ca877611ff040c2ffd (diff) |
- adding Goal Tracking related goodness in core, and plugins
- goal table icon below datatable that have goal segmentation
- rss export icon below datatable
- cleaning code, refactoring, renaming goodness
- adding switch enable_detect_unique_visitor_using_settings that enables/disable heuristic based on config hash
- refactoring how plugins handle archiving for more clarity
git-svn-id: http://dev.piwik.org/svn/trunk@838 59fd770c-687e-43c8-a1e3-f5a4ff64c105
100 files changed, 3265 insertions, 2293 deletions
diff --git a/config/global.ini.php b/config/global.ini.php index 4331a20df7..4cf9ad1006 100755 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -61,7 +61,6 @@ PluginsInstalled[] = Installation [Plugins_Tracker] - [Debug] ; if set to true, the archiving process will always be triggered, even if the archive has already been computed ; this is useful when making changes to the archiving code so we can force the archiving process @@ -98,6 +97,9 @@ enable_browser_archiving_triggering = true ; the page first-post in the subcategory development which belongs to the blog category action_category_delimiter = / +; currency used by default when reporting money in Piwik +default_currency = "$" + ; if you want all your users to use Piwik in only one language, disable the LanguagesManager ; plugin, and set this default_language (users won't see the language drop down) default_language = en @@ -152,6 +154,16 @@ campaign_keyword_var_name = piwik_kwd ; name of the cookie used to store the visitor information cookie_name = piwik_visitor +; if set to false, any goal conversion will be credited to the last more recent non empty referer. +; when set to true, the first ever referer used to reach the website will be used +use_first_referer_to_determine_goal_referer = false + +; if set to true, Piwik will try to match visitors without cookie to a previous visitor that has the same +; configuration: OS, browser, resolution, IP, etc. This heuristic adds an extra SQL query for each page view without cookie. +; it is advised to set it to true for more accurate detection of unique visitors. +; However when most users have the same IP, and the same configuration, it is advised to set it to false +enable_detect_unique_visitor_using_settings = false + [log] ;possible values for log: screen, database, file diff --git a/core/API/DataTableGenericFilter.php b/core/API/DataTableGenericFilter.php index bfe6367027..4304da0c40 100644 --- a/core/API/DataTableGenericFilter.php +++ b/core/API/DataTableGenericFilter.php @@ -42,13 +42,16 @@ class Piwik_API_DataTableGenericFilter ), 'ExcludeLowPopulation' => array( 'filter_excludelowpop' => array('string'), - 'filter_excludelowpop_value'=> array('float'), + 'filter_excludelowpop_value'=> array('float', '0'), ), 'AddColumnsWhenShowAllColumns' => array( 'filter_add_columns_when_show_all_columns' => array('integer') ), + 'UpdateColumnsWhenShowAllGoals' => array( + 'filter_update_columns_when_show_all_goals' => array('integer') + ), 'Sort' => array( - 'filter_sort_column' => array('string', 'nb_visits'), + 'filter_sort_column' => array('string', Piwik_Archive::INDEX_NB_VISITS), 'filter_sort_order' => array('string', Zend_Registry::get('config')->General->dataTable_default_sort_order), ), 'Limit' => array( @@ -110,7 +113,7 @@ class Piwik_API_DataTableGenericFilter break; } } - + if(!$exceptionRaised) { // a generic filter class name must follow this pattern diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php index 2a97febdb0..f0455c95c9 100644 --- a/core/API/ResponseBuilder.php +++ b/core/API/ResponseBuilder.php @@ -245,7 +245,7 @@ class Piwik_API_ResponseBuilder protected function handleDataTable($datatable) { // if the flag disable_generic_filters is defined we skip the generic filters - if(Piwik_Common::getRequestVar('disable_generic_filters', 'false', 'string', $this->request) == 'false') + if('false' == Piwik_Common::getRequestVar('disable_generic_filters', 'false', 'string', $this->request)) { $genericFilter = new Piwik_API_DataTableGenericFilter($datatable, $this->request); $genericFilter->filter(); diff --git a/core/Access.php b/core/Access.php index f6251ec2a1..36a91f5c77 100644 --- a/core/Access.php +++ b/core/Access.php @@ -253,7 +253,6 @@ class Piwik_Access /** * If the user doesn't have an ADMIN access for at least one website, throws an exception - * * @throws Exception */ public function checkUserHasSomeAdminAccess() @@ -264,6 +263,19 @@ class Piwik_Access throw new Piwik_Access_NoAccessException("You can't access this resource as it requires an 'admin' access for at least one website."); } } + + /** + * If the user doesn't have any view permission, throw exception + * @throws Exception + */ + public function checkUserHasSomeViewAccess() + { + $idSitesAccessible = $this->getSitesIdWithAtLeastViewAccess(); + if(count($idSitesAccessible) == 0) + { + throw new Piwik_Access_NoAccessException("You can't access this resource as it requires a 'view' access for at least one website."); + } + } /** * This method checks that the user has ADMIN access for the given list of websites. diff --git a/core/Archive.php b/core/Archive.php index b06bc11891..f6ca63c2b6 100644 --- a/core/Archive.php +++ b/core/Archive.php @@ -1,255 +1,273 @@ -<?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: Archive.php 585 2008-07-28 00:56:50Z matt $
- *
- * @package Piwik
- */
-
-require_once 'Period.php';
-require_once 'Date.php';
-require_once 'ArchiveProcessing.php';
-require_once 'Archive/Single.php';
-
-/**
- * The archive object is used to query specific data for a day or a period of statistics for a given website.
- *
- * Example:
- * <pre>
- * $archive = Piwik_Archive::build($idSite = 1, $period = 'week', '2008-03-08' );
- * $dataTable = $archive->getDataTable('Provider_hostnameExt');
- * $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames');
- * return $dataTable;
- * </pre>
- *
- * Example bis:
- * <pre>
- * $archive = Piwik_Archive::build($idSite = 3, $period = 'day', $date = 'today' );
- * $nbVisits = $archive->getNumeric('nb_visits');
- * return $nbVisits;
- * </pre>
- *
- * If the requested statistics are not yet processed, Archive uses ArchiveProcessing to archive the statistics.
- *
- * @package Piwik
- * @subpackage Piwik_Archive
- */
-abstract class Piwik_Archive
-{
- /**
- * When saving DataTables in the DB, we sometimes replace the columns name by these IDs so we save up lots of bytes
- * Eg. INDEX_NB_UNIQ_VISITORS is an integer: 4 bytes, but 'nb_uniq_visitors' is 16 bytes at least
- * (in php it's actually even much more)
- *
- */
- const INDEX_NB_UNIQ_VISITORS = 1;
- const INDEX_NB_VISITS = 2;
- const INDEX_NB_ACTIONS = 3;
- const INDEX_MAX_ACTIONS = 4;
- const INDEX_SUM_VISIT_LENGTH = 5;
- const INDEX_BOUNCE_COUNT = 6;
+<?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: Archive.php 585 2008-07-28 00:56:50Z matt $ + * + * @package Piwik + */ - /* - * Integer indexed column name => string indexed column name +require_once 'Period.php'; +require_once 'Date.php'; +require_once 'ArchiveProcessing.php'; +require_once 'Archive/Single.php'; + +/** + * The archive object is used to query specific data for a day or a period of statistics for a given website. + * + * Example: + * <pre> + * $archive = Piwik_Archive::build($idSite = 1, $period = 'week', '2008-03-08' ); + * $dataTable = $archive->getDataTable('Provider_hostnameExt'); + * $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames'); + * return $dataTable; + * </pre> + * + * Example bis: + * <pre> + * $archive = Piwik_Archive::build($idSite = 3, $period = 'day', $date = 'today' ); + * $nbVisits = $archive->getNumeric('nb_visits'); + * return $nbVisits; + * </pre> + * + * If the requested statistics are not yet processed, Archive uses ArchiveProcessing to archive the statistics. + * + * @package Piwik + * @subpackage Piwik_Archive + */ +abstract class Piwik_Archive +{ + /** + * When saving DataTables in the DB, we sometimes replace the columns name by these IDs so we save up lots of bytes + * Eg. INDEX_NB_UNIQ_VISITORS is an integer: 4 bytes, but 'nb_uniq_visitors' is 16 bytes at least + * (in php it's actually even much more) + * */ + const INDEX_NB_UNIQ_VISITORS = 1; + const INDEX_NB_VISITS = 2; + const INDEX_NB_ACTIONS = 3; + const INDEX_MAX_ACTIONS = 4; + const INDEX_SUM_VISIT_LENGTH = 5; + const INDEX_BOUNCE_COUNT = 6; + const INDEX_NB_VISITS_CONVERTED = 7; + const INDEX_NB_CONVERSIONS = 8; + const INDEX_REVENUE = 9; + const INDEX_GOALS = 10; + + const INDEX_GOAL_NB_CONVERSIONS = 1; + const INDEX_GOAL_REVENUE = 2; + public static $mappingFromIdToName = array( - Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 'nb_uniq_visitors', - Piwik_Archive::INDEX_NB_VISITS => 'nb_visits', - Piwik_Archive::INDEX_NB_ACTIONS => 'nb_actions', - Piwik_Archive::INDEX_MAX_ACTIONS => 'max_actions', - Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 'sum_visit_length', - Piwik_Archive::INDEX_BOUNCE_COUNT => 'bounce_count', + Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 'nb_uniq_visitors', + Piwik_Archive::INDEX_NB_VISITS => 'nb_visits', + Piwik_Archive::INDEX_NB_ACTIONS => 'nb_actions', + Piwik_Archive::INDEX_MAX_ACTIONS => 'max_actions', + Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 'sum_visit_length', + Piwik_Archive::INDEX_BOUNCE_COUNT => 'bounce_count', + Piwik_Archive::INDEX_NB_VISITS_CONVERTED => 'nb_visits_converted', + Piwik_Archive::INDEX_NB_CONVERSIONS => 'nb_conversions', + Piwik_Archive::INDEX_REVENUE => 'revenue', + Piwik_Archive::INDEX_GOALS => 'goals', ); + + public static $mappingFromIdToNameGoal = array( + Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 'nb_conversions', + Piwik_Archive::INDEX_GOAL_REVENUE => 'revenue', + ); + /* * string indexed column name => Integer indexed column name */ public static $mappingFromNameToId = array( - 'nb_uniq_visitors' => Piwik_Archive::INDEX_NB_UNIQ_VISITORS, - 'nb_visits' => Piwik_Archive::INDEX_NB_VISITS, - 'nb_actions' => Piwik_Archive::INDEX_NB_ACTIONS, - 'max_actions' => Piwik_Archive::INDEX_MAX_ACTIONS, - 'sum_visit_length' => Piwik_Archive::INDEX_SUM_VISIT_LENGTH, - 'bounce_count' => Piwik_Archive::INDEX_BOUNCE_COUNT, - ); -
- /**
- * Website Piwik_Site
- *
- * @var Piwik_Site
- */
- protected $site = null;
-
- /**
- * Stores the already built archives.
- * Act as a big caching array
- *
- * @var array of Piwik_Archive
- */
- static protected $alreadyBuilt = array();
-
- /**
- * Builds an Archive object or returns the same archive if previously built.
- *
- * @param string|int idSite integer, or comma separated list of integer
- * @param string|Piwik_Date $date 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory()
- * @param string $period 'week' 'day' etc.
- *
- * @return Piwik_Archive
- */
- static public function build($idSite, $period, $strDate )
- {
- if($idSite === 'all')
- {
- $sites = Piwik_SitesManager_API::getSitesIdWithAtLeastViewAccess();
- }
- else
- {
- $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite);
+ 'nb_uniq_visitors' => Piwik_Archive::INDEX_NB_UNIQ_VISITORS, + 'nb_visits' => Piwik_Archive::INDEX_NB_VISITS, + 'nb_actions' => Piwik_Archive::INDEX_NB_ACTIONS, + 'max_actions' => Piwik_Archive::INDEX_MAX_ACTIONS, + 'sum_visit_length' => Piwik_Archive::INDEX_SUM_VISIT_LENGTH, + 'bounce_count' => Piwik_Archive::INDEX_BOUNCE_COUNT, + 'nb_visits_converted' => Piwik_Archive::INDEX_NB_VISITS_CONVERTED, + 'nb_conversions' => Piwik_Archive::INDEX_NB_CONVERSIONS, + 'revenue' => Piwik_Archive::INDEX_REVENUE, + 'goals' => Piwik_Archive::INDEX_GOALS, + ); + + /** + * Website Piwik_Site + * + * @var Piwik_Site + */ + protected $site = null; + + /** + * Stores the already built archives. + * Act as a big caching array + * + * @var array of Piwik_Archive + */ + static protected $alreadyBuilt = array(); + + /** + * Builds an Archive object or returns the same archive if previously built. + * + * @param string|int idSite integer, or comma separated list of integer + * @param string|Piwik_Date $date 'YYYY-MM-DD' or magic keywords 'today' @see Piwik_Date::factory() + * @param string $period 'week' 'day' etc. + * + * @return Piwik_Archive + */ + static public function build($idSite, $period, $strDate ) + { + if($idSite === 'all') + { + $sites = Piwik_SitesManager_API::getSitesIdWithAtLeastViewAccess(); } -
- // idSite=1,3 or idSite=all
+ else + { + $sites = Piwik_Site::getIdSitesFromIdSitesString($idSite); + } + + // idSite=1,3 or idSite=all if( count($sites) > 1 - || $idSite === 'all' )
- {
- require_once 'Archive/Array/IndexedBySite.php';
- $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate);
- }
- // if a period date string is detected: either 'last30', 'previous10' or 'YYYY-MM-DD,YYYY-MM-DD'
- elseif(is_string($strDate)
- && (
- ereg('^(last|previous){1}([0-9]*)$', $strDate, $regs)
- || ereg('^([0-9]{4}-[0-9]{1,2}-[0-9]{1,2}),([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$', $strDate, $regs)
- )
- )
- {
- $oSite = new Piwik_Site($idSite);
- require_once 'Archive/Array/IndexedByDate.php';
- $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate);
- }
- // case we request a single archive
- else
- {
- if(is_string($strDate))
- {
- $oDate = Piwik_Date::factory($strDate);
- }
- else
- {
- $oDate = $strDate;
- }
- $date = $oDate->toString();
-
+ || $idSite === 'all' ) + { + require_once 'Archive/Array/IndexedBySite.php'; + $archive = new Piwik_Archive_Array_IndexedBySite($sites, $period, $strDate); + } + // if a period date string is detected: either 'last30', 'previous10' or 'YYYY-MM-DD,YYYY-MM-DD' + elseif(is_string($strDate) + && ( + ereg('^(last|previous){1}([0-9]*)$', $strDate, $regs) + || ereg('^([0-9]{4}-[0-9]{1,2}-[0-9]{1,2}),([0-9]{4}-[0-9]{1,2}-[0-9]{1,2})$', $strDate, $regs) + ) + ) + { + $oSite = new Piwik_Site($idSite); + require_once 'Archive/Array/IndexedByDate.php'; + $archive = new Piwik_Archive_Array_IndexedByDate($oSite, $period, $strDate); + } + // case we request a single archive + else + { + if(is_string($strDate)) + { + $oDate = Piwik_Date::factory($strDate); + } + else + { + $oDate = $strDate; + } + $date = $oDate->toString(); + if(isset(self::$alreadyBuilt[$idSite][$date][$period])) { - return self::$alreadyBuilt[$idSite][$date][$period];
- }
-
- $oPeriod = Piwik_Period::factory($period, $oDate);
-
- $archive = new Piwik_Archive_Single();
- $archive->setPeriod($oPeriod);
+ return self::$alreadyBuilt[$idSite][$date][$period]; + } + + $oPeriod = Piwik_Period::factory($period, $oDate); + + $archive = new Piwik_Archive_Single(); + $archive->setPeriod($oPeriod); $archive->setSite(new Piwik_Site($idSite)); - $archiveJustProcessed = $archive->prepareArchive();
+ $archiveJustProcessed = $archive->prepareArchive(); //we don't cache the archives just processed, the datatable were freed from memory if(!$archiveJustProcessed) - {
+ { self::$alreadyBuilt[$idSite][$date][$period] = $archive; - }
- }
-
- return $archive;
- }
-
- abstract public function prepareArchive();
-
- /**
- * Returns the value of the element $name from the current archive
- * The value to be returned is a numeric value and is stored in the archive_numeric_* tables
- *
- * @param string $name For example Referers_distinctKeywords
- * @return float|int|false False if no value with the given name
- */
- abstract public function getNumeric( $name );
-
- /**
- * Returns the value of the element $name from the current archive
- *
- * The value to be returned is a blob value and is stored in the archive_numeric_* tables
- *
- * It can return anything from strings, to serialized PHP arrays or PHP objects, etc.
- *
- * @param string $name For example Referers_distinctKeywords
- * @return mixed False if no value with the given name
- */
- abstract public function getBlob( $name );
+ } + } + + return $archive; + } + + abstract public function prepareArchive(); + + /** + * Returns the value of the element $name from the current archive + * The value to be returned is a numeric value and is stored in the archive_numeric_* tables + * + * @param string $name For example Referers_distinctKeywords + * @return float|int|false False if no value with the given name + */ + abstract public function getNumeric( $name ); /** + * Returns the value of the element $name from the current archive * + * The value to be returned is a blob value and is stored in the archive_numeric_* tables + * + * It can return anything from strings, to serialized PHP arrays or PHP objects, etc. + * + * @param string $name For example Referers_distinctKeywords + * @return mixed False if no value with the given name + */ + abstract public function getBlob( $name ); + + /** + * + * @return Piwik_DataTable + */ + abstract public function getDataTableFromNumeric( $fields ); + + /** + * This method will build a dataTable from the blob value $name in the current archive. + * + * For example $name = 'Referers_searchEngineByKeyword' will return a Piwik_DataTable containing all the keywords + * If a idSubTable is given, the method will return the subTable of $name + * + * @param string $name + * @param int $idSubTable or null if requesting the parent table + * @return Piwik_DataTable + * @throws exception If the value cannot be found + */ + abstract public function getDataTable( $name, $idSubTable = null ); + + /** + * Same as getDataTable() except that it will also load in memory + * all the subtables for the DataTable $name. + * You can then access the subtables by using the Piwik_DataTable_Manager getTable() + * + * @param string $name + * @param int $idSubTable or null if requesting the parent table * @return Piwik_DataTable - */
- abstract public function getDataTableFromNumeric( $fields );
-
- /**
- * This method will build a dataTable from the blob value $name in the current archive.
- *
- * For example $name = 'Referers_searchEngineByKeyword' will return a Piwik_DataTable containing all the keywords
- * If a idSubTable is given, the method will return the subTable of $name
- *
- * @param string $name
- * @param int $idSubTable or null if requesting the parent table
- * @return Piwik_DataTable
- * @throws exception If the value cannot be found
- */
- abstract public function getDataTable( $name, $idSubTable = null );
-
- /**
- * Same as getDataTable() except that it will also load in memory
- * all the subtables for the DataTable $name.
- * You can then access the subtables by using the Piwik_DataTable_Manager getTable()
- *
- * @param string $name
- * @param int $idSubTable or null if requesting the parent table
- * @return Piwik_DataTable
- */
- abstract public function getDataTableExpanded($name, $idSubTable = null);
-
- /**
- * Sets the site
- *
- * @param Piwik_Site $site
- */
- public function setSite( Piwik_Site $site )
- {
- $this->site = $site;
- }
-
- /**
- * Gets the site
- *
- * @param Piwik_Site $site
- */
- public function getSite()
- {
- return $this->site;
- }
-
- /**
- * Returns the Id site associated with this archive
- *
- * @return int
- */
- public function getIdSite()
- {
- return $this->site->getId();
- }
-
-}
-
-
-
-
-
+ */ + abstract public function getDataTableExpanded($name, $idSubTable = null); + + /** + * Sets the site + * + * @param Piwik_Site $site + */ + public function setSite( Piwik_Site $site ) + { + $this->site = $site; + } + + /** + * Gets the site + * + * @param Piwik_Site $site + */ + public function getSite() + { + return $this->site; + } + + /** + * Returns the Id site associated with this archive + * + * @return int + */ + public function getIdSite() + { + return $this->site->getId(); + } + +} + + + + + diff --git a/core/Archive/Single.php b/core/Archive/Single.php index faf53de1c2..4035e2804b 100644 --- a/core/Archive/Single.php +++ b/core/Archive/Single.php @@ -372,6 +372,7 @@ class Piwik_Archive_Single extends Piwik_Archive * 'nb_actions', * 'sum_visit_length', * 'bounce_count', + * 'nb_visits_converted' * ); * * @param string|array $fields Name or array of names of Archive fields @@ -454,4 +455,4 @@ class Piwik_Archive_Single extends Piwik_Archive return $dataTableToLoad; } } -?>
\ No newline at end of file +?> diff --git a/core/ArchiveProcessing.php b/core/ArchiveProcessing.php index 6a1ef954bb..9a99636841 100644 --- a/core/ArchiveProcessing.php +++ b/core/ArchiveProcessing.php @@ -334,6 +334,7 @@ abstract class Piwik_ArchiveProcessing $this->logTable = Piwik::prefixTable('log_visit'); $this->logVisitActionTable = Piwik::prefixTable('log_link_visit_action'); $this->logActionTable = Piwik::prefixTable('log_action'); + $this->logConversionTable = Piwik::prefixTable('log_conversion'); } /** @@ -430,6 +431,28 @@ abstract class Piwik_ArchiveProcessing return $this->timestampDateStart; } + + // exposing the number of visits publicly (number used to compute conversions rates) + protected $nb_visits = null; + protected $nb_visits_converted = null; + + protected function setNumberOfVisits($nb_visits) + { + $this->nb_visits = $nb_visits; + } + public function getNumberOfVisits() + { + return $this->nb_visits; + } + protected function setNumberOfVisitsConverted($nb_visits_converted) + { + $this->nb_visits_converted = $nb_visits_converted; + } + public function getNumberOfVisitsConverted() + { + return $this->nb_visits_converted; + } + /** * Returns the idArchive we will use for the current archive * @@ -499,7 +522,7 @@ abstract class Piwik_ArchiveProcessing ); $timeStampWhere = " AND UNIX_TIMESTAMP(ts_archived) >= ? "; $bindSQL[] = $this->maxTimestampArchive; - + $sqlQuery = " SELECT idarchive, value, name, UNIX_TIMESTAMP(date1) as timestamp FROM ".$this->tableArchiveNumeric->getTableName()." WHERE idsite = ? @@ -510,7 +533,6 @@ abstract class Piwik_ArchiveProcessing OR name = 'nb_visits') $timeStampWhere ORDER BY ts_archived DESC"; - $results = Zend_Registry::get('db')->fetchAll($sqlQuery, $bindSQL ); if(empty($results)) { diff --git a/core/ArchiveProcessing/Day.php b/core/ArchiveProcessing/Day.php index f3a4f5d6c7..300234f3ce 100644 --- a/core/ArchiveProcessing/Day.php +++ b/core/ArchiveProcessing/Day.php @@ -11,8 +11,9 @@ /** - * Handles the archiving process for a day. - * The class provides generic methods to manipulate data from the DB, easily create Piwik_DataTable objects. + * Handles the archiving process for a day. + * The class provides generic helper methods to manipulate data from the DB, + * easily create Piwik_DataTable objects from running SELECT ... GROUP BY on the log_visit table. * * All the logic of the archiving is done inside the plugins listening to the event 'ArchiveProcessing_Day.compute' * @@ -51,7 +52,8 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing sum(visit_total_actions) as nb_actions, max(visit_total_actions) as max_actions, sum(visit_total_time) as sum_visit_length, - sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count + sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, + sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted FROM ".$this->logTable." WHERE visit_server_date = ? AND idsite = ? @@ -70,7 +72,8 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing { $record = new Piwik_ArchiveProcessing_Record_Numeric($name, $value); } - + $this->setNumberOfVisits($row['nb_visits']); + $this->setNumberOfVisitsConverted($row['nb_visits_converted']); Piwik_PostEvent('ArchiveProcessing_Day.compute', $this); } @@ -138,8 +141,26 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing return $table; } + public function getDataTableFromArray( $array ) + { + $table = new Piwik_DataTable; + $table->addRowsFromArrayWithIndexLabel($array); + return $table; + } + /** - * Helper function that returns common statistics for a given database field distinct values. + * Output: + * array( + * LABEL => array( + * Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0, + * Piwik_Archive::INDEX_NB_VISITS => 0 + * ), + * LABEL2 => array( + * [...] + * ) + * ) + * + * Helper function that returns an array with common statistics for a given database field distinct values. * * The statistics returned are: * - number of unique visitors @@ -150,18 +171,18 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * - count of bouncing visits (visits with one page view) * * For example if $label = 'config_os' it will return the statistics for every distinct Operating systems - * The returned DataTable will have a row per distinct operating systems, - * and a column per stat (nb of visits, max actions, etc) + * The returned array will have a row per distinct operating systems, + * and a column per stat (nb of visits, max actions, etc) * - * label nb_uniq_visitors nb_visits nb_actions max_actions sum_visit_length bounce_count - * Linux 27 66 66 1 660 66 - * Windows XP 12 39 39 1 390 39 - * Mac OS 15 36 36 1 360 36 + * 'label' Piwik_Archive::INDEX_NB_UNIQ_VISITORS Piwik_Archive::INDEX_NB_VISITS etc. + * Linux 27 66 ... + * Windows XP 12 ... + * Mac OS 15 36 ... * * @param string $label Table log_visit field name to be use to compute common stats - * @return Piwik_DataTable + * @return array */ - public function getDataTableInterestForLabel( $label ) + public function getArrayInterestForLabel($label) { $query = "SELECT $label as label, count(distinct visitor_idcookie) as nb_uniq_visitors, @@ -169,34 +190,21 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing sum(visit_total_actions) as nb_actions, max(visit_total_actions) as max_actions, sum(visit_total_time) as sum_visit_length, - sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count + sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, + sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted FROM ".$this->logTable." WHERE visit_server_date = ? AND idsite = ? GROUP BY label"; - $query = $this->db->query($query, array( $this->strDateStart, $this->idsite ) ); $interest = array(); - while($rowBefore = $query->fetch()) + while($row = $query->fetch()) { - $row = array( - Piwik_Archive::INDEX_NB_UNIQ_VISITORS => $rowBefore['nb_uniq_visitors'], - Piwik_Archive::INDEX_NB_VISITS => $rowBefore['nb_visits'], - Piwik_Archive::INDEX_NB_ACTIONS => $rowBefore['nb_actions'], - Piwik_Archive::INDEX_MAX_ACTIONS => $rowBefore['max_actions'], - Piwik_Archive::INDEX_SUM_VISIT_LENGTH => $rowBefore['sum_visit_length'], - Piwik_Archive::INDEX_BOUNCE_COUNT => $rowBefore['bounce_count'], - 'label' => $rowBefore['label'] - ); - if(!isset($interest[$row['label']])) $interest[$row['label']]= $this->getNewInterestRow(); $this->updateInterestStats( $row, $interest[$row['label']]); } - - $table = new Piwik_DataTable; - $table->addRowsFromArrayWithIndexLabel($interest); - return $table; + return $interest; } /** @@ -259,17 +267,19 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing /** * Helper function that returns the multiple serialized DataTable of the given PHP array. * The DataTable here associates a subtable to every row of the level 0 array. - * This is used for example for search engines. Every search engine (level 0) has a subtable containing the - * keywords. + * This is used for example for search engines. + * Every search engine (level 0) has a subtable containing the keywords. * * The $arrayLevel0 must have the format * Example: array ( + * // Yahoo.com => array( kwd1 => stats, kwd2 => stats ) * LABEL => array(col1 => X, col2 => Y), * LABEL2 => array(col1 => X, col2 => Y), * ) * * The $subArrayLevel1ByKey must have the format * Example: array( + * // Yahoo.com => array( stats ) * LABEL => #Piwik_DataTable_ForLABEL, * LABEL2 => #Piwik_DataTable_ForLABEL2, * ) @@ -279,10 +289,9 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * @param array of Piwik_DataTable $subArrayLevel1ByKey * @return array Array with N elements: the strings of the datatable serialized */ - public function getDataTablesSerialized( $arrayLevel0, $subArrayLevel1ByKey, $maximumRowsInDataTableLevelZero = null, $maximumRowsInSubDataTable = null) + public function getDataTableWithSubtablesFromArraysIndexedByLabel( $arrayLevel0, $subArrayLevel1ByKey ) { $tablesByLabel = array(); - foreach($arrayLevel0 as $label => $aAllRowsForThisLabel) { $table = new Piwik_DataTable; @@ -292,8 +301,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $parentTableLevel0 = new Piwik_DataTable; $parentTableLevel0->addRowsFromArrayWithIndexLabel($subArrayLevel1ByKey, $tablesByLabel); - $toReturn = $parentTableLevel0->getSerialized($maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); - return $toReturn; + return $parentTableLevel0; } /** @@ -308,7 +316,8 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing Piwik_Archive::INDEX_NB_ACTIONS => 0, Piwik_Archive::INDEX_MAX_ACTIONS => 0, Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 0, - Piwik_Archive::INDEX_BOUNCE_COUNT => 0 + Piwik_Archive::INDEX_BOUNCE_COUNT => 0, + Piwik_Archive::INDEX_NB_VISITS_CONVERTED=> 0, ); } @@ -339,13 +348,124 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * @param array $oldRowToUpdate */ public function updateInterestStats( $newRowToAdd, &$oldRowToUpdate) - { - $oldRowToUpdate[Piwik_Archive::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd[Piwik_Archive::INDEX_NB_UNIQ_VISITORS]; - $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS] += $newRowToAdd[Piwik_Archive::INDEX_NB_VISITS]; - $oldRowToUpdate[Piwik_Archive::INDEX_NB_ACTIONS] += $newRowToAdd[Piwik_Archive::INDEX_NB_ACTIONS]; - $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd[Piwik_Archive::INDEX_MAX_ACTIONS], $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS]); - $oldRowToUpdate[Piwik_Archive::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd[Piwik_Archive::INDEX_SUM_VISIT_LENGTH]; - $oldRowToUpdate[Piwik_Archive::INDEX_BOUNCE_COUNT] += $newRowToAdd[Piwik_Archive::INDEX_BOUNCE_COUNT]; + { + $oldRowToUpdate[Piwik_Archive::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd['nb_uniq_visitors']; + $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS] += $newRowToAdd['nb_visits']; + $oldRowToUpdate[Piwik_Archive::INDEX_NB_ACTIONS] += $newRowToAdd['nb_actions']; + $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd['max_actions'], $oldRowToUpdate[Piwik_Archive::INDEX_MAX_ACTIONS]); + $oldRowToUpdate[Piwik_Archive::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd['sum_visit_length']; + $oldRowToUpdate[Piwik_Archive::INDEX_BOUNCE_COUNT] += $newRowToAdd['bounce_count']; + $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd['nb_visits_converted']; + } + + //TODO comment + public function queryConversionsBySegment($segments = '') + { + if(!empty($segments)) + { + $segments = ", ". $segments; + } + $query = "SELECT idgoal, + count(*) as nb_conversions, + sum(revenue) as revenue + $segments + FROM ".$this->logConversionTable." + WHERE visit_server_date = ? + AND idsite = ? + GROUP BY idgoal $segments"; + $query = $this->db->query($query, array( $this->strDateStart, $this->idsite )); + return $query; + } + + public function queryConversionsBySingleSegment($segment) + { + $query = "SELECT idgoal, + count(*) as nb_conversions, + sum(revenue) as revenue, + $segment as label + FROM ".$this->logConversionTable." + WHERE visit_server_date = ? + AND idsite = ? + GROUP BY idgoal, label"; + $query = $this->db->query($query, array( $this->strDateStart, $this->idsite )); + return $query; + } + + /** + * Input: + * array( + * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X, + * Piwik_Archive::INDEX_GOALS => array( + * idgoal1 => array( [...] ), + * idgoal2 => array( [...] ), + * ), + * [...] ), + * LABEL2 => array( Piwik_Archive::INDEX_NB_VISITS => Y, [...] ) + * ); + * + * Output: + * array( + * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X, + * + * Piwik_Archive::INDEX_GOALS => array( + * idgoal1 => array( [...] ), + * idgoal2 => array( [...] ), + * ), + * [...] ), + * LABEL2 => array( Piwik_Archive::INDEX_NB_VISITS => Y, [...] ) + * ); + * ) + * @param array by reference, will be modified + * @return void (array by reference is modified) + */ + function enrichConversionsByLabelArray(&$interestByLabel) + { + foreach($interestByLabel as $label => &$values) + { + if(isset($values[Piwik_Archive::INDEX_GOALS])) + { + $revenue = $conversions = 0; + foreach($values[Piwik_Archive::INDEX_GOALS] as $idgoal => $goalValues) + { + $revenue += $goalValues[Piwik_Archive::INDEX_GOAL_REVENUE]; + $conversions += $goalValues[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS]; + } + $values[Piwik_Archive::INDEX_NB_CONVERSIONS] = $conversions; + $values[Piwik_Archive::INDEX_REVENUE] = $revenue; + } + } + } + + /** + * @param array $interestByLabelAndSubLabel + * @return void (array by reference is modified) + */ + function enrichConversionsByLabelArrayHasTwoLevels(&$interestByLabelAndSubLabel) + { + foreach($interestByLabelAndSubLabel as $mainLabel => &$interestBySubLabel) + { + $this->enrichConversionsByLabelArray($interestBySubLabel); + } + } + + function updateGoalStats($newRowToAdd, &$oldRowToUpdate) + { + $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS] += $newRowToAdd['nb_conversions']; + $oldRowToUpdate[Piwik_Archive::INDEX_GOAL_REVENUE] += $newRowToAdd['revenue']; + } + + function getNewGoalRow() + { + return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0, + Piwik_Archive::INDEX_GOAL_REVENUE => 0, + ); + } + + function getGoalRowFromQueryRow($queryRow) + { + return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => $queryRow['nb_conversions'], + Piwik_Archive::INDEX_GOAL_REVENUE => $queryRow['revenue'], + ); } } diff --git a/core/ArchiveProcessing/Period.php b/core/ArchiveProcessing/Period.php index 7e834bec62..a4deaa3faf 100644 --- a/core/ArchiveProcessing/Period.php +++ b/core/ArchiveProcessing/Period.php @@ -119,7 +119,7 @@ class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing /** - * This powerful method will compute the sum of DataTables over the period for the given fields $aRecordName. + * This method will compute the sum of DataTables over the period for the given fields $aRecordName. * The resulting DataTable will be then added to queue of data to be recorded in the database. * It will usually be called in a plugin that listens to the hook 'ArchiveProcessing_Period.compute' * @@ -223,20 +223,24 @@ class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing { $this->archiveNumericValuesMax( 'max_actions' ); $toSum = array( - 'nb_uniq_visitors', + 'nb_uniq_visitors', //TODO fix 'nb_visits', 'nb_actions', 'sum_visit_length', 'bounce_count', + 'nb_visits_converted', ); $record = $this->archiveNumericValuesSum($toSum); - $this->isThereSomeVisits = ($record['nb_visits']->value != 0); + $nbVisits = $record['nb_visits']->value; + $nbVisitsConverted = $record['nb_visits_converted']->value; + $this->isThereSomeVisits = ( $nbVisits!= 0); if($this->isThereSomeVisits === false) { return; } - + $this->setNumberOfVisits($nbVisits); + $this->setNumberOfVisitsConverted($nbVisitsConverted); Piwik_PostEvent('ArchiveProcessing_Period.compute', $this); } @@ -250,21 +254,25 @@ class Piwik_ArchiveProcessing_Period extends Piwik_ArchiveProcessing { parent::postCompute(); - // we delete records that are now out of date - // in the case of a period we delete archives that were archived before the end of the period - // and only if they are at least 1 day old (so we don't delete archives computed today that may be stil valid) - $blobTable = $this->tableArchiveBlob->getTableName(); - $numericTable = $this->tableArchiveNumeric->getTableName(); - - $query = " DELETE - FROM %s - WHERE period > ? - AND DATE(ts_archived) <= date2 - AND date(ts_archived) < date_sub(CURRENT_DATE(), INTERVAL 1 DAY) - "; - - Zend_Registry::get('db')->query(sprintf($query, $blobTable), Piwik::$idPeriods['day']); - Zend_Registry::get('db')->query(sprintf($query, $numericTable), Piwik::$idPeriods['day']); + //TODO should be done in a different asynchronous job + if(rand(0, 15) == 5) + { + // we delete records that are now out of date + // in the case of a period we delete archives that were archived before the end of the period + // and only if they are at least 1 day old (so we don't delete archives computed today that may be stil valid) + $blobTable = $this->tableArchiveBlob->getTableName(); + $numericTable = $this->tableArchiveNumeric->getTableName(); + + $query = " DELETE + FROM %s + WHERE period > ? + AND DATE(ts_archived) <= date2 + AND date(ts_archived) < date_sub(CURRENT_DATE(), INTERVAL 1 DAY) + "; + + Zend_Registry::get('db')->query(sprintf($query, $blobTable), Piwik::$idPeriods['day']); + Zend_Registry::get('db')->query(sprintf($query, $numericTable), Piwik::$idPeriods['day']); + } } } diff --git a/core/ArchiveProcessing/Record/BlobArray.php b/core/ArchiveProcessing/Record/BlobArray.php index 4e3165489e..de187ac62d 100644 --- a/core/ArchiveProcessing/Record/BlobArray.php +++ b/core/ArchiveProcessing/Record/BlobArray.php @@ -32,7 +32,6 @@ */ class Piwik_ArchiveProcessing_Record_BlobArray extends Piwik_ArchiveProcessing_Record { - function __construct( $name, $aValue) { foreach($aValue as $id => $value) @@ -49,13 +48,14 @@ class Piwik_ArchiveProcessing_Record_BlobArray extends Piwik_ArchiveProcessing_R $newName = $name . '_' . $id; } $record = new Piwik_ArchiveProcessing_Record_Blob( $newName, $value ); - } } + public function __toString() { throw new Exception( 'Not valid' ); } + public function delete() { throw new Exception( 'Not valid' ); diff --git a/core/Common.php b/core/Common.php index 2cca73ce91..7b43f2e693 100644 --- a/core/Common.php +++ b/core/Common.php @@ -124,22 +124,22 @@ class Piwik_Common if( false !== strpos($value, '=')) { $exploded = explode('=',$value); - $name = $exploded[0];
-
- // if array without indexes
- if( substr($name,-2,2) == '[]' )
- {
- $name = substr($name, 0, -2);
- if( isset($nameToValue[$name]) == false || is_array($nameToValue[$name]) == false )
- {
- $nameToValue[$name] = array();
- }
- array_push($nameToValue[$name],$exploded[1]);
- }
- else
- {
- $nameToValue[$name] = $exploded[1];
- }
+ $name = $exploded[0]; + + // if array without indexes + if( substr($name,-2,2) == '[]' ) + { + $name = substr($name, 0, -2); + if( isset($nameToValue[$name]) == false || is_array($nameToValue[$name]) == false ) + { + $nameToValue[$name] = array(); + } + array_push($nameToValue[$name],$exploded[1]); + } + else + { + $nameToValue[$name] = $exploded[1]; + } } } return $nameToValue; diff --git a/core/Controller.php b/core/Controller.php index 95356c27bf..e544796d58 100644 --- a/core/Controller.php +++ b/core/Controller.php @@ -9,6 +9,7 @@ * @package Piwik */ +require_once "ViewDataTable.php"; /** * Parent class of all plugins Controllers (located in /plugins/PluginName/Controller.php * It defines some helper functions controllers can use. @@ -78,6 +79,15 @@ abstract class Piwik_Controller */ protected function renderView( Piwik_ViewDataTable $view, $fetch) { + Piwik_PostEvent( 'Controller.renderView', + $this, + array( 'view' => $view, + 'controllerName' => $view->getCurrentControllerName(), + 'controllerAction' => $view->getCurrentControllerAction(), + 'apiMethodToRequestDataTable' => $view->getApiMethodToRequestDataTable(), + 'controllerActionCalledWhenRequestSubTable' => $view->getControllerActionCalledWhenRequestSubTable(), + ) + ); $view->main(); $rendered = $view->getView()->render(); if($fetch) diff --git a/core/DataTable.php b/core/DataTable.php index 2d94c53ee4..3f52385512 100644 --- a/core/DataTable.php +++ b/core/DataTable.php @@ -170,6 +170,13 @@ class Piwik_DataTable protected $indexNotUpToDate = false; /** + * Column name of last time the table was sorted + * + * @var string + */ + protected $tableSortedBy = false; + + /** * List of Piwik_DataTable_Filter queued to this table * * @var array @@ -218,10 +225,12 @@ class Piwik_DataTable * Sort the dataTable rows using the php callback function * * @param string $functionCallback + * @param string $columnSortedBy The column name. Used to then ask the datatable what column are you sorted by */ - public function sort( $functionCallback ) + public function sort( $functionCallback, $columnSortedBy ) { $this->indexNotUpToDate = true; + $this->tableSortedBy = $columnSortedBy; usort( $this->rows, $functionCallback ); if($this->enableRecursiveSort === true) @@ -232,12 +241,17 @@ class Piwik_DataTable { $table = Piwik_DataTable_Manager::getInstance()->getTable($idSubtable); $table->enableRecursiveSort(); - $table->sort($functionCallback); + $table->sort($functionCallback, $columnSortedBy); } } } } + public function getSortedByColumnName() + { + return $this->tableSortedBy; + } + /** * Enables the recursive sort. Means that when using $table->sort() * it will also sort all subtables using the same callback diff --git a/core/DataTable/Filter.php b/core/DataTable/Filter.php index de03b1cfec..46984d5426 100644 --- a/core/DataTable/Filter.php +++ b/core/DataTable/Filter.php @@ -59,4 +59,5 @@ require_once "DataTable/Filter/AddSummaryRow.php"; require_once "DataTable/Filter/ReplaceSummaryRowLabel.php"; require_once "DataTable/Filter/ExactMatch.php"; require_once "DataTable/Filter/SafeDecodeLabel.php"; -require_once "DataTable/Filter/AddColumnsWhenShowAllColumns.php";
+require_once "DataTable/Filter/AddColumnsWhenShowAllColumns.php"; +require_once "DataTable/Filter/UpdateColumnsWhenShowAllGoals.php"; diff --git a/core/DataTable/Filter/AddColumnsWhenShowAllColumns.php b/core/DataTable/Filter/AddColumnsWhenShowAllColumns.php index fd5810512e..7e5427570f 100644 --- a/core/DataTable/Filter/AddColumnsWhenShowAllColumns.php +++ b/core/DataTable/Filter/AddColumnsWhenShowAllColumns.php @@ -24,4 +24,5 @@ class Piwik_DataTable_Filter_AddColumnsWhenShowAllColumns extends Piwik_DataTabl $row->addColumn('bounce_rate', $bounceRate); } } -}
\ No newline at end of file +} + diff --git a/core/DataTable/Filter/AddSummaryRow.php b/core/DataTable/Filter/AddSummaryRow.php index f5763ffa07..a62c5dc2da 100644 --- a/core/DataTable/Filter/AddSummaryRow.php +++ b/core/DataTable/Filter/AddSummaryRow.php @@ -60,8 +60,8 @@ class Piwik_DataTable_Filter_AddSummaryRow extends Piwik_DataTable_Filter $newRow->sumRow($rows[$i]); } } - $newRow->addColumn('label', $this->labelSummaryRow); + $newRow->addColumn('label', $this->labelSummaryRow); $filter = new Piwik_DataTable_Filter_Limit($this->table, 0, $this->startRowToSummarize); $this->table->addSummaryRow($newRow); } diff --git a/core/DataTable/Filter/ExactMatch.php b/core/DataTable/Filter/ExactMatch.php index 1027fc62a6..3f68658d61 100644 --- a/core/DataTable/Filter/ExactMatch.php +++ b/core/DataTable/Filter/ExactMatch.php @@ -1,52 +1,52 @@ -<?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_DataTable
- */
-
-/**
- * Delete all rows for which the given $columnToFilter do not equal $patternToSearch
- * This filter can be used on both integer and string columns.
- * You can pass an array of integers in $patternToSearch parameter.
- *
- * @package Piwik_DataTable
- * @subpackage Piwik_DataTable_Filter
- */
-class Piwik_DataTable_Filter_ExactMatch extends Piwik_DataTable_Filter
-{
- private $columnToFilter;
- private $patternToSearch;
-
- public function __construct( $table, $columnToFilter, $patternToSearch )
- {
- parent::__construct($table);
- $this->patternToSearch = $patternToSearch;
- $this->columnToFilter = $columnToFilter;
- $this->filter();
- }
-
- protected function filter()
- {
- foreach($this->table->getRows() as $key => $row)
- {
- if( is_array($this->patternToSearch) )
- {
- if( in_array($row->getColumn($this->columnToFilter), $this->patternToSearch) == false )
- {
- $this->table->deleteRow($key);
- }
- }
- else if( $row->getColumn($this->columnToFilter) != $this->patternToSearch )
- {
- $k = $row->getColumn($this->columnToFilter);
- $this->table->deleteRow($key);
- }
- }
- }
-}
-
+<?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_DataTable + */ + +/** + * Delete all rows for which the given $columnToFilter do not equal $patternToSearch + * This filter can be used on both integer and string columns. + * You can pass an array of integers in $patternToSearch parameter. + * + * @package Piwik_DataTable + * @subpackage Piwik_DataTable_Filter + */ +class Piwik_DataTable_Filter_ExactMatch extends Piwik_DataTable_Filter +{ + private $columnToFilter; + private $patternToSearch; + + public function __construct( $table, $columnToFilter, $patternToSearch ) + { + parent::__construct($table); + $this->patternToSearch = $patternToSearch; + $this->columnToFilter = $columnToFilter; + $this->filter(); + } + + protected function filter() + { + foreach($this->table->getRows() as $key => $row) + { + if( is_array($this->patternToSearch) ) + { + if( in_array($row->getColumn($this->columnToFilter), $this->patternToSearch) == false ) + { + $this->table->deleteRow($key); + } + } + else if( $row->getColumn($this->columnToFilter) != $this->patternToSearch ) + { + $k = $row->getColumn($this->columnToFilter); + $this->table->deleteRow($key); + } + } + } +} + diff --git a/core/DataTable/Filter/ExcludeLowPopulation.php b/core/DataTable/Filter/ExcludeLowPopulation.php index cb038d6d78..94acd0baea 100644 --- a/core/DataTable/Filter/ExcludeLowPopulation.php +++ b/core/DataTable/Filter/ExcludeLowPopulation.php @@ -24,21 +24,29 @@ class Piwik_DataTable_Filter_ExcludeLowPopulation extends Piwik_DataTable_Filter static public $minimumValue; public function __construct( $table, $columnToFilter, $minimumValue ) { + parent::__construct($table); $this->columnToFilter = $columnToFilter; + + if($minimumValue == 0) + { + $minimumPercentageThreshold = 0.02; + $allValues = $this->table->getColumn($this->columnToFilter); + $sumValues = array_sum($allValues); + $minimumValue = $sumValues * $minimumPercentageThreshold; + } self::$minimumValue = $minimumValue; - parent::__construct($table); - $this->filter(); + if(self::$minimumValue > 1) + { + $this->filter(); + } } function filter() { - $function = array("Piwik_DataTable_Filter_ExcludeLowPopulation", - "excludeLowPopulation"); - $filter = new Piwik_DataTable_Filter_ColumnCallbackDeleteRow( $this->table, $this->columnToFilter, - $function + array("Piwik_DataTable_Filter_ExcludeLowPopulation", "excludeLowPopulation") ); } diff --git a/core/DataTable/Filter/ReplaceColumnNames.php b/core/DataTable/Filter/ReplaceColumnNames.php index f1bc174c4a..8d25c960ac 100644 --- a/core/DataTable/Filter/ReplaceColumnNames.php +++ b/core/DataTable/Filter/ReplaceColumnNames.php @@ -55,7 +55,9 @@ class Piwik_DataTable_Filter_ReplaceColumnNames extends Piwik_DataTable_Filter { foreach($table->getRows() as $key => $row) { - $this->renameColumns($row); + $oldColumns = $row->getColumns(); + $newColumns = $this->getRenamedColumns($oldColumns); + $row->setColumns( $newColumns ); try { $subTable = Piwik_DataTable_Manager::getInstance()->getTable( $row->getIdSubDataTable() ); $this->filterTable($subTable); @@ -65,18 +67,31 @@ class Piwik_DataTable_Filter_ReplaceColumnNames extends Piwik_DataTable_Filter } } - protected function renameColumns($row) + protected function getRenamedColumns($columns) { - $columns = $row->getColumns(); - foreach($this->mappingToApply as $oldName => $newName) + $newColumns = array(); + foreach($columns as $columnName => $columnValue) { - if(isset($columns[$oldName])) + if(isset(Piwik_Archive::$mappingFromIdToName[$columnName])) { - $columns[$newName] = $columns[$oldName]; - unset($columns[$oldName]); + $columnName = Piwik_Archive::$mappingFromIdToName[$columnName]; + if($columnName == 'goals') + { + $newSubColumns = array(); + foreach($columnValue as $idGoal => $goalValues) + { + foreach($goalValues as $id => $goalValue) + { + $subColumnName = Piwik_Archive::$mappingFromIdToNameGoal[$id]; + $newSubColumns['idgoal='.$idGoal][$subColumnName] = $goalValue; + } + } + $columnValue = $newSubColumns; + } } + $newColumns[$columnName] = $columnValue; } - $row->setColumns($columns); + return $newColumns; } } diff --git a/core/DataTable/Filter/Sort.php b/core/DataTable/Filter/Sort.php index 805a8ed28e..148bb5b6e1 100644 --- a/core/DataTable/Filter/Sort.php +++ b/core/DataTable/Filter/Sort.php @@ -85,6 +85,40 @@ class Piwik_DataTable_Filter_Sort extends Piwik_DataTable_Filter ); } + /** + * @param Piwik_DataTable_Row + */ + protected function selectColumnToSort($row) + { + $value = $row->getColumn($this->columnToSort); + if($value !== false) + { + return $this->columnToSort; + } + + // sorting by "nb_visits" but the index is Piwik_Archive::INDEX_NB_VISITS in the table + if(isset(Piwik_Archive::$mappingFromNameToId[$this->columnToSort])) + { + $column = Piwik_Archive::$mappingFromNameToId[$this->columnToSort]; + $value = $row->getColumn($column); + + if($value !== false) + { + return $column; + } + } + + // eg. was previously sorted by revenue_per_visit, but this table + // doesn't have this column; defaults with nb_visits + $column = Piwik_Archive::INDEX_NB_VISITS; + $value = $row->getColumn($column); + if($value !== false) + { + return $column; + } + + return false; + } protected function filter() { if($this->table instanceof Piwik_DataTable_Simple) @@ -97,26 +131,14 @@ class Piwik_DataTable_Filter_Sort extends Piwik_DataTable_Filter return; } $row = current($rows); - $value = $row->getColumn($this->columnToSort); + $this->columnToSort = $this->selectColumnToSort($row); - if($value === false) + if($this->columnToSort === false) { - if(!isset(Piwik_Archive::$mappingFromNameToId[$this->columnToSort])) - { - // we don't throw the exception because we sometimes export a DataTable without a column labelled '2' - // and when the generic filters tries to sort by default using this column 2, this shouldnt raise an exception... - //throw new Exception("The column to sort by '".$this->columnToSort."' is unknown in the row ". implode(array_keys($row->getColumns()), ',')); - return; - } - // case we are sorting by "nb_visits" but the column is still integer indexed - $this->columnToSort = Piwik_Archive::$mappingFromNameToId[$this->columnToSort]; - $value = $row->getColumn($this->columnToSort); - if($value === false) - { - return; - } + return; } + $value = $row->getColumn($this->columnToSort); if( Piwik::isNumeric($value)) { $methodToUse = "sort"; @@ -132,7 +154,7 @@ class Piwik_DataTable_Filter_Sort extends Piwik_DataTable_Filter $methodToUse = "sortString"; } } - $this->table->sort( array($this,$methodToUse) ); + $this->table->sort( array($this,$methodToUse), $this->columnToSort ); } } diff --git a/core/DataTable/Filter/UpdateColumnsWhenShowAllGoals.php b/core/DataTable/Filter/UpdateColumnsWhenShowAllGoals.php new file mode 100644 index 0000000000..61ce48a8cc --- /dev/null +++ b/core/DataTable/Filter/UpdateColumnsWhenShowAllGoals.php @@ -0,0 +1,115 @@ +<?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_DataTable + */ + +/** + * @package Piwik_DataTable + * @subpackage Piwik_DataTable_Filter + */ +class Piwik_DataTable_Filter_UpdateColumnsWhenShowAllGoals extends Piwik_DataTable_Filter +{ + protected $mappingIdToNameGoal; + + public function __construct( $table, $mappingToApply = null ) + { + parent::__construct($table); + $this->mappingIdToNameGoal = Piwik_Archive::$mappingFromIdToNameGoal; + $this->filter(); + } + + protected function filter() + { + $invalidDivision = 'N/A'; + $roundingPrecision = 2; + $expectedColumns = array(); + foreach($this->table->getRows() as $key => $row) + { + $currentColumns = $row->getColumns(); + $newColumns = array(); + + $nbVisits = 0; + // visits could be undefined when there is a convertion but no visit + if(isset($currentColumns[Piwik_Archive::INDEX_NB_VISITS])) + { + $nbVisits = $currentColumns[Piwik_Archive::INDEX_NB_VISITS]; + } + $newColumns['nb_visits'] = $nbVisits; + $newColumns['label'] = $currentColumns['label']; + + if(isset($currentColumns[Piwik_Archive::INDEX_GOALS])) + { + $nbVisitsConverted = $revenue = 0; + if(isset($currentColumns[Piwik_Archive::INDEX_NB_VISITS_CONVERTED])) + { + $nbVisitsConverted = $currentColumns[Piwik_Archive::INDEX_NB_VISITS_CONVERTED]; + $revenue = $currentColumns[Piwik_Archive::INDEX_REVENUE]; + } + + if($nbVisitsConverted == 0) + { + $conversionRate = $invalidDivision; + } + else + { + $conversionRate = round(100 * $nbVisitsConverted / $nbVisits, $roundingPrecision); + } + + if($nbVisits == 0) + { + $revenuePerVisit = $invalidDivision; + } + else + { + $revenuePerVisit = round( $revenue / $nbVisits, $roundingPrecision ); + } + foreach($currentColumns[Piwik_Archive::INDEX_GOALS] as $goalId => $columnValue) + { + $name = 'goal_' . $goalId . '_conversion_rate'; + if($nbVisits == 0) + { + $value = $invalidDivision; + } + else + { + $value = round(100 * $columnValue[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS] / $nbVisits, $roundingPrecision); + } + $newColumns[$name] = $value; + $expectedColumns[$name] = true; + + $name = 'goal_' . $goalId . '_nb_conversions'; + $newColumns[$name] = $columnValue[Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS]; + $expectedColumns[$name] = true; + } + $newColumns['revenue_per_visit'] = $revenuePerVisit; + $newColumns['goals_conversion_rate'] = $conversionRate; + } + + $row->setColumns($newColumns); + } + $expectedColumns['revenue_per_visit'] = true; + $expectedColumns['goals_conversion_rate'] = true; + + // make sure all goals values are set, 0 by default + // if no value then sorting would put at the end + $expectedColumns = array_keys($expectedColumns); + $rows = $this->table->getRows(); + foreach($rows as &$row) + { + foreach($expectedColumns as $name) + { + if(false === $row->getColumn($name)) + { + $row->addColumn($name, 0); + } + } + } + } +} + diff --git a/core/DataTable/Renderer/Csv.php b/core/DataTable/Renderer/Csv.php index e686ba9023..d311981c51 100644 --- a/core/DataTable/Renderer/Csv.php +++ b/core/DataTable/Renderer/Csv.php @@ -132,11 +132,28 @@ class Piwik_DataTable_Renderer_Csv extends Piwik_DataTable_Renderer $columns = $row->getColumns(); foreach($columns as $name => $value) { - if(!isset($allColumns[$name])) + //goals => array( 'idgoal=1' =>array(..), 'idgoal=2' => array(..)) + if(is_array($value)) + { + foreach($value as $key => $subValues) + { + if(is_array($subValues)) + { + foreach($subValues as $subKey => $subValue) + { + // goals_idgoal=1 + $columnName = $name . "_" . $key . "_" . $subKey; + $allColumns[$columnName] = true; + $csvRow[$columnName] = $subValue; + } + } + } + } + else { $allColumns[$name] = true; + $csvRow[$name] = $value; } - $csvRow[$name] = $value; } if($this->exportMetadata) diff --git a/core/DataTable/Renderer/Php.php b/core/DataTable/Renderer/Php.php index 5e472d7519..fee43640f1 100644 --- a/core/DataTable/Renderer/Php.php +++ b/core/DataTable/Renderer/Php.php @@ -173,7 +173,7 @@ class Piwik_DataTable_Renderer_Php extends Piwik_DataTable_Renderer 'columns' => $row->getColumns(), 'metadata' => $row->getMetadata(), 'idsubdatatable' => $row->getIdSubDataTable(), - ); + ); if($this->renderSubTables && $row->getIdSubDataTable() !== null) diff --git a/core/DataTable/Renderer/Rss.php b/core/DataTable/Renderer/Rss.php index 18574dfcd5..7f7efa1b23 100644 --- a/core/DataTable/Renderer/Rss.php +++ b/core/DataTable/Renderer/Rss.php @@ -97,7 +97,7 @@ class Piwik_DataTable_Renderer_Rss extends Piwik_DataTable_Renderer <lastBuildDate>$generationDate</lastBuildDate>"; return $header; } - + protected function renderDataTable($table) { @@ -110,7 +110,7 @@ class Piwik_DataTable_Renderer_Rss extends Piwik_DataTable_Renderer { $table->deleteColumn('label'); } - + $i = 1; $tableStructure = array(); @@ -125,6 +125,12 @@ class Piwik_DataTable_Renderer_Rss extends Piwik_DataTable_Renderer { foreach($row->getColumns() as $column => $value) { + // for example, goals data is array: not supported in export RSS + // in the future we shall reuse ViewDataTable for html exports in RSS anyway + if(is_array($value)) + { + continue; + } $allColumns[$column] = true; $tableStructure[$i][$column] = $value; } @@ -146,14 +152,14 @@ class Piwik_DataTable_Renderer_Rss extends Piwik_DataTable_Renderer foreach($tableStructure as $row) { $html .= "\n\n<tr>"; - foreach($allColumns as $name => $toDisplay) + foreach($allColumns as $columnName => $toDisplay) { if($toDisplay !== false) { $value = "-"; - if(isset($row[$name])) + if(isset($row[$columnName])) { - $value = urldecode($row[$name]); + $value = urldecode($row[$columnName]); } $html .= "\n\t<td>$value</td>"; diff --git a/core/DataTable/Renderer/Xml.php b/core/DataTable/Renderer/Xml.php index 425f506ae3..1b3a77866d 100644 --- a/core/DataTable/Renderer/Xml.php +++ b/core/DataTable/Renderer/Xml.php @@ -29,6 +29,7 @@ class Piwik_DataTable_Renderer_Xml extends Piwik_DataTable_Renderer function render() { +// var_dump($this->table);exit; return $this->renderTable($this->table); } @@ -41,7 +42,7 @@ class Piwik_DataTable_Renderer_Xml extends Piwik_DataTable_Renderer protected function renderTable($table, $returnOnlyDataTableXml = false, $prefixLines = '') { $array = $this->getArrayFromDataTable($table); - +// var_dump($array);exit; if($table instanceof Piwik_DataTable_Array) { $out = $this->renderDataTableArray($table, $array, $prefixLines); @@ -235,9 +236,21 @@ class Piwik_DataTable_Renderer_Xml extends Piwik_DataTable_Renderer protected function renderDataTable( $array, $prefixLine = "" ) { $out = ''; - foreach($array as $row) + foreach($array as $rowId => $row) { - $out .= $prefixLine."\t<row>"; + if(!is_array($row)) + { + $value = $this->formatValue($row); + $out .= $prefixLine."\t\t<$rowId>".$value."</$rowId>\n"; + continue; + } + $rowAttribute = ''; + if(($equalFound = strstr($rowId, '=')) !== false) + { + $rowAttribute = explode('=', $rowId); + $rowAttribute = " " . $rowAttribute[0] . "='" . $rowAttribute[1] . "'"; + } + $out .= $prefixLine."\t<row$rowAttribute>"; if(count($row) === 1 && key($row) === 0) diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php index 9c5edfdd33..abc6aea288 100644 --- a/core/DataTable/Row.php +++ b/core/DataTable/Row.php @@ -329,39 +329,49 @@ class Piwik_DataTable_Row */ public function sumRow( Piwik_DataTable_Row $rowToSum ) { - foreach($rowToSum->getColumns() as $name => $value) + foreach($rowToSum->getColumns() as $columnToSumName => $columnToSumValue) { - if($name != 'label') + if($columnToSumName != 'label') { - if(Piwik::isNumeric($value)) - { - $current = $this->getColumn($name); - if($current === false) - { - $current = 0; - } - $this->setColumn( $name, $current + $value); - } - elseif(is_array($value)) + $thisColumnValue = $this->getColumn($columnToSumName); + $newValue = $this->sumRowArray($thisColumnValue, $columnToSumValue); + $this->setColumn( $columnToSumName, $newValue); + } + } + } + + protected function sumRowArray( $thisColumnValue, $columnToSumValue ) + { + $newValue = 0; + if(Piwik::isNumeric($columnToSumValue)) + { + if($thisColumnValue === false) + { + $thisColumnValue = 0; + } + $newValue = $thisColumnValue + $columnToSumValue; + } + elseif(is_array($columnToSumValue)) + { + $newValue = array(); + if($thisColumnValue == false) + { + $newValue = $columnToSumValue; + } + else + { + $newValue = $thisColumnValue; + foreach($columnToSumValue as $arrayIndex => $arrayValue) { - $current = $this->getColumn($name); - $newValue = array(); - if($current == false) + if(!isset($newValue[$arrayIndex])) { - $newValue = $value; + $newValue[$arrayIndex] = false; } - else - { - $newValue = $current; - foreach($value as $arrayIndex => $arrayValue) - { - $newValue[$arrayIndex] += $arrayValue; - } - } - $this->setColumn($name, $newValue); + $newValue[$arrayIndex] = $this->sumRowArray($newValue[$arrayIndex], $arrayValue); } } } + return $newValue; } /** @@ -381,10 +391,7 @@ class Piwik_DataTable_Row //same columns $cols1 = $row1->getColumns(); $cols2 = $row2->getColumns(); - - uksort($cols1, 'strnatcasecmp'); - uksort($cols2, 'strnatcasecmp'); - if($cols1 != $cols2) + if(array_diff($cols1, $cols2) !== array_diff($cols2, $cols1)) { return false; } diff --git a/core/Date.php b/core/Date.php index 450682df68..efcc526da4 100644 --- a/core/Date.php +++ b/core/Date.php @@ -41,6 +41,18 @@ class Piwik_Date return new Piwik_Date($dateString); } + protected $timestamp = null; + + /** + * Returns the unix timestamp of the date + * + * @return int + */ + public function getTimestamp() + { + return $this->timestamp; + } + /** * Builds a Piwik_Date object * @@ -79,16 +91,6 @@ class Piwik_Date } /** - * Returns the unix timestamp of the date - * - * @return int - */ - public function getTimestamp() - { - return $this->timestamp; - } - - /** * Returns true if the current date is older than the given $date * * @param Piwik_Date $date diff --git a/core/FrontController.php b/core/FrontController.php index 2644f02938..718f4e997c 100644 --- a/core/FrontController.php +++ b/core/FrontController.php @@ -142,7 +142,6 @@ class Piwik_FrontController { throw new Exception("Action $action not found in the controller $controllerClassName."); } - try { return call_user_func_array( array($controller, $action ), $parameters); } catch(Piwik_Access_NoAccessException $e) { diff --git a/core/Period/Range.php b/core/Period/Range.php index 03c97d3e23..d2cfb8dcf9 100644 --- a/core/Period/Range.php +++ b/core/Period/Range.php @@ -131,7 +131,6 @@ class Piwik_Period_Range extends Piwik_Period { throw new Exception("The date '$this->strDate' is not a date range. Should have the following format: 'lastN' or 'previousN' or 'YYYY-MM-DD,YYYY-MM-DD'."); } - $endSubperiod = Piwik_Period::factory($this->strPeriod, $endDate); $arrayPeriods= array(); diff --git a/core/Piwik.php b/core/Piwik.php index 998d6da08b..9b2aa70801 100644 --- a/core/Piwik.php +++ b/core/Piwik.php @@ -423,7 +423,23 @@ class Piwik static public function isNumeric($value) { - return !is_array($value) && ereg('^([-]{0,1}[0-9]{1,}[.]{0,1}[0-9]*)$', $value); + return is_numeric($value); + } + + static public function getCurrency() + { + static $symbol = null; + if(is_null($symbol)) + { + $symbol = Zend_Registry::get('config')->General->default_currency; + } + return $symbol; + } + + static public function getPrettyMoney($value) + { + $symbol = self::getCurrency(); + return sprintf("$symbol%.2f", $value); } static public function getPrettyTimeFromSeconds($numberOfSeconds) @@ -664,7 +680,8 @@ class Piwik period TINYINT UNSIGNED NULL, ts_archived DATETIME NULL, value FLOAT NULL, - PRIMARY KEY(idarchive, name) + PRIMARY KEY(idarchive, name), + KEY `index_all` (`idsite`,`date1`,`date2`,`name`,`ts_archived`) ) ", 'archive_blob' => "CREATE TABLE {$prefixTables}archive_blob ( @@ -676,7 +693,8 @@ class Piwik period TINYINT UNSIGNED NULL, ts_archived DATETIME NULL, value MEDIUMBLOB NULL, - PRIMARY KEY(idarchive, name) + PRIMARY KEY(idarchive, name), + KEY `index_all` (`idsite`,`date1`,`date2`,`name`,`ts_archived`) ) ", ); @@ -777,6 +795,11 @@ class Piwik Zend_Registry::get('access')->checkUserHasSomeAdminAccess(); } + static public function checkUserHasSomeViewAccess() + { + Zend_Registry::get('access')->checkUserHasSomeViewAccess(); + } + static public function isUserHasViewAccess( $idSites ) { try{ diff --git a/core/Plugin.php b/core/Plugin.php index f606751736..de3e3a7f72 100644 --- a/core/Plugin.php +++ b/core/Plugin.php @@ -71,15 +71,6 @@ abstract class Piwik_Plugin } /** - * Returns the names of the required plugins - * @var array - */ - public function getListRequiredPlugins() - { - return array(); - } - - /** * Returns the plugin name * @var string */ diff --git a/core/SmartyPlugins/function.logoHtml.php b/core/SmartyPlugins/function.logoHtml.php new file mode 100644 index 0000000000..85a57b02e3 --- /dev/null +++ b/core/SmartyPlugins/function.logoHtml.php @@ -0,0 +1,32 @@ +<?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 SmartyPlugins + */ + +function smarty_function_logoHtml($params, &$smarty) +{ + if(!isset($params['metadata']['logo'])) + { + return; + } + $width = $height = $alt = ''; + if(isset($params['metadata']['logoWidth'])) + { + $width = "width=".$params['metadata']['logoWidth']; + } + if(isset($params['metadata']['logoHeight'])) + { + $height = "height=".$params['metadata']['logoHeight']; + } + if(isset($params['alt'])) + { + $alt = "title='".$params['alt']."' alt='".$params['alt']."'"; + } + return " <img $alt $width $height src=".$params['metadata']['logo']." />"; +} diff --git a/core/SmartyPlugins/function.url.php b/core/SmartyPlugins/function.url.php index adadea3beb..a837a8459b 100644 --- a/core/SmartyPlugins/function.url.php +++ b/core/SmartyPlugins/function.url.php @@ -1,31 +1,31 @@ -<?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: function.url.php 525 2008-06-25 23:49:13Z matt $
- *
- * @package SmartyPlugins
- */
-
-require_once "Url.php";
-
-/**
- * Smarty {url} function plugin.
- * Generates a piwik URL with the specified parameters modified.
- *
- * Examples:
- * <pre>
- * {url module="API"} will rewrite the URL modifying the module GET parameter
- * {url module="API" method="getKeywords"} will rewrite the URL modifying the parameters module=API method=getKeywords
- * </pre>
- *
- * @see Piwik_Url::getCurrentQueryStringWithParametersModified()
- * @param $name=$value of the parameters to modify in the generated URL
- * @return string Something like index.php?module=X&action=Y
- */
-function smarty_function_url($params, &$smarty)
-{
- return htmlspecialchars(Piwik_Url::getCurrentScriptName() . Piwik_Url::getCurrentQueryStringWithParametersModified( $params ));
-}
+<?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: function.url.php 525 2008-06-25 23:49:13Z matt $ + * + * @package SmartyPlugins + */ + +require_once "Url.php"; + +/** + * Smarty {url} function plugin. + * Generates a piwik URL with the specified parameters modified. + * + * Examples: + * <pre> + * {url module="API"} will rewrite the URL modifying the module GET parameter + * {url module="API" method="getKeywords"} will rewrite the URL modifying the parameters module=API method=getKeywords + * </pre> + * + * @see Piwik_Url::getCurrentQueryStringWithParametersModified() + * @param $name=$value of the parameters to modify in the generated URL + * @return string Something like index.php?module=X&action=Y + */ +function smarty_function_url($params, &$smarty) +{ + return htmlspecialchars(Piwik_Url::getCurrentScriptName() . Piwik_Url::getCurrentQueryStringWithParametersModified( $params )); +} diff --git a/core/Timer.php b/core/Timer.php index 783dcff6ea..07c2adbd01 100644 --- a/core/Timer.php +++ b/core/Timer.php @@ -29,12 +29,12 @@ class Piwik_Timer $this->memoryStart = $this->getMemoryUsage(); } - public function getTime($decimals = 2) + public function getTime($decimals = 3) { return number_format($this->getMicrotime() - $this->timerStart, $decimals, '.', ''); } - public function getTimeMs($decimals = 2) + public function getTimeMs($decimals = 3) { return number_format(1000*($this->getMicrotime() - $this->timerStart), $decimals, '.', ''); } diff --git a/core/Tracker.php b/core/Tracker.php index 171871efe3..5c3b52e8a3 100644 --- a/core/Tracker.php +++ b/core/Tracker.php @@ -11,29 +11,10 @@ /** * Class used by the logging script piwik.php called by the javascript tag. - * Handles the visitor & his/her actions on the website, saves the data in the DB, saves information in the cookie, etc. + * Handles the visitor & his/her actions on the website, saves the data in the DB, + * saves information in the cookie, etc. * - * To maximise the performance of the logging module, we use different techniques. - * - * On the PHP-only side: - * - minimize the number of external files included. - * Ideally only one (the configuration file) in all the normal cases. - * We load the Loggers only when an error occurs ; this error is logged in the DB/File/etc - * depending on the loggers settings in the configuration file. - * - we may have to include external classes but we try to include only very - * simple code without any dependency, so that we could simply write a script - * that would merge all this simple code into a big piwik.php file. - * - * On the Database-related side: - * - write all the SQL queries without using any DB abstraction layer. Manual filtering of input values. - * - minimize the number of SQL queries necessary to complete the algorithm. - * - carefully index the tables used - * - try to have fixed length rows - * - * [ - use a partitionning by date for the tables ] - * - * Configuration options for the statsLogEngine module: - * - use_cookie ; defines if we try to get/set a cookie to help recognize a unique visitor + * We try to include as little files as possible (no dependency on 3rd party modules). * * @package Piwik_Tracker */ @@ -57,6 +38,12 @@ class Piwik_Tracker const COOKIE_INDEX_TIMESTAMP_FIRST_ACTION = 3; const COOKIE_INDEX_ID_VISIT = 4; const COOKIE_INDEX_ID_LAST_ACTION = 5; + const COOKIE_INDEX_REFERER_ID_VISIT = 6; + const COOKIE_INDEX_REFERER_TIMESTAMP = 7; + const COOKIE_INDEX_REFERER_TYPE = 8; + const COOKIE_INDEX_REFERER_NAME = 9; + const COOKIE_INDEX_REFERER_KEYWORD = 10; + const COOKIE_INDEX_VISITOR_RETURNING = 11; public function __construct() {} @@ -68,15 +55,27 @@ class Piwik_Tracker { try { self::connectDatabase(); + $visit = $this->getNewVisitObject(); $visit->handle(); + unset($visit); } catch (PDOException $e) { $this->setState(self::STATE_LOGGING_DISABLE); - } + } } $this->end(); } + /** + * Returns the date in the "Y-m-d H:i:s" PHP format + * @return string + */ + public static function getDatetimeFromTimestamp($timestamp) + { + return date("Y-m-d H:i:s", $timestamp); + } + + protected function init() { $this->loadTrackerPlugins(); @@ -155,7 +154,10 @@ class Piwik_Tracker self::$db = $db; } - public static function getDb() + /** + * @return Piwik_Tracker_Db + */ + public static function getDatabase() { return self::$db; } @@ -188,7 +190,6 @@ class Piwik_Tracker throw new Exception("The Visit object set in the plugin must implement Piwik_Tracker_Visit_Interface"); } - $visit->setDb(self::$db); return $visit; } diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php index a50baa06b4..970f2c0189 100644 --- a/core/Tracker/Action.php +++ b/core/Tracker/Action.php @@ -16,7 +16,7 @@ * @package Piwik_Tracker */ interface Piwik_Tracker_Action_Interface { - public function getActionId(); + public function getIdAction(); public function record( $idVisit, $idRefererAction, $timeSpentRefererAction ); public function setIdSite( $idSite ); } @@ -45,11 +45,12 @@ interface Piwik_Tracker_Action_Interface { */ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface { - private $actionName; private $url; - private $defaultActionName; - private $nameDownloadOutlink; private $idSite; + private $idLinkVisitAction; + private $finalActionName; + private $actionType; + private $idAction = null; /** * 3 types of action, Standard action / Download / Outlink click @@ -58,28 +59,20 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface const TYPE_DOWNLOAD = 3; const TYPE_OUTLINK = 2; + protected function getDefaultActionName() + { + return Piwik_Tracker_Config::getInstance()->Tracker['default_action_name']; + } + /** - * @param Piwik_Tracker_Db Database object to be used + * Returns URL of the page currently being tracked, or the file being downloaded, or the outlink being clicked + * @return string */ - function __construct( $db ) + public function getUrl() { - $this->actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string'); - - $downloadVariableName = Piwik_Tracker_Config::getInstance()->Tracker['download_url_var_name']; - $this->downloadUrl = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string'); - - $outlinkVariableName = Piwik_Tracker_Config::getInstance()->Tracker['outlink_url_var_name']; - $this->outlinkUrl = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string'); - - $nameVariableName = Piwik_Tracker_Config::getInstance()->Tracker['download_outlink_name_var']; - $this->nameDownloadOutlink = Piwik_Common::getRequestVar( $nameVariableName, '', 'string'); - - $this->url = Piwik_Common::getRequestVar( 'url', '', 'string'); - $this->db = $db; - $this->defaultActionName = Piwik_Tracker_Config::getInstance()->Tracker['default_action_name']; + return $this->url; } - /** * Returns the idaction of the current action name. * This idaction is used in the visitor logging table to link the visit information @@ -91,9 +84,12 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface * * @return int Id action that is associated to this action name in the Actions table lookup */ - function getActionId() + function getIdAction() { - $this->loadActionId(); + if(is_null($this->idAction)) + { + $this->idAction = $this->loadActionId(); + } return $this->idAction; } @@ -116,16 +112,16 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface */ public function record( $idVisit, $idRefererAction, $timeSpentRefererAction) { - $this->db->query("/* SHARDING_ID_SITE = ".$this->idSite." */ INSERT INTO ".$this->db->prefixTable('log_link_visit_action') + Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ".$this->idSite." */ INSERT INTO ".Piwik_Tracker::getDatabase()->prefixTable('log_link_visit_action') ." (idvisit, idaction, idaction_ref, time_spent_ref_action) VALUES (?,?,?,?)", - array($idVisit, $this->idAction, $idRefererAction, $timeSpentRefererAction) + array($idVisit, $this->getIdAction(), $idRefererAction, $timeSpentRefererAction) ); - $idLinkVisitAction = $this->db->lastInsertId(); + $this->idLinkVisitAction = Piwik_Tracker::getDatabase()->lastInsertId(); $info = array( 'idSite' => $this->idSite, - 'idLinkVistAction' => $idLinkVisitAction, + 'idLinkVisitAction' => $this->idLinkVisitAction, 'idVisit' => $idVisit, 'idRefererAction' => $idRefererAction, 'timeSpentRefererAction' => $timeSpentRefererAction, @@ -137,6 +133,16 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface Piwik_PostEvent('Tracker.Action.record', $this, $info); } + /** + * Returns the ID of the newly created record in the log_link_visit_action table + * + * @return int | false + */ + public function getIdLinkVisitAction() + { + return $this->idLinkVisitAction; + } + /** * Generates the name of the action from the URL or the specified name. * Sets the name as $this->finalActionName @@ -146,34 +152,44 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface private function generateInfo() { $actionName = ''; - if(!empty($this->downloadUrl)) + + $downloadVariableName = Piwik_Tracker_Config::getInstance()->Tracker['download_url_var_name']; + $downloadUrl = Piwik_Common::getRequestVar( $downloadVariableName, '', 'string'); + + if(!empty($downloadUrl)) { $this->actionType = self::TYPE_DOWNLOAD; - $url = $this->downloadUrl; - //$actionName = $this->nameDownloadOutlink; + $url = $downloadUrl; $actionName = $url; } - elseif(!empty($this->outlinkUrl)) + else { - $this->actionType = self::TYPE_OUTLINK; - $url = $this->outlinkUrl; - //remove the last '/' character if it's present - if(substr($url,-1) == '/') + $outlinkVariableName = Piwik_Tracker_Config::getInstance()->Tracker['outlink_url_var_name']; + $outlinkUrl = Piwik_Common::getRequestVar( $outlinkVariableName, '', 'string'); + if(!empty($outlinkUrl)) { - $url = substr($url,0,-1); + $this->actionType = self::TYPE_OUTLINK; + $url = $outlinkUrl; + //remove the last '/' character if it's present + if(substr($url,-1) == '/') + { + $url = substr($url,0,-1); + } + $nameVariableName = Piwik_Tracker_Config::getInstance()->Tracker['download_outlink_name_var']; + $actionName = Piwik_Common::getRequestVar( $nameVariableName, '', 'string'); + if( empty($actionName) ) + { + $actionName = $url; + } } - $actionName = $this->nameDownloadOutlink; - if( empty($actionName) ) + else { - $actionName = $url; + $this->actionType = self::TYPE_ACTION; + $url = Piwik_Common::getRequestVar( 'url', '', 'string'); + $actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string'); } } - else - { - $this->actionType = self::TYPE_ACTION; - $url = $this->url; - $actionName = $this->actionName; - } + $this->url = $url; // the ActionName wasn't specified if( empty($actionName) ) @@ -188,7 +204,7 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface && $actionName[strlen($actionName)-1] == '/' ) { - $actionName.=$this->defaultActionName; + $actionName .= $this->getDefaultActionName(); } } @@ -224,7 +240,7 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface if(empty($actionName)) { - $actionName = $this->defaultActionName; + $actionName = $this->getDefaultActionName(); } $this->finalActionName = $actionName; @@ -233,17 +249,17 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface /** * Sets the attribute $idAction based on $finalActionName and $actionType. * - * @see getActionId() + * @see getIdAction() */ private function loadActionId() - { + { $this->generateInfo(); $name = $this->finalActionName; $type = $this->actionType; - $idAction = $this->db->fetch("/* SHARDING_ID_SITE = ".$this->idSite." */ SELECT idaction - FROM ".$this->db->prefixTable('log_action') + $idAction = Piwik_Tracker::getDatabase()->fetch("/* SHARDING_ID_SITE = ".$this->idSite." */ SELECT idaction + FROM ".Piwik_Tracker::getDatabase()->prefixTable('log_action') ." WHERE name = ? AND type = ?", array($name, $type) ); @@ -251,16 +267,18 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface // the action name has not been found, create it if($idAction === false) { - $this->db->query("/* SHARDING_ID_SITE = ".$this->idSite." */ INSERT INTO ". $this->db->prefixTable('log_action'). "( name, type ) - VALUES (?,?)",array($name,$type) ); - $idAction = $this->db->lastInsertId(); + Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ".$this->idSite." */ + INSERT INTO ". Piwik_Tracker::getDatabase()->prefixTable('log_action'). "( name, type ) + VALUES (?,?)", + array($name,$type) + ); + $idAction = Piwik_Tracker::getDatabase()->lastInsertId(); } else { $idAction = $idAction['idaction']; } - - $this->idAction = $idAction; + return $idAction; } } diff --git a/core/Tracker/Generator.php b/core/Tracker/Generator.php index 0979b02fea..8a11a7028c 100644 --- a/core/Tracker/Generator.php +++ b/core/Tracker/Generator.php @@ -355,7 +355,6 @@ class Piwik_Tracker_Generator for($i = 0; $i < $nbVisitors; $i++) { $nbActions = mt_rand(1, $nbActionsMaxPerVisit); - Piwik_Tracker_Generator_Visit::setTimestampToUse($this->getTimestampToUse()); $this->generateNewVisit(); @@ -364,7 +363,6 @@ class Piwik_Tracker_Generator $this->generateActionVisit(); $this->saveVisit(); } - $nbActionsTotal += $nbActions; } return $nbActionsTotal; @@ -655,6 +653,7 @@ class Piwik_Tracker_Generator $this->setFakeRequest(); $process = new Piwik_Tracker_Generator_Tracker; $process->main(); + unset($process); } } diff --git a/core/Tracker/Generator/Tracker.php b/core/Tracker/Generator/Tracker.php index d4457c33fc..4344e2a515 100644 --- a/core/Tracker/Generator/Tracker.php +++ b/core/Tracker/Generator/Tracker.php @@ -47,7 +47,6 @@ class Piwik_Tracker_Generator_Tracker extends Piwik_Tracker protected function getNewVisitObject() { $visit = new Piwik_Tracker_Generator_Visit(); - $visit->setDb(self::$db); return $visit; } diff --git a/core/Tracker/Generator/Visit.php b/core/Tracker/Generator/Visit.php index 41535f045c..533b0963f5 100644 --- a/core/Tracker/Generator/Visit.php +++ b/core/Tracker/Generator/Visit.php @@ -37,11 +37,6 @@ class Piwik_Tracker_Generator_Visit extends Piwik_Tracker_Visit self::$timestampToUse += mt_rand(4,1840); return self::$timestampToUse; } - - protected function getDatetimeFromTimestamp($timestamp) - { - return date("Y-m-d H:i:s",$timestamp); - } protected function updateCookie() { @@ -49,3 +44,4 @@ class Piwik_Tracker_Generator_Visit extends Piwik_Tracker_Visit } } + diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php new file mode 100644 index 0000000000..477339ad0e --- /dev/null +++ b/core/Tracker/GoalManager.php @@ -0,0 +1,147 @@ +<?php + +class Piwik_Tracker_GoalManager +{ + /** + * @var Piwik_Cookie + */ + protected $cookie = null; + /** + * @var Piwik_Tracker_Action + */ + protected $action = null; + protected $matchedGoals = array(); + protected $idsite = null; + + function __construct($action) + { + $this->action = $action; + + + } + + function setCookie($cookie) + { + $this->cookie = $cookie; + } + + //TODO goalid should be incrementing on a website basis + // load goal definitions from file + static public function getGoalDefinitions() + { + return array( + 0 => array( 'id' => 1, + 'name' => 'Downloads', + 'default_revenue' => '3', + 'pattern' => '/e/' + ), + 1 => array( 'id' => 5, + 'name' => 'hosted signups', + 'default_revenue' => false, + 'pattern' => '//' + ), + ); + } + + static public function getGoalDefinition( $idGoal ) + { + $goals = self::getGoalDefinitions(); + foreach($goals as $goal) + { + if($goal['id'] == $idGoal) + { + return $goal; + } + } + throw new Exception("The goal id = $idGoal couldn't be found."); + } + + static public function getGoalIds() + { + $goals = self::getGoalDefinitions(); + $goalIds = array(); + foreach($goals as $goal) + { + $goalIds[] = $goal['id']; + } + return $goalIds; + } + + //TODO does this code work for manually triggered goals, with custom revenue? + function detectGoals($idSite) + { + $url = $this->action->getUrl(); + $goals = $this->getGoalDefinitions($idSite); + foreach($goals as $goal) + { + $match = preg_match($goal['pattern'], $url); + if($match === 1) + { + $this->matchedGoals[] = $goal; + } + } + return count($this->matchedGoals) > 0; + } + + function recordGoals($visitorInformation) + { + $location_country = isset($visitorInformation['location_country']) ? $visitorInformation['location_country'] : Piwik_Common::getCountry(Piwik_Common::getBrowserLanguage()); + $location_continent = isset($visitorInformation['location_continent']) ? $visitorInformation['location_continent'] : Piwik_Common::getContinent($location_country); + + $goal = array( + 'idvisit' => $visitorInformation['idvisit'], + 'idsite' => $visitorInformation['idsite'], + 'visitor_idcookie' => $visitorInformation['visitor_idcookie'], + 'server_time' => Piwik_Tracker::getDatetimeFromTimestamp($visitorInformation['visit_last_action_time']), + 'visit_server_date' => $visitorInformation['visit_server_date'], + 'idaction' => $this->action->getIdAction(), + 'idlink_va' => $this->action->getIdLinkVisitAction(), + 'location_country' => $location_country, + 'location_continent'=> $location_continent, + 'url' => $this->action->getUrl(), + 'visitor_returning' => $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_VISITOR_RETURNING ), + ); + + $referer_idvisit = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_ID_VISIT ); + if($referer_idvisit !== false) + { + $goal += array( + 'referer_idvisit' => $referer_idvisit, + 'referer_visit_server_date' => date("Y-m-d", $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_TIMESTAMP )), + 'referer_type' => htmlspecialchars_decode($this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_TYPE )), + 'referer_name' => htmlspecialchars_decode($this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_NAME )), + 'referer_keyword' => htmlspecialchars_decode($this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_KEYWORD )), + ); + } + + foreach($this->matchedGoals as $matchedGoal) + { + printDebug("- Goal ".$matchedGoal['id'] ." matched. Recording..."); + $newGoal = $goal; + $newGoal['idgoal'] = $matchedGoal['id']; + $newGoal['revenue'] = $matchedGoal['default_revenue']; + printDebug($newGoal); + + $fields = implode(", ", array_keys($newGoal)); + $bindFields = substr(str_repeat( "?,",count($newGoal)),0,-1); + + try { + Piwik_Tracker::getDatabase()->query( + "INSERT INTO " . Piwik_Tracker::getDatabase()->prefixTable('log_conversion') . " ($fields) + VALUES ($bindFields) ", array_values($newGoal) + ); + } catch( Exception $e) { + if(strpos($e->getMessage(), '1062') !== false) + { + // integrity violation when same visit converts to the same goal twice + printDebug("--> Goal already recorded for this (idvisit, idgoal)"); + } + else + { + throw $e; + } + } + //$idlog_goal = Piwik_Tracker::getDatabase()->lastInsertId(); + } + } +}
\ No newline at end of file diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index d7bcc8832d..087bb1a0fc 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -12,7 +12,6 @@ interface Piwik_Tracker_Visit_Interface { function handle(); - function setDb($db); } /** @@ -31,11 +30,14 @@ interface Piwik_Tracker_Visit_Interface { class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface { - protected $cookieLog = null; + /** + * @var Piwik_Cookie + */ + protected $cookie = null; protected $visitorInfo = array(); protected $userSettingsInformation = null; - protected $db = null; protected $idsite; + protected $visitorKnown; function __construct() { @@ -48,9 +50,203 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $this->idsite = $idsite; } - public function setDb($db) + /** + * Main algorith to handle the visit. + * + * Once we have the visitor information, we have to define if the visit is a new or a known visit. + * + * 1) When the last action was done more than 30min ago, + * or if the visitor is new, then this is a new visit. + * + * 2) If the last action is less than 30min ago, then the same visit is going on. + * Because the visit goes on, we can get the time spent during the last action. + * + * NB: + * - In the case of a new visit, then the time spent + * during the last action of the previous visit is unknown. + * + * - In the case of a new visit but with a known visitor, + * we can set the 'returning visitor' flag. + * + * In all the cases we set a cookie to the visitor with the new information. + */ + public function handle() + { + if($this->isExcluded()) + { + return; + } + + // current action + $action = $this->newAction(); + $actionId = $action->getIdAction(); + + // goal matched? + $goalManager = new Piwik_Tracker_GoalManager( $action ); + $someGoalsConverted = false; + if($goalManager->detectGoals($this->idsite)) + { + $someGoalsConverted = true; + } + + // the visitor and session + $this->recognizeTheVisitor(); + if( $this->isVisitorKnown() + && $this->isLastActionInTheSameVisit()) + { + $this->handleKnownVisit($actionId, $someGoalsConverted); + $action->record( $this->visitorInfo['idvisit'], + $this->visitorInfo['visit_exit_idaction'], + $this->visitorInfo['time_spent_ref_action'] + ); + } + else + { + $this->handleNewVisit($actionId, $someGoalsConverted); + $action->record( $this->visitorInfo['idvisit'], 0, 0 ); + } + + // update the cookie with the new visit information + $this->updateCookie(); + + // record the goals if applicable + if($someGoalsConverted) + { + $goalManager->setCookie($this->cookie); + $goalManager->recordGoals($this->visitorInfo); + } + unset($goalManager); + unset($action); + } + + + /** + * In the case of a known visit, we have to do the following actions: + * + * 1) Insert the new action + * + * 2) Update the visit information + */ + protected function handleKnownVisit($actionId, $someGoalsConverted) + { + printDebug("Visit known."); + $serverTime = $this->getCurrentTimestamp(); + $datetimeServer = Piwik_Tracker::getDatetimeFromTimestamp($serverTime); + + $sqlUpdateGoalConverted = ''; + if($someGoalsConverted) + { + $sqlUpdateGoalConverted = " visit_goal_converted = 1,"; + } + + Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ". $this->idsite ." */ + UPDATE ". Piwik_Tracker::getDatabase()->prefixTable('log_visit')." + SET visit_last_action_time = ?, + visit_exit_idaction = ?, + visit_total_actions = visit_total_actions + 1, + $sqlUpdateGoalConverted + visit_total_time = UNIX_TIMESTAMP(visit_last_action_time) - UNIX_TIMESTAMP(visit_first_action_time) + WHERE idvisit = ? + LIMIT 1", + array( $datetimeServer, + $actionId, + $this->visitorInfo['idvisit'] ) + ); + $this->visitorInfo['idsite'] = $this->idsite; + $this->visitorInfo['visit_server_date'] = $this->getCurrentDate(); + + // will be updated in cookie + $this->visitorInfo['visit_last_action_time'] = $serverTime; + $this->visitorInfo['visit_exit_idaction'] = $actionId; + $this->visitorInfo['time_spent_ref_action'] = $serverTime - $this->visitorInfo['visit_last_action_time']; + } + + /** + * In the case of a new visit, we have to do the following actions: + * + * 1) Insert the new action + * + * 2) Insert the visit information + */ + protected function handleNewVisit($actionId, $someGoalsConverted) { - $this->db = $db; + printDebug("New Visit."); + + $localTime = Piwik_Common::getRequestVar( 'h', $this->getCurrentDate("H"), 'numeric') + .':'. Piwik_Common::getRequestVar( 'm', $this->getCurrentDate("i"), 'numeric') + .':'. Piwik_Common::getRequestVar( 's', $this->getCurrentDate("s"), 'numeric'); + $serverTime = $this->getCurrentTimestamp(); + $serverDate = $this->getCurrentDate(); + + if($this->isVisitorKnown()) + { + $idcookie = $this->visitorInfo['visitor_idcookie']; + $returningVisitor = 1; + } + else + { + $idcookie = $this->getVisitorUniqueId(); + $returningVisitor = 0; + } + + $defaultTimeOnePageVisit = Piwik_Tracker_Config::getInstance()->Tracker['default_time_one_page_visit']; + + $userInfo = $this->getUserSettingsInformation(); + $country = Piwik_Common::getCountry($userInfo['location_browser_lang']); + $refererInfo = $this->getRefererInformation(); + + /** + * Save the visitor + */ + $this->visitorInfo = array( + 'idsite' => $this->idsite, + 'visitor_localtime' => $localTime, + 'visitor_idcookie' => $idcookie, + 'visitor_returning' => $returningVisitor, + 'visit_first_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($serverTime), + 'visit_last_action_time' => Piwik_Tracker::getDatetimeFromTimestamp($serverTime), + 'visit_server_date' => $serverDate, + 'visit_entry_idaction' => $actionId, + 'visit_exit_idaction' => $actionId, + 'visit_total_actions' => 1, + 'visit_total_time' => $defaultTimeOnePageVisit, + 'visit_goal_converted' => $someGoalsConverted ? 1: 0, + 'referer_type' => $refererInfo['referer_type'], + 'referer_name' => $refererInfo['referer_name'], + 'referer_url' => $refererInfo['referer_url'], + 'referer_keyword' => $refererInfo['referer_keyword'], + 'config_md5config' => $userInfo['config_md5config'], + 'config_os' => $userInfo['config_os'], + 'config_browser_name' => $userInfo['config_browser_name'], + 'config_browser_version' => $userInfo['config_browser_version'], + 'config_resolution' => $userInfo['config_resolution'], + 'config_pdf' => $userInfo['config_pdf'], + 'config_flash' => $userInfo['config_flash'], + 'config_java' => $userInfo['config_java'], + 'config_director' => $userInfo['config_director'], + 'config_quicktime' => $userInfo['config_quicktime'], + 'config_realplayer' => $userInfo['config_realplayer'], + 'config_windowsmedia' => $userInfo['config_windowsmedia'], + 'config_cookie' => $userInfo['config_cookie'], + 'location_ip' => $userInfo['location_ip'], + 'location_browser_lang' => $userInfo['location_browser_lang'], + 'location_country' => $country + ); + + Piwik_PostEvent('Tracker.newVisitorInformation', $this->visitorInfo); + $this->visitorInfo['location_continent'] = Piwik_Common::getContinent( $this->visitorInfo['location_country'] ); + + $fields = implode(", ", array_keys($this->visitorInfo)); + $values = substr(str_repeat( "?,",count($this->visitorInfo)),0,-1); + + Piwik_Tracker::getDatabase()->query( "INSERT INTO ".Piwik_Tracker::getDatabase()->prefixTable('log_visit'). + " ($fields) VALUES ($values)", array_values($this->visitorInfo)); + + $idVisit = Piwik_Tracker::getDatabase()->lastInsertId(); + + $this->visitorInfo['idvisit'] = $idVisit; + $this->visitorInfo['visit_first_action_time'] = $serverTime; + $this->visitorInfo['visit_last_action_time'] = $serverTime; } /** @@ -70,15 +266,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface { return time(); } - - /** - * Returns the date in the "Y-m-d H:i:s" PHP format - * @return string - */ - protected function getDatetimeFromTimestamp($timestamp) - { - return date("Y-m-d H:i:s", $timestamp); - } /** * Test if the current visitor is excluded from the statistics. @@ -144,19 +331,18 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface protected function recognizeTheVisitor() { $this->visitorKnown = false; + $this->cookie = new Piwik_Cookie( $this->getCookieName() ); - $this->cookieLog = new Piwik_Cookie( $this->getCookieName() ); /* * Case the visitor has the piwik cookie. * We make sure all the data that should saved in the cookie is available. */ - - if( false !== ($idVisitor = $this->cookieLog->get( Piwik_Tracker::COOKIE_INDEX_IDVISITOR )) ) + if( false !== ($idVisitor = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_IDVISITOR )) ) { - $timestampLastAction = $this->cookieLog->get( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_LAST_ACTION ); - $timestampFirstAction = $this->cookieLog->get( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION ); - $idVisit = $this->cookieLog->get( Piwik_Tracker::COOKIE_INDEX_ID_VISIT ); - $idLastAction = $this->cookieLog->get( Piwik_Tracker::COOKIE_INDEX_ID_LAST_ACTION ); + $timestampLastAction = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_LAST_ACTION ); + $timestampFirstAction = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION ); + $idVisit = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_ID_VISIT ); + $idLastAction = $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_ID_LAST_ACTION ); if( $timestampLastAction !== false && is_numeric($timestampLastAction) && $timestampFirstAction !== false && is_numeric($timestampFirstAction) @@ -177,21 +363,22 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface } /* - * If the visitor doesn't have the piwik cookie, we look for a visitor that has exactly the same configuration - * and that visited the website today. + * If the visitor doesn't have the piwik cookie, we look for a visitor + * that has exactly the same configuration and that visited the website today. */ - if( !$this->visitorKnown ) + if( !$this->visitorKnown + && Piwik_Tracker_Config::getInstance()->Tracker['enable_detect_unique_visitor_using_settings']) { $userInfo = $this->getUserSettingsInformation(); $md5Config = $userInfo['config_md5config']; - $visitRow = $this->db->fetch( + $visitRow = Piwik_Tracker::getDatabase()->fetch( " SELECT visitor_idcookie, UNIX_TIMESTAMP(visit_last_action_time) as visit_last_action_time, UNIX_TIMESTAMP(visit_first_action_time) as visit_first_action_time, idvisit, visit_exit_idaction - FROM ".$this->db->prefixTable('log_visit'). + FROM ".Piwik_Tracker::getDatabase()->prefixTable('log_visit'). " WHERE visit_server_date = ? AND idsite = ? AND config_md5config = ? @@ -226,8 +413,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface { return $this->userSettingsInformation; } - - $plugin_Flash = Piwik_Common::getRequestVar( 'fla', 0, 'int'); $plugin_Director = Piwik_Common::getRequestVar( 'dir', 0, 'int'); $plugin_Quicktime = Piwik_Common::getRequestVar( 'qt', 0, 'int'); @@ -249,7 +434,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $ip = Piwik_Common::getIp(); $ip = ip2long($ip); - $browserLang = substr(Piwik_Common::getBrowserLanguage(), 0, 20); + $browserLang = Piwik_Common::getBrowserLanguage(); $configurationHash = $this->getConfigHash( $os, @@ -305,47 +490,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface return $this->visitorKnown === true; } - /** - * Main algorith to handle the visit. - * - * Once we have the visitor information, we have to define if the visit is a new or a known visit. - * - * 1) When the last action was done more than 30min ago, - * or if the visitor is new, then this is a new visit. - * - * 2) If the last action is less than 30min ago, then the same visit is going on. - * Because the visit goes on, we can get the time spent during the last action. - * - * NB: - * - In the case of a new visit, then the time spent - * during the last action of the previous visit is unknown. - * - * - In the case of a new visit but with a known visitor, - * we can set the 'returning visitor' flag. - * - * In all the cases we set a cookie to the visitor with the new information. - */ - public function handle() - { - if($this->isExcluded()) - { - return; - } - - $this->recognizeTheVisitor(); - if( $this->isVisitorKnown() - && $this->isLastActionInTheSameVisit()) - { - $this->handleKnownVisit(); - } - else - { - $this->handleNewVisit(); - } - - // we update the cookie with the new visit information - $this->updateCookie(); - } /** * Update the cookie information. @@ -354,190 +498,50 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface { printDebug("We manage the cookie..."); + if( isset($this->visitorInfo['referer_type']) + && $this->visitorInfo['referer_type'] != Piwik_Common::REFERER_TYPE_DIRECT_ENTRY) + { + // if the setting is set to use only the first referer, + // we don't update the cookie referer values if they are already set + if( !Piwik_Tracker_Config::getInstance()->Tracker['use_first_referer_to_determine_goal_referer'] + || $this->cookie->get( Piwik_Tracker::COOKIE_INDEX_REFERER_TYPE ) == false) + { + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_TYPE, $this->visitorInfo['referer_type']); + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_NAME, $this->visitorInfo['referer_name']); + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_KEYWORD, $this->visitorInfo['referer_keyword']); + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_ID_VISIT, $this->visitorInfo['idvisit']); + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_REFERER_TIMESTAMP, $this->getCurrentTimestamp()) ; + } + } + // idcookie has been generated in handleNewVisit or we simply propagate the old value - $this->cookieLog->set( Piwik_Tracker::COOKIE_INDEX_IDVISITOR, + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_IDVISITOR, $this->visitorInfo['visitor_idcookie'] ); // the last action timestamp is the current timestamp - $this->cookieLog->set( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_LAST_ACTION, + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_LAST_ACTION, $this->visitorInfo['visit_last_action_time'] ); // the first action timestamp is the timestamp of the first action of the current visit - $this->cookieLog->set( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION, + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_TIMESTAMP_FIRST_ACTION, $this->visitorInfo['visit_first_action_time'] ); // the idvisit has been generated by mysql in handleNewVisit or simply propagated here - $this->cookieLog->set( Piwik_Tracker::COOKIE_INDEX_ID_VISIT, + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_ID_VISIT, $this->visitorInfo['idvisit'] ); // the last action ID is the current exit idaction - $this->cookieLog->set( Piwik_Tracker::COOKIE_INDEX_ID_LAST_ACTION, + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_ID_LAST_ACTION, $this->visitorInfo['visit_exit_idaction'] ); - - $this->cookieLog->save(); - } - - - /** - * In the case of a known visit, we have to do the following actions: - * - * 1) Insert the new action - * - * 2) Update the visit information - */ - protected function handleKnownVisit() - { - printDebug("Visit known."); - - /** - * Init the action - */ - $action = $this->getActionObject(); - $actionId = $action->getActionId(); - printDebug("idAction = $actionId"); - - $serverTime = $this->getCurrentTimestamp(); - $datetimeServer = $this->getDatetimeFromTimestamp($serverTime); - - $this->db->query("/* SHARDING_ID_SITE = ". $this->idsite ." */ - UPDATE ". $this->db->prefixTable('log_visit')." - SET visit_last_action_time = ?, - visit_exit_idaction = ?, - visit_total_actions = visit_total_actions + 1, - visit_total_time = UNIX_TIMESTAMP(visit_last_action_time) - UNIX_TIMESTAMP(visit_first_action_time) - WHERE idvisit = ? - LIMIT 1", - array( $datetimeServer, - $actionId, - $this->visitorInfo['idvisit'] ) - ); - /** - * Save the action - */ - $timespentLastAction = $serverTime - $this->visitorInfo['visit_last_action_time']; - - $action->record( $this->visitorInfo['idvisit'], - $this->visitorInfo['visit_exit_idaction'], - $timespentLastAction - ); - - - /** - * Cookie fields to be updated - */ - $this->visitorInfo['visit_last_action_time'] = $serverTime; - $this->visitorInfo['visit_exit_idaction'] = $actionId; - - } - - /** - * In the case of a new visit, we have to do the following actions: - * - * 1) Insert the new action - * - * 2) Insert the visit information - */ - protected function handleNewVisit() - { - printDebug("New Visit."); - - /** - * Get the variables from the REQUEST - */ - $localTime = Piwik_Common::getRequestVar( 'h', $this->getCurrentDate("H"), 'numeric') - .':'. Piwik_Common::getRequestVar( 'm', $this->getCurrentDate("i"), 'numeric') - .':'. Piwik_Common::getRequestVar( 's', $this->getCurrentDate("s"), 'numeric'); - - $serverTime = $this->getCurrentTimestamp(); - $serverDate = $this->getCurrentDate(); - - if($this->isVisitorKnown()) - { - $idcookie = $this->visitorInfo['visitor_idcookie']; - $returningVisitor = 1; - } - else + // for a new visit, we flag the visit with visitor_returning + if(isset($this->visitorInfo['visitor_returning'])) { - $idcookie = $this->getVisitorUniqueId(); - $returningVisitor = 0; + $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_VISITOR_RETURNING, + $this->visitorInfo['visitor_returning'] ); } - $defaultTimeOnePageVisit = Piwik_Tracker_Config::getInstance()->Tracker['default_time_one_page_visit']; - - $userInfo = $this->getUserSettingsInformation(); - $country = Piwik_Common::getCountry($userInfo['location_browser_lang']); - - $refererInfo = $this->getRefererInformation(); - - /** - * Init the action - */ - $action = $this->getActionObject(); - $actionId = $action->getActionId(); - - printDebug("idAction = $actionId"); - - - /** - * Save the visitor - */ - $informationToSave = array( - 'idsite' => $this->idsite, - 'visitor_localtime' => $localTime, - 'visitor_idcookie' => $idcookie, - 'visitor_returning' => $returningVisitor, - 'visit_first_action_time' => $this->getDatetimeFromTimestamp($serverTime), - 'visit_last_action_time' => $this->getDatetimeFromTimestamp($serverTime), - 'visit_server_date' => $serverDate, - 'visit_entry_idaction' => $actionId, - 'visit_exit_idaction' => $actionId, - 'visit_total_actions' => 1, - 'visit_total_time' => $defaultTimeOnePageVisit, - 'referer_type' => $refererInfo['referer_type'], - 'referer_name' => $refererInfo['referer_name'], - 'referer_url' => $refererInfo['referer_url'], - 'referer_keyword' => $refererInfo['referer_keyword'], - 'config_md5config' => $userInfo['config_md5config'], - 'config_os' => $userInfo['config_os'], - 'config_browser_name' => $userInfo['config_browser_name'], - 'config_browser_version' => $userInfo['config_browser_version'], - 'config_resolution' => $userInfo['config_resolution'], - 'config_pdf' => $userInfo['config_pdf'], - 'config_flash' => $userInfo['config_flash'], - 'config_java' => $userInfo['config_java'], - 'config_director' => $userInfo['config_director'], - 'config_quicktime' => $userInfo['config_quicktime'], - 'config_realplayer' => $userInfo['config_realplayer'], - 'config_windowsmedia' => $userInfo['config_windowsmedia'], - 'config_cookie' => $userInfo['config_cookie'], - 'location_ip' => $userInfo['location_ip'], - 'location_browser_lang' => $userInfo['location_browser_lang'], - 'location_country' => $country - ); - - Piwik_PostEvent('Tracker.newVisitorInformation', $informationToSave); - $informationToSave['location_continent'] = Piwik_Common::getContinent( $informationToSave['location_country'] ); - - $fields = implode(", ", array_keys($informationToSave)); - $values = substr(str_repeat( "?,",count($informationToSave)),0,-1); - - $this->db->query( "INSERT INTO ".$this->db->prefixTable('log_visit'). - " ($fields) VALUES ($values)", array_values($informationToSave)); - - $idVisit = $this->db->lastInsertId(); - - // Update the visitor information attribute with this information array - $this->visitorInfo = $informationToSave; - $this->visitorInfo['idvisit'] = $idVisit; - - // we have to save timestamp in the object properties, whereas mysql eats some other datetime format - $this->visitorInfo['visit_first_action_time'] = $serverTime; - $this->visitorInfo['visit_last_action_time'] = $serverTime; - - // saves the action - $action->record( $idVisit, 0, 0 ); - + $this->cookie->save(); } /** @@ -546,14 +550,14 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface * * @return Piwik_Tracker_Action child or fake but with same public interface */ - protected function getActionObject() + protected function newAction() { $action = null; Piwik_PostEvent('Tracker.newAction', $action); if(is_null($action)) { - $action = new Piwik_Tracker_Action( $this->db ); + $action = new Piwik_Tracker_Action(); } elseif(!($action instanceof Piwik_Tracker_Action_Interface)) { diff --git a/core/Updates/0.2.28.php b/core/Updates/0.2.28.php new file mode 100644 index 0000000000..a64b2b2a72 --- /dev/null +++ b/core/Updates/0.2.28.php @@ -0,0 +1,9 @@ +<?php + +Piwik_Query( "ALTER TABLE `".Piwik::prefixTable('log_visit')."` + ADD `visit_goal_converted` VARCHAR( 1 ) NOT NULL AFTER `visit_total_time` ;"); + +//TODO +// alter all archive_* +// KEY `index_all` (`idsite`,`date1`,`date2`,`name`,`ts_archived`) + diff --git a/core/Version.php b/core/Version.php index d54b22b973..89ad548c01 100644 --- a/core/Version.php +++ b/core/Version.php @@ -1,4 +1,4 @@ -<?php
+<?php final class Piwik_Version { const VERSION = '0.2.26'; diff --git a/core/View.php b/core/View.php index 5d62bd0aa1..7a1a6479c2 100644 --- a/core/View.php +++ b/core/View.php @@ -9,7 +9,6 @@ * @package Piwik_Visualization */ -require_once "Smarty.php"; require_once "iView.php"; /** @@ -25,6 +24,7 @@ class Piwik_View implements Piwik_iView public function __construct( $templateFile, $smConf = array()) { + require_once "Smarty.php"; $this->template = $templateFile; $this->smarty = new Piwik_Smarty(); diff --git a/core/ViewDataTable.php b/core/ViewDataTable.php index c91a200e00..cc767a1337 100644 --- a/core/ViewDataTable.php +++ b/core/ViewDataTable.php @@ -93,13 +93,13 @@ abstract class Piwik_ViewDataTable * @see init() * @var string */ - protected $actionToLoadTheSubTable = null; + protected $controllerActionCalledWhenRequestSubTable = null; /** * @see init() * @var string */ - protected $moduleNameAndMethod; + protected $apiMethodToRequestDataTable; /** * This view should be an implementation of the Interface Piwik_iView @@ -197,6 +197,11 @@ abstract class Piwik_ViewDataTable return new Piwik_ViewDataTable_HtmlTable_AllColumns(); break; + case 'tableGoals': + require_once "ViewDataTable/HtmlTable/Goals.php"; + return new Piwik_ViewDataTable_HtmlTable_Goals(); + break; + case 'table': default: require_once "ViewDataTable/HtmlTable.php"; @@ -208,42 +213,45 @@ abstract class Piwik_ViewDataTable /** * Inits the object given the $currentControllerName, $currentControllerAction of * the calling controller action, eg. 'Referers' 'getLongListOfKeywords'. - * The initialization also requires the $moduleNameAndMethod of the API method + * The initialization also requires the $apiMethodToRequestDataTable of the API method * to call in order to get the DataTable, eg. 'Referers.getKeywords'. - * The optional $actionToLoadTheSubTable defines the method name of the API to call when there is a idSubtable. + * The optional $controllerActionCalledWhenRequestSubTable defines the method name of the API to call when there is a idSubtable. * This value would be used by the javascript code building the GET request to the API. * * Example: * For the keywords listing, a click on the row loads the subTable of the Search Engines for this row. - * In this case $actionToLoadTheSubTable = 'getSearchEnginesFromKeywordId'. + * In this case $controllerActionCalledWhenRequestSubTable = 'getSearchEnginesFromKeywordId'. * The GET request will hit 'Referers.getSearchEnginesFromKeywordId'. * * @param string $currentControllerName eg. 'Referers' * @param string $currentControllerAction eg. 'getKeywords' - * @param string $moduleNameAndMethod eg. 'Referers.getKeywords' - * @param string $actionToLoadTheSubTable eg. 'getSearchEnginesFromKeywordId' + * @param string $apiMethodToRequestDataTable eg. 'Referers.getKeywords' + * @param string $controllerActionCalledWhenRequestSubTable eg. 'getSearchEnginesFromKeywordId' * @return void */ public function init( $currentControllerName, $currentControllerAction, - $moduleNameAndMethod, - $actionToLoadTheSubTable = null) + $apiMethodToRequestDataTable, + $controllerActionCalledWhenRequestSubTable = null) { $this->currentControllerName = $currentControllerName; $this->currentControllerAction = $currentControllerAction; - $this->moduleNameAndMethod = $moduleNameAndMethod; - $this->actionToLoadTheSubTable = $actionToLoadTheSubTable; - $this->method = $moduleNameAndMethod; + $this->apiMethodToRequestDataTable = $apiMethodToRequestDataTable; + $this->controllerActionCalledWhenRequestSubTable = $controllerActionCalledWhenRequestSubTable; $this->idSubtable = Piwik_Common::getRequestVar('idSubtable', false, 'int'); + $this->viewProperties['show_goals'] = false; $this->viewProperties['show_search'] = Piwik_Common::getRequestVar('show_search', true); $this->viewProperties['show_table_all_columns'] = Piwik_Common::getRequestVar('show_table_all_columns', true); $this->viewProperties['show_exclude_low_population'] = Piwik_Common::getRequestVar('show_exclude_low_population', true); $this->viewProperties['show_offset_information'] = Piwik_Common::getRequestVar('show_offset_information', true);; $this->viewProperties['show_footer'] = Piwik_Common::getRequestVar('show_footer', true); $this->viewProperties['show_footer_icons'] = ($this->idSubtable == false); + $this->viewProperties['apiMethodToRequestDataTable'] = $this->apiMethodToRequestDataTable; + $this->viewProperties['uniqueId'] = $this->getUniqueIdViewDataTable(); } + /** * Forces the View to use a given template. * Usually the template to use is set in the specific ViewDataTable_* @@ -274,6 +282,26 @@ abstract class Piwik_ViewDataTable return $this->view; } + public function getCurrentControllerAction() + { + return $this->currentControllerAction; + } + + public function getCurrentControllerName() + { + return $this->currentControllerName; + } + + public function getApiMethodToRequestDataTable() + { + return $this->apiMethodToRequestDataTable; + } + + public function getControllerActionCalledWhenRequestSubTable() + { + return $this->controllerActionCalledWhenRequestSubTable; + } + /** * Returns the DataTable loaded from the API * @@ -299,7 +327,7 @@ abstract class Piwik_ViewDataTable protected function loadDataTableFromAPI() { // we build the request string (URL) to call the API - $requestString = $this->getRequestString();
+ $requestString = $this->getRequestString(); // we make the request to the API $request = new Piwik_API_Request($requestString); @@ -319,7 +347,7 @@ abstract class Piwik_ViewDataTable // we setup the method and format variable // - we request the method to call to get this specific DataTable // - the format = original specifies that we want to get the original DataTable structure itself, not rendered - $requestString = 'method='.$this->moduleNameAndMethod; + $requestString = 'method='.$this->apiMethodToRequestDataTable; $requestString .= '&format=original'; $toSetEventually = array( @@ -384,7 +412,7 @@ abstract class Piwik_ViewDataTable * @see datatable.js * @return string */ - protected function getUniqIdTable() + protected function getUniqueIdViewDataTable() { // if we request a subDataTable the $this->currentControllerAction DIV ID is already there in the page // we make the DIV ID really unique by appending the ID of the subtable requested @@ -477,13 +505,25 @@ abstract class Piwik_ViewDataTable $javascriptVariablesToSet[$name] = $value; } } - + + if($this->dataTable instanceof Piwik_DataTable) + { + // we override the filter_sort_column with the column used for sorting, + // which can be different from the one specified (eg. if the column doesn't exist) + $javascriptVariablesToSet['filter_sort_column'] = $this->dataTable->getSortedByColumnName(); + // datatable can return "2" but we want to write "nb_visits" in the js + if(isset(Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']])) + { + $javascriptVariablesToSet['filter_sort_column'] = Piwik_Archive::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']]; + } + } + $javascriptVariablesToSet['module'] = $this->currentControllerName; $javascriptVariablesToSet['action'] = $this->currentControllerAction; $javascriptVariablesToSet['viewDataTable'] = $this->getViewDataTableId(); - if(!is_null($this->actionToLoadTheSubTable)) + if(!is_null($this->controllerActionCalledWhenRequestSubTable)) { - $javascriptVariablesToSet['actionToLoadTheSubTable'] = $this->actionToLoadTheSubTable; + $javascriptVariablesToSet['controllerActionCalledWhenRequestSubTable'] = $this->controllerActionCalledWhenRequestSubTable; } if($this->dataTable) @@ -496,16 +536,27 @@ abstract class Piwik_ViewDataTable // are loaded with Piwik_Common::getRequestVar() foreach($javascriptVariablesToSet as &$value) { - if(is_array($value))
- {
- $value = array_map('addslashes',$value);
- }
- else
- {
+ if(is_array($value)) + { + $value = array_map('addslashes',$value); + } + else + { $value = addslashes($value); } - }
+ } + $deleteFromJavascriptVariables = array( + 'filter_excludelowpop', + 'filter_excludelowpop_value', + ); + foreach($deleteFromJavascriptVariables as $name) + { + if(isset($javascriptVariablesToSet[$name])) + { + unset($javascriptVariablesToSet[$name]); + } + } return $javascriptVariablesToSet; } @@ -605,18 +656,27 @@ abstract class Piwik_ViewDataTable $this->viewProperties['show_table_all_columns'] = false; } - /**
- * Sets the pattern to look for in the table (only rows with column equal to the pattern will be kept)
- *
- * @param array $pattern arrays of patterns to look for
- * @param string $column to compare the pattern to
- * @return void
- */
- public function setExactPattern($pattern, $column)
- {
- $this->variablesDefault['filter_exact_pattern'] = $pattern;
- $this->variablesDefault['filter_exact_column'] = $column;
- }
+ /** + * Whether or not to show the "goal" icon + * @return void + */ + public function enableShowGoals() + { + $this->viewProperties['show_goals'] = true; + } + + /** + * Sets the pattern to look for in the table (only rows with column equal to the pattern will be kept) + * + * @param array $pattern arrays of patterns to look for + * @param string $column to compare the pattern to + * @return void + */ + public function setExactPattern($pattern, $column) + { + $this->variablesDefault['filter_exact_pattern'] = $pattern; + $this->variablesDefault['filter_exact_column'] = $column; + } /** * Sets the value to use for the Exclude low population filter. @@ -625,18 +685,12 @@ abstract class Piwik_ViewDataTable * @param string The name of the column for which we compare the value to $minValue * @return void */ - public function setExcludeLowPopulation( $minValue = null, $columnName = null ) + public function setExcludeLowPopulation( $columnName = null, $minValue = null ) { - if( is_null( $minValue) ) - { - throw new Exception("setExcludeLowPopulation() value shouldn't be null"); - } - if(is_null($columnName)) { $columnName = Piwik_Archive::INDEX_NB_VISITS; } - $this->variablesDefault['filter_excludelowpop'] = $columnName; $this->variablesDefault['filter_excludelowpop_value'] = $minValue; } diff --git a/core/ViewDataTable/Cloud.php b/core/ViewDataTable/Cloud.php index 179c8959d2..29cb676eab 100644 --- a/core/ViewDataTable/Cloud.php +++ b/core/ViewDataTable/Cloud.php @@ -33,11 +33,11 @@ class Piwik_ViewDataTable_Cloud extends Piwik_ViewDataTable */ function init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod ) + $apiMethodToRequestDataTable ) { parent::init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod ); + $apiMethodToRequestDataTable ); $this->dataTableTemplate = 'CoreHome/templates/cloud.tpl'; $this->disableOffsetInformation(); $this->disableExcludeLowPopulation(); @@ -99,8 +99,6 @@ class Piwik_ViewDataTable_Cloud extends Piwik_ViewDataTable $view->labelMetadata = $labelMetadata; $view->cloudValues = $cloudValues; - $view->method = $this->method; - $view->id = $this->getUniqIdTable(); $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet(); $view->properties = $this->getViewProperties(); return $view; diff --git a/core/ViewDataTable/GenerateGraphData/ChartEvolution.php b/core/ViewDataTable/GenerateGraphData/ChartEvolution.php index 93c69b2a49..8b51c394d6 100644 --- a/core/ViewDataTable/GenerateGraphData/ChartEvolution.php +++ b/core/ViewDataTable/GenerateGraphData/ChartEvolution.php @@ -19,198 +19,198 @@ class Piwik_ViewDataTable_GenerateGraphData_ChartEvolution extends Piwik_ViewDat $this->view = new Piwik_Visualization_ChartEvolution; } - var $lineLabels = array();
- var $data = array();
-
- private function generateLine( $dataArray, $columns, $schema = "##label## ##column##" )
- {
- $data = array();
-
- foreach($dataArray as $keyName => $table)
- {
- $table->applyQueuedFilters();
-
- // initialize data (default values for all lines is 0)
- $dataRow = array();
-
- $rows = $table->getRows();
-
- foreach($rows as $row)
+ var $lineLabels = array(); + var $data = array(); + + private function generateLine( $dataArray, $columns, $schema = "##label## ##column##" ) + { + $data = array(); + + foreach($dataArray as $keyName => $table) + { + $table->applyQueuedFilters(); + + // initialize data (default values for all lines is 0) + $dataRow = array(); + + $rows = $table->getRows(); + + foreach($rows as $row) + { + $rowLabel = $schema; + + if( strpos($rowLabel, "##label##") !== false ) + { + $rowLabel = str_replace("##label##", $row->getColumn('label'), $rowLabel); + } + + foreach($columns as $col) + { + $label = $rowLabel; + + if( strpos($label, "##column##") !== false ) + { + $label = str_replace("##column##", $col, $label); + } + + if( !isset($this->lineLabels[$label]) ) + { + $this->lineLabels[$label] = count($this->lineLabels); + } + $lineNb = $this->lineLabels[$label]; + + $value = $row->getColumn($col); + + $dataRow['value'.$lineNb] = $value; + } + } + $data[] = $dataRow; + } + return $data; + } + + private function generateLabels( $dataArray ) + { + $data = array(); + + foreach($dataArray as $keyName => $table) + { + $table->applyQueuedFilters(); + + $data[] = array('label' => $keyName); + } + + return $data; + } + + private function addArray( &$data, $newData ) + { + for($i = 0; $i < count($newData); $i++) + { + foreach($newData[$i] as $key => $value) + { + $data[$i][$key] = $value; + } + } + } + + private function fillValues( &$data ) + { + $nbLines = count($this->lineLabels); + + for($i = 0; $i < count($data); $i++) + { + for($j = 0; $j < $nbLines; $j++) + { + if( !isset($data[$i]['value'.$j]) ) + { + $data[$i]['value'.$j] = 0; + } + } + } + } + + /* + * generates data for evolution graph from a numeric DataTable (DataTable that has only 'label' and 'value' columns) + */ + protected function generateDataFromNumericDataTable($dataArray, $siteLabel = "") + { + $columnsToDisplay = Piwik_Common::getRequestVar('columns', array(), 'array'); + + // for numeric we want to have only one column name + if( count($columnsToDisplay) != 1 ) + { + $columnsToDisplay = array( 'nb_uniq_visitors' ); + } + + $label = $siteLabel . array_shift($columnsToDisplay); + + $this->addArray($this->data, $this->generateLabels($dataArray)); + $this->addArray($this->data, $this->generateLine($dataArray,array('value'),$label)); + $this->fillValues($this->data); + } + + /* + * generates data for evolution graph from a DataTable that has named columns (i.e. 'nb_hits', 'nb_uniq_visitors') + */ + protected function generateDataFromRegularDataTable($dataArray, $siteLabel = "") + { + // get list of columns to display i.e. array('nb_hits','nb_uniq_visitors') + $columnsToDisplay = Piwik_Common::getRequestVar('columns', array(), 'array'); + + // default column + if( count($columnsToDisplay) == 0 ) + { + $columnsToDisplay = array( 'nb_uniq_visitors' ); + } + + $this->addArray($this->data, $this->generateLabels($dataArray)); + $this->addArray($this->data, $this->generateLine($dataArray, $columnsToDisplay, $siteLabel."##label## ##column##")); + $this->fillValues($this->data); + } + + protected function handleSiteGenerateDataFromDataTable($dataArray, $siteLabel = "") + { + // detect if we got numeric Datatable or regular DataTable + foreach($dataArray as $table) + { + $row = $table->getFirstRow(); + + if( $row != null ) + { + $columns = $row->getColumns(); + + // if we got 2 columns - 'label' and 'value' this is numeric DataTable + if( count($columns) == 2 && isset($columns['label']) && isset($columns['value']) ) + { + $this->generateDataFromNumericDataTable($dataArray, $siteLabel); + } + else + { + $this->generateDataFromRegularDataTable($dataArray, $siteLabel); + } + break; + } + } + } + + public function generateDataFromDataTable() + { + $data = array(); + + if( $this->dataTable->getRowsCount() ) + { + $row = null; + + // find first table with rows + foreach($this->dataTable->getArray() as $idsite => $table) { - $rowLabel = $schema;
-
- if( strpos($rowLabel, "##label##") !== false )
- {
- $rowLabel = str_replace("##label##", $row->getColumn('label'), $rowLabel);
- }
-
- foreach($columns as $col)
- {
- $label = $rowLabel;
-
- if( strpos($label, "##column##") !== false )
- {
- $label = str_replace("##column##", $col, $label);
- }
-
- if( !isset($this->lineLabels[$label]) )
- {
- $this->lineLabels[$label] = count($this->lineLabels);
- }
- $lineNb = $this->lineLabels[$label];
-
- $value = $row->getColumn($col);
-
- $dataRow['value'.$lineNb] = $value;
- }
- }
- $data[] = $dataRow;
- }
- return $data;
- }
-
- private function generateLabels( $dataArray )
- {
- $data = array();
-
- foreach($dataArray as $keyName => $table)
- {
- $table->applyQueuedFilters();
-
- $data[] = array('label' => $keyName);
- }
-
- return $data;
- }
-
- private function addArray( &$data, $newData )
- {
- for($i = 0; $i < count($newData); $i++)
- {
- foreach($newData[$i] as $key => $value)
- {
- $data[$i][$key] = $value;
- }
- }
- }
-
- private function fillValues( &$data )
- {
- $nbLines = count($this->lineLabels);
-
- for($i = 0; $i < count($data); $i++)
- {
- for($j = 0; $j < $nbLines; $j++)
- {
- if( !isset($data[$i]['value'.$j]) )
- {
- $data[$i]['value'.$j] = 0;
- }
- }
- }
- }
-
- /*
- * generates data for evolution graph from a numeric DataTable (DataTable that has only 'label' and 'value' columns)
- */
- protected function generateDataFromNumericDataTable($dataArray, $siteLabel = "")
- {
- $columnsToDisplay = Piwik_Common::getRequestVar('columns', array(), 'array');
-
- // for numeric we want to have only one column name
- if( count($columnsToDisplay) != 1 )
- {
- $columnsToDisplay = array( 'nb_uniq_visitors' );
- }
-
- $label = $siteLabel . array_shift($columnsToDisplay);
-
- $this->addArray($this->data, $this->generateLabels($dataArray));
- $this->addArray($this->data, $this->generateLine($dataArray,array('value'),$label));
- $this->fillValues($this->data);
- }
-
- /*
- * generates data for evolution graph from a DataTable that has named columns (i.e. 'nb_hits', 'nb_uniq_visitors')
- */
- protected function generateDataFromRegularDataTable($dataArray, $siteLabel = "")
- {
- // get list of columns to display i.e. array('nb_hits','nb_uniq_visitors')
- $columnsToDisplay = Piwik_Common::getRequestVar('columns', array(), 'array');
-
- // default column
- if( count($columnsToDisplay) == 0 )
- {
- $columnsToDisplay = array( 'nb_uniq_visitors' );
- }
-
- $this->addArray($this->data, $this->generateLabels($dataArray));
- $this->addArray($this->data, $this->generateLine($dataArray, $columnsToDisplay, $siteLabel."##label## ##column##"));
- $this->fillValues($this->data);
- }
-
- protected function handleSiteGenerateDataFromDataTable($dataArray, $siteLabel = "")
- {
- // detect if we got numeric Datatable or regular DataTable
- foreach($dataArray as $table)
- {
- $row = $table->getFirstRow();
-
- if( $row != null )
- {
- $columns = $row->getColumns();
-
- // if we got 2 columns - 'label' and 'value' this is numeric DataTable
- if( count($columns) == 2 && isset($columns['label']) && isset($columns['value']) )
- {
- $this->generateDataFromNumericDataTable($dataArray, $siteLabel);
- }
- else
- {
- $this->generateDataFromRegularDataTable($dataArray, $siteLabel);
- }
- break;
- }
- }
- }
-
- public function generateDataFromDataTable()
- {
- $data = array();
-
- if( $this->dataTable->getRowsCount() )
- {
- $row = null;
-
- // find first table with rows
- foreach($this->dataTable->getArray() as $idsite => $table)
- {
- // detect if we got data from more than one site
- if( $table instanceof Piwik_DataTable_Array)
- {
- // multiple sites
- $site = new Piwik_Site($idsite);
-
- $this->handleSiteGenerateDataFromDataTable($table->getArray(), $site->getName()." ");
- }
- else if( $table instanceof Piwik_DataTable_Simple && $this->dataTable->getKeyName() == 'idSite')
- {
- // multiple sites (when numeric DataTable)
- $site = new Piwik_Site($idsite);
-
- $this->handleSiteGenerateDataFromDataTable($table->getFirstRow()->getColumn('value')->getArray(), $site->getName()." ");
- }
- else
- {
- // single site
- $this->handleSiteGenerateDataFromDataTable($this->dataTable->getArray());
- break;
- }
- }
-
- }
- array_unshift($this->data, array_keys($this->lineLabels));
-
- return $this->data;
+ // detect if we got data from more than one site + if( $table instanceof Piwik_DataTable_Array) + { + // multiple sites + $site = new Piwik_Site($idsite); + + $this->handleSiteGenerateDataFromDataTable($table->getArray(), $site->getName()." "); + } + else if( $table instanceof Piwik_DataTable_Simple && $this->dataTable->getKeyName() == 'idSite') + { + // multiple sites (when numeric DataTable) + $site = new Piwik_Site($idsite); + + $this->handleSiteGenerateDataFromDataTable($table->getFirstRow()->getColumn('value')->getArray(), $site->getName()." "); + } + else + { + // single site + $this->handleSiteGenerateDataFromDataTable($this->dataTable->getArray()); + break; + } + } + + } + array_unshift($this->data, array_keys($this->lineLabels)); + + return $this->data; } } diff --git a/core/ViewDataTable/GenerateGraphHTML.php b/core/ViewDataTable/GenerateGraphHTML.php index 3204361748..58849049d3 100644 --- a/core/ViewDataTable/GenerateGraphHTML.php +++ b/core/ViewDataTable/GenerateGraphHTML.php @@ -27,11 +27,11 @@ abstract class Piwik_ViewDataTable_GenerateGraphHTML extends Piwik_ViewDataTable */ function init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod ) + $apiMethodToRequestDataTable ) { parent::init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod ); + $apiMethodToRequestDataTable ); $this->dataTableTemplate = 'CoreHome/templates/graph.tpl'; @@ -74,7 +74,7 @@ abstract class Piwik_ViewDataTable_GenerateGraphHTML extends Piwik_ViewDataTable protected function buildView() { $view = new Piwik_View($this->dataTableTemplate); - $this->id = $this->getUniqIdTable(); + $this->uniqueIdViewDataTable = $this->getUniqueIdViewDataTable(); $view->graphType = $this->graphType; $this->parametersToModify['action'] = $this->currentControllerAction; @@ -82,11 +82,9 @@ abstract class Piwik_ViewDataTable_GenerateGraphHTML extends Piwik_ViewDataTable $view->jsInvocationTag = $this->getFlashInvocationCode($url); $view->urlGraphData = $url; - $view->formEmbedId = "formEmbed".$this->id; + $view->formEmbedId = "formEmbed".$this->uniqueIdViewDataTable; $view->graphCodeEmbed = $this->graphCodeEmbed; - $view->id = $this->id; - $view->method = $this->method; $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet(); $view->properties = $this->getViewProperties(); return $view; @@ -105,8 +103,8 @@ abstract class Piwik_ViewDataTable_GenerateGraphHTML extends Piwik_ViewDataTable // escape the & and stuff: $url = urlencode($url); - $obj_id = $this->id . "Chart"; - $div_name = $this->id . "FlashContent"; + $obj_id = $this->uniqueIdViewDataTable . "Chart"; + $div_name = $this->uniqueIdViewDataTable . "FlashContent"; $return = ''; if( $use_swfobject ) diff --git a/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php b/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php index 0eb29034e3..98fa9323a8 100644 --- a/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php +++ b/core/ViewDataTable/GenerateGraphHTML/ChartEvolution.php @@ -28,24 +28,24 @@ class Piwik_ViewDataTable_GenerateGraphHTML_ChartEvolution extends Piwik_ViewDat function init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod ) + $apiMethodToRequestDataTable ) { parent::init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod ); + $apiMethodToRequestDataTable ); $this->setParametersToModify(array('date' => Piwik_Common::getRequestVar('date', 'last30', 'string'))); $this->doNotShowFooter(); } -
- /**
- * Sets the columns that will be displayed on output evolution chart
- * By default all columns are displayed ($columnsNames = array() will display all columns)
- *
- * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits')
- */
- public function setColumnsToDisplay( $columnsNames)
- {
- $this->setParametersToModify( array('columns' => $columnsNames) );
- }
+ + /** + * Sets the columns that will be displayed on output evolution chart + * By default all columns are displayed ($columnsNames = array() will display all columns) + * + * @param array $columnsNames Array of column names eg. array('nb_visits','nb_hits') + */ + public function setColumnsToDisplay( $columnsNames) + { + $this->setParametersToModify( array('columns' => $columnsNames) ); + } }
\ No newline at end of file diff --git a/core/ViewDataTable/HtmlTable.php b/core/ViewDataTable/HtmlTable.php index 9c27447417..3beb82265e 100644 --- a/core/ViewDataTable/HtmlTable.php +++ b/core/ViewDataTable/HtmlTable.php @@ -53,21 +53,15 @@ class Piwik_ViewDataTable_HtmlTable extends Piwik_ViewDataTable */ function init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod, - $actionToLoadTheSubTable = null ) + $apiMethodToRequestDataTable, + $controllerActionCalledWhenRequestSubTable = null ) { parent::init($currentControllerName, $currentControllerAction, - $moduleNameAndMethod, - $actionToLoadTheSubTable); + $apiMethodToRequestDataTable, + $controllerActionCalledWhenRequestSubTable); $this->dataTableTemplate = 'CoreHome/templates/datatable.tpl'; - $this->variablesDefault['enable_sort'] = '1'; - - // load general columns translations - $this->setColumnTranslation('nb_visits', Piwik_Translate('General_ColumnNbVisits')); - $this->setColumnTranslation('label', Piwik_Translate('General_ColumnLabel')); - $this->setColumnTranslation('nb_uniq_visitors', Piwik_Translate('General_ColumnNbUniqVisitors')); } protected function getViewDataTableId() @@ -97,6 +91,10 @@ class Piwik_ViewDataTable_HtmlTable extends Piwik_ViewDataTable */ protected function postDataTableLoadedFromAPI() { + // load general columns translations + $this->setColumnTranslation('nb_visits', Piwik_Translate('General_ColumnNbVisits')); + $this->setColumnTranslation('label', Piwik_Translate('General_ColumnLabel')); + $this->setColumnTranslation('nb_uniq_visitors', Piwik_Translate('General_ColumnNbUniqVisitors')); } /** @@ -109,7 +107,6 @@ class Piwik_ViewDataTable_HtmlTable extends Piwik_ViewDataTable $phpArray = $this->getPHPArrayFromDataTable(); $view->arrayDataTable = $phpArray; - $view->method = $this->method; $columns = $this->getColumnsToDisplay($phpArray); $view->dataTableColumns = $columns; @@ -122,11 +119,24 @@ class Piwik_ViewDataTable_HtmlTable extends Piwik_ViewDataTable } $view->nbColumns = $nbColumns; - $view->id = $this->getUniqIdTable(); $view->javascriptVariablesToSet = $this->getJavascriptVariablesToSet(); $view->properties = $this->getViewProperties(); return $view; } + + protected function handleLowPopulation( $columnToApplyFilter = null) + { + if(Piwik_Common::getRequestVar('enable_filter_excludelowpop', '0', 'string' ) == '0') + { + return; + } + + if(is_null($columnToApplyFilter)) + { + $columnToApplyFilter = Piwik_Archive::INDEX_NB_VISITS; + } + $this->setExcludeLowPopulation( $columnToApplyFilter); + } /** * Returns friendly php array from the Piwik_DataTable @@ -156,6 +166,16 @@ class Piwik_ViewDataTable_HtmlTable extends Piwik_ViewDataTable } /** + * Adds a column to the list of columns to be displayed + * + * @param string $columnName + */ + public function addColumnToDisplay( $columnName ) + { + $this->columnsToDisplay[] = $columnName; + } + + /** * Sets translation string for given column * * @param string $columnName column name @@ -217,6 +237,7 @@ class Piwik_ViewDataTable_HtmlTable extends Piwik_ViewDataTable $columnsToDisplay = $columnsInDataTable; } + $columnsToDisplay = array_unique($columnsToDisplay); foreach($columnsToDisplay as $columnToDisplay) { if(in_array($columnToDisplay, $columnsInDataTable)) diff --git a/core/ViewDataTable/HtmlTable/AllColumns.php b/core/ViewDataTable/HtmlTable/AllColumns.php index 9ea33f5a85..e1c0882301 100644 --- a/core/ViewDataTable/HtmlTable/AllColumns.php +++ b/core/ViewDataTable/HtmlTable/AllColumns.php @@ -3,8 +3,6 @@ require_once "ViewDataTable/HtmlTable.php"; class Piwik_ViewDataTable_HtmlTable_AllColumns extends Piwik_ViewDataTable_HtmlTable { - const LOW_POPULATION_THRESHOLD_PERCENTAGE_VISIT = 0.005; - protected function getViewDataTableId() { return 'tableAllColumns'; @@ -18,22 +16,6 @@ class Piwik_ViewDataTable_HtmlTable_AllColumns extends Piwik_ViewDataTable_HtmlT parent::main(); } - protected function handleLowPopulation() - { - if(Piwik_Common::getRequestVar('filter_excludelowpop', '0', 'string' ) == '0') - { - return; - } - - require_once "VisitsSummary/Controller.php"; - $visits = Piwik_VisitsSummary_Controller::getVisits(); - $visitsThreshold = floor( self::LOW_POPULATION_THRESHOLD_PERCENTAGE_VISIT * $visits); - if($visitsThreshold > 0) - { - $this->setExcludeLowPopulation( $visitsThreshold, Piwik_Archive::INDEX_NB_VISITS ); - } - } - protected function getRequestString() { $requestString = parent::getRequestString(); @@ -42,6 +24,7 @@ class Piwik_ViewDataTable_HtmlTable_AllColumns extends Piwik_ViewDataTable_HtmlT protected function postDataTableLoadedFromAPI() { + parent::postDataTableLoadedFromAPI(); $this->setColumnsToDisplay(array('label', 'nb_visits', 'nb_uniq_visitors', diff --git a/core/ViewDataTable/HtmlTable/Goals.php b/core/ViewDataTable/HtmlTable/Goals.php new file mode 100644 index 0000000000..ebb5d9e800 --- /dev/null +++ b/core/ViewDataTable/HtmlTable/Goals.php @@ -0,0 +1,77 @@ +<?php +require_once "ViewDataTable/HtmlTable.php"; + +class Piwik_ViewDataTable_HtmlTable_Goals extends Piwik_ViewDataTable_HtmlTable +{ + protected function getViewDataTableId() + { + return 'tableGoals'; + } + + public function main() + { + $this->viewProperties['show_exclude_low_population'] = true; + $this->viewProperties['show_goals'] = true; + $this->setColumnsToDisplay( array( 'label', + 'nb_visits', + 'goals_conversion_rate', + 'goal_%s_conversion_rate', + 'revenue_per_visit', + )); + $this->handleLowPopulation(); + parent::main(); + } + + public function disableSubTableWhenShowGoals() + { + $this->controllerActionCalledWhenRequestSubTable = null; + } + + protected function getRequestString() + { + $requestString = parent::getRequestString(); + return $requestString . '&filter_update_columns_when_show_all_goals=1'; + } + + protected $columnsToPercentageFilter = array(); + + public function setColumnsToDisplay($columnsNames) + { + $newColumnsNames = array(); + foreach($columnsNames as $columnName) + { + if($columnName == 'goal_%s_conversion_rate') + { + require_once "core/Tracker/GoalManager.php"; + $goals = Piwik_Tracker_GoalManager::getGoalDefinitions(); + foreach($goals as $goal) + { + $idgoal = $goal['id']; + $name = $goal['name']; + $columnName = 'goal_'.$idgoal.'_conversion_rate'; + $newColumnsNames[] = $columnName; + $this->setColumnTranslation($columnName, $name); + $this->columnsToPercentageFilter[] = $columnName; + } + } + else + { + $newColumnsNames[] = $columnName; + } + } + parent::setColumnsToDisplay($newColumnsNames); + } + + protected function postDataTableLoadedFromAPI() + { + parent::postDataTableLoadedFromAPI(); + $this->setColumnTranslation('revenue_per_visit', 'Value per Visit'); + $this->setColumnTranslation('goals_conversion_rate', 'Visits with Conversions'); + $this->columnsToPercentageFilter[] = 'goals_conversion_rate'; + foreach($this->columnsToPercentageFilter as $columnName) + { + $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($this->dataTable, $columnName, create_function('$rate', 'return $rate."%";')); + } + $filter = new Piwik_DataTable_Filter_ColumnCallbackReplace($this->dataTable, 'revenue_per_visit', array("Piwik", "getPrettyMoney")); + } +} diff --git a/core/ViewDataTable/Sparkline.php b/core/ViewDataTable/Sparkline.php index ab6fce4ce7..f9908a50fa 100644 --- a/core/ViewDataTable/Sparkline.php +++ b/core/ViewDataTable/Sparkline.php @@ -24,18 +24,6 @@ class Piwik_ViewDataTable_Sparkline extends Piwik_ViewDataTable } /** - * @see Piwik_ViewDataTable::init() - */ - function init($currentControllerName, - $currentControllerAction, - $moduleNameAndMethod ) - { - parent::init($currentControllerName, - $currentControllerAction, - $moduleNameAndMethod ); - } - - /** * @see Piwik_ViewDataTable::main() */ public function main() @@ -64,6 +52,15 @@ class Piwik_ViewDataTable_Sparkline extends Piwik_ViewDataTable } /** + * Sparkline needs to be fast to load. Currently we only load numeric values + * that do not need filtering, we can disable the filters for more performance. + */ + protected function getRequestString() + { + return parent::getRequestString() . "&disable_generic_filters=1"; + } + + /** * Given a Piwik_DataTable_Array made of DataTable_Simple rows, returns a php array with the structure: * array( * array( label => X, value => Y), diff --git a/core/Visualization/ChartEvolution.php b/core/Visualization/ChartEvolution.php index 9160eb8bc2..82f2da4fa4 100644 --- a/core/Visualization/ChartEvolution.php +++ b/core/Visualization/ChartEvolution.php @@ -1,72 +1,72 @@ -<?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: ChartVerticalBar.php 168 2008-01-14 05:26:43Z matt $
- *
- * @package Piwik_Visualization
- */
-
-require_once "Visualization/Chart.php";
-
-/**
- * Customize the Evolution chart style for the flash graph
- *
- * @package Piwik_Visualization
- */
-class Piwik_Visualization_ChartEvolution extends Piwik_Visualization_Chart
-{
- function customizeGraph()
- {
- parent::customizeGraph();
- //$this->prepareData();
-
- $colors = array(
- "0x3357A0",
- "0x9933CC",
- "0xCC3399",
- "0x80a033",
- "0xFD9816",
- "0x246AD2",
- "0xFD16EA",
- "0x49C100",
- );
-
- // first row in array contains line labels (legend)
- $legendLabels = array_shift($this->dataGraph);
-
- $line = array();
-
- // define labels
- foreach($legendLabels as $nbLabel => $labelName)
- {
- $line[$nbLabel] = new line_hollow( 1, 3, $colors[$nbLabel] );
- $line[$nbLabel]->key( $labelName, 10 );
- }
-
- $maxData = 0;
- $xLabels = array();
- $cnt = count($this->dataGraph);
-
- // loop over data
- foreach($this->dataGraph as $values)
- {
- // add x axis value (label)
- array_push($xLabels, $values['label']);
-
- // loop over values for all lines (y axis values)
- for($j = 0; $j < count($legendLabels); $j++)
- {
- // get the y axis value for line $j
- $dotValue = $values['value'.$j];
-
- // find maximum y axis value
- if( $dotValue > $maxData )
- {
- $maxData = $dotValue;
- }
+<?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: ChartVerticalBar.php 168 2008-01-14 05:26:43Z matt $ + * + * @package Piwik_Visualization + */ + +require_once "Visualization/Chart.php"; + +/** + * Customize the Evolution chart style for the flash graph + * + * @package Piwik_Visualization + */ +class Piwik_Visualization_ChartEvolution extends Piwik_Visualization_Chart +{ + function customizeGraph() + { + parent::customizeGraph(); + //$this->prepareData(); + + $colors = array( + "0x3357A0", + "0x9933CC", + "0xCC3399", + "0x80a033", + "0xFD9816", + "0x246AD2", + "0xFD16EA", + "0x49C100", + ); + + // first row in array contains line labels (legend) + $legendLabels = array_shift($this->dataGraph); + + $line = array(); + + // define labels + foreach($legendLabels as $nbLabel => $labelName) + { + $line[$nbLabel] = new line_hollow( 1, 3, $colors[$nbLabel] ); + $line[$nbLabel]->key( $labelName, 10 ); + } + + $maxData = 0; + $xLabels = array(); + $cnt = count($this->dataGraph); + + // loop over data + foreach($this->dataGraph as $values) + { + // add x axis value (label) + array_push($xLabels, $values['label']); + + // loop over values for all lines (y axis values) + for($j = 0; $j < count($legendLabels); $j++) + { + // get the y axis value for line $j + $dotValue = $values['value'.$j]; + + // find maximum y axis value + if( $dotValue > $maxData ) + { + $maxData = $dotValue; + } $link = null; if($this->isLinkEnabled()) @@ -89,12 +89,12 @@ class Piwik_Visualization_ChartEvolution extends Piwik_Visualization_Chart else { $line[$j]->add($dotValue); - }
- }
- }
- $this->data_sets = $line;
- $this->set_y_max( $maxData );
- $this->set_x_labels( $xLabels );
+ } + } + } + $this->data_sets = $line; + $this->set_y_max( $maxData ); + $this->set_x_labels( $xLabels ); } private function isLinkEnabled() @@ -105,5 +105,5 @@ class Piwik_Visualization_ChartEvolution extends Piwik_Visualization_Chart $linkEnabled = !Piwik_Common::getRequestVar('disableLink', 0, 'int'); } return $linkEnabled; - }
+ } } @@ -3,18 +3,21 @@ Following up show all columns new UI element - passe en année, actions puis pages => c'est vide !!! - pages columns: first should be unique page views, sorted by unique pageviews - exclude all population doesn't work for actions +- when used with visitsGenerator, in dowloads export XML, full_url and url is 0 +- add new column "overall visits with conversion" - post 29 There seems not to be a way to export the pages information widget. I wanted to download the entry pages to see where my visitors entered my site from, but there is no option to do so. - -$("td img", domElem).attr("src") is undefined -[Break on this error] var plusDetected = $('td img', domElem).attr('src').indexOf('plus') >= 0; -datatable.js (line 907) -$("td img", domElem).attr("src") is undefined -[Break on this error] var plusDetected = $('td img', domElem).attr('src').indexOf('plus') >= 0; -when clicking MINUS on actions + $("td img", domElem).attr("src") is undefined + [Break on this error] var plusDetected = $('td img', domElem).attr('src').indexOf('plus') >= 0; + datatable.js (line 907) + $("td img", domElem).attr("src") is undefined + [Break on this error] var plusDetected = $('td img', domElem).attr('src').indexOf('plus') >= 0; + when clicking MINUS on actions - icons disappear when showing all columns for browsers - exclude low population doesn't work for providers - ADD forward of non true show_values to forward when specified from URL to ajax - disable show icon in some iframes and/or dashboard +- when exclude low, switch back to normal table, include all is not avail and low are still excluded + ->when switch back to simple table, reset exclude - do proper CSS factoring - clarify 'false' '0' values etc. //convert all JS datatable footer parameter values to '1' or '0' - check state of javascript footer (should not have safe_decode and filter_add_columns blah) @@ -39,26 +42,31 @@ UI fixes + adjustements - write test to check disabled db plugin default - show total size in MB in db plugin - search inside datatable doesnt really work eg. "web analytics" restore regex search + can we find a "binary operator string -> regex" conversion function? - bounce count should be bounce rate - idem in 222 times that a returning visit has bounced (left the site after one page) - show new vs returning more clearly in UI - calendar CSS on API page - WRONG::: 147 sites Internet différents (utilisant 147 différentes adresses) - see also #421 +- add "days since last visit" +- add "visit count until conversion" check analytics books high priority features ==== -- tester clickheat http://www.labsmedia.com/clickheat/piwik-clickheat.tar.gz + add FAQ -- sync update + email install - Goal tracking +- tester clickheat http://www.labsmedia.com/clickheat/piwik-clickheat.tar.gz + add FAQ +- sync update + email install + automatic update - Live! - simple MultiSites report plugin. - new JS tagging API - Exclude webmaster - Loyalty (Most people visited: 1 times) and Recency (Most people last visited: X days ago) - + http://en.wikipedia.org/wiki/RFM + + UI elements: ==== - add link under widgets to report (optional display) @@ -75,9 +83,12 @@ website Bugs - add icon calendar http://developer.yahoo.com/ypatterns/pattern.php?pattern=calendar#Solution - something is wrong in JS calendar when year selected -- I had to chmod 777 tmp/ to pass install -all graphic showing data evolution are labelled with the text "nb_uniq_visitors" no matter if youre watching last visits graph, global visits graph or any other. Its happening in every graph under Visitors subpages and in the Dashboard. -In Referers->Evolution page, in addition, I think its melting titles because I read in spanish "Entrada directa nb_uniq_visitors" for the first graph, "Paginas web nb_uniq_visitors" for the second and so on. +- all graphic showing data evolution are labelled with the text "nb_uniq_visitors" + no matter if youre watching last visits graph, global visits graph or any other. + Its happening in every graph under Visitors subpages and in the Dashboard. +- In Referers->Evolution page, in addition, I think its melting titles because I read + in spanish "Entrada directa nb_uniq_visitors" for the first graph, + "Paginas web nb_uniq_visitors" for the second and so on. To clean - plugins should listen to user delete/ site delete and delete their own user/site related data diff --git a/misc/generateVisits.php b/misc/generateVisits.php index f8984199e8..38774e0d00 100644 --- a/misc/generateVisits.php +++ b/misc/generateVisits.php @@ -3,10 +3,10 @@ * The script can be used to generate huge number of visits and actions * for a given number of days. */ -$minVisitors = 500; -$maxVisitors = 1000; +$minVisitors = 200; +$maxVisitors = 200; $nbActions = 3; -$daysToCompute = 1; +$daysToCompute = 10; //----------------------------------------------------------------------------- error_reporting(E_ALL|E_NOTICE); @@ -46,6 +46,7 @@ require_once "Tracker/Action.php"; require_once "Tracker/Db.php"; require_once "Tracker/Visit.php"; require_once "Tracker/Generator.php"; +require_once "Tracker/GoalManager.php"; //Piwik_PluginsManager::getInstance()->unloadPlugins(); @@ -73,6 +74,7 @@ while($startTime <= time()) $visitors = rand($minVisitors, $maxVisitors); $actions = $nbActions; $generator->setTimestampToUse($startTime); + $nbActionsTotalThisDay = $generator->generate($visitors, $actions); $actionsPerVisit = round($nbActionsTotalThisDay / $visitors); print("Generated $visitors unique visitors and $actionsPerVisit actions per visit for the ".date("Y-m-d", $startTime)."<br>\n"); @@ -25,6 +25,7 @@ require_once "Tracker/Action.php"; require_once "Cookie.php"; require_once "Tracker/Db.php"; require_once "Tracker/Visit.php"; +require_once "Tracker/GoalManager.php"; $GLOBALS['DEBUGPIWIK'] = false; diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 0972d00e46..4545762f64 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -1,21 +1,21 @@ -<?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_Actions
- */
-
-/**
- *
- * @package Piwik_Actions
- */
-class Piwik_Actions extends Piwik_Plugin
-{
- static protected $actionCategoryDelimiter = null;
+<?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_Actions + */ + +/** + * + * @package Piwik_Actions + */ +class Piwik_Actions extends Piwik_Plugin +{ + static protected $actionCategoryDelimiter = null; static protected $limitLevelSubCategory = 10; public function getInformation() @@ -41,309 +41,310 @@ class Piwik_Actions extends Piwik_Plugin ); return $hooks; } -
- public function __construct()
+ + public function __construct() + { + $this->setCategoryDelimiter( Zend_Registry::get('config')->General->action_category_delimiter); + } + + public function setCategoryDelimiter($delimiter) { - $this->setCategoryDelimiter( Zend_Registry::get('config')->General->action_category_delimiter);
- }
-
- public function setCategoryDelimiter($delimiter)
- {
- self::$actionCategoryDelimiter = $delimiter;
- }
-
+ self::$actionCategoryDelimiter = $delimiter; + } + function addWidgets() { Piwik_AddWidget( 'Actions', 'getActions', Piwik_Translate('Actions_SubmenuPages')); Piwik_AddWidget( 'Actions', 'getDownloads', Piwik_Translate('Actions_SubmenuDownloads')); Piwik_AddWidget( 'Actions', 'getOutlinks', Piwik_Translate('Actions_SubmenuOutlinks')); } -
- function addMenus()
- {
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'getActions'));
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'getOutlinks'));
- Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'getDownloads'));
- }
-
- function archivePeriod( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- $dataTableToSum = array(
- 'Actions_actions',
- 'Actions_downloads',
- 'Actions_outlink',
- );
+ + function addMenus() + { + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'getActions')); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'getOutlinks')); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'getDownloads')); + } + + function archivePeriod( $notification ) + { + $archiveProcessing = $notification->getNotificationObject(); + + $dataTableToSum = array( + 'Actions_actions', + 'Actions_downloads', + 'Actions_outlink', + ); $maximumRowsInDataTableLevelZero = 200; - $maximumRowsInSubDataTable = 50;
- $archiveProcessing->archiveDataTable($dataTableToSum, $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable);
- }
-
- /**
- * Compute all the actions along with their hierarchies.
- *
- * For each action we process the "interest statistics" :
- * visits, unique visitors, bouce count, sum visit length.
- *
- *
- */
- public function archiveDay( $notification )
- {
- $archiveProcessing = $notification->getNotificationObject();
-
- require_once "Tracker/Action.php";
-
- $this->actionsTablesByType = array(
- Piwik_Tracker_Action::TYPE_ACTION => array(),
- Piwik_Tracker_Action::TYPE_DOWNLOAD => array(),
- Piwik_Tracker_Action::TYPE_OUTLINK => array(),
- );
-
- // This row is used in the case where an action is know as an exit_action
- // but this action was not properly recorded when it was hit in the first place
- // so we add this fake row information to make sure there is a nb_hits, etc. column for every action
- $this->defaultRow = new Piwik_DataTable_Row(array(
- Piwik_DataTable_Row::COLUMNS => array(
- 'nb_visits' => 1,
- 'nb_uniq_visitors' => 1,
- 'nb_hits' => 1,
- )));
-
- /*
- * Actions global information
- */
- $query = "SELECT name,
- type,
- count(distinct t1.idvisit) as nb_visits,
- count(distinct visitor_idcookie) as nb_uniq_visitors,
- count(*) as nb_hits
- FROM (".$archiveProcessing->logTable." as t1
- LEFT JOIN ".$archiveProcessing->logVisitActionTable." as t2 USING (idvisit))
- LEFT JOIN ".$archiveProcessing->logActionTable." as t3 USING (idaction)
- WHERE visit_server_date = ?
- AND idsite = ?
+ $maximumRowsInSubDataTable = 50; + $archiveProcessing->archiveDataTable($dataTableToSum, $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); + } + + /** + * Compute all the actions along with their hierarchies. + * + * For each action we process the "interest statistics" : + * visits, unique visitors, bouce count, sum visit length. + * + * + */ + public function archiveDay( $notification ) + { + $archiveProcessing = $notification->getNotificationObject(); + + require_once "Tracker/Action.php"; + + $this->actionsTablesByType = array( + Piwik_Tracker_Action::TYPE_ACTION => array(), + Piwik_Tracker_Action::TYPE_DOWNLOAD => array(), + Piwik_Tracker_Action::TYPE_OUTLINK => array(), + ); + + // This row is used in the case where an action is know as an exit_action + // but this action was not properly recorded when it was hit in the first place + // so we add this fake row information to make sure there is a nb_hits, etc. column for every action + $this->defaultRow = new Piwik_DataTable_Row(array( + Piwik_DataTable_Row::COLUMNS => array( + 'nb_visits' => 1, + 'nb_uniq_visitors' => 1, + 'nb_hits' => 1, + ))); + + /* + * Actions global information + */ + $query = "SELECT name, + type, + count(distinct t1.idvisit) as nb_visits, + count(distinct visitor_idcookie) as nb_uniq_visitors, + count(*) as nb_hits + FROM (".$archiveProcessing->logTable." as t1 + LEFT JOIN ".$archiveProcessing->logVisitActionTable." as t2 USING (idvisit)) + LEFT JOIN ".$archiveProcessing->logActionTable." as t3 USING (idaction) + WHERE visit_server_date = ? + AND idsite = ? GROUP BY t3.idaction - ORDER BY nb_hits DESC";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
-
-
- /*
- * Entry actions
- */
- $query = "SELECT name,
- type,
- count(distinct visitor_idcookie) as entry_nb_unique_visitor,
- count(*) as entry_nb_visits,
- sum(visit_total_actions) as entry_nb_actions,
- sum(visit_total_time) as entry_sum_visit_length,
- sum(case visit_total_actions when 1 then 1 else 0 end) as entry_bounce_count
- FROM ".$archiveProcessing->logTable."
- JOIN ".$archiveProcessing->logActionTable." ON (visit_entry_idaction = idaction)
- WHERE visit_server_date = ?
- AND idsite = ?
+ ORDER BY nb_hits DESC"; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + + + /* + * Entry actions + */ + $query = "SELECT name, + type, + count(distinct visitor_idcookie) as entry_nb_unique_visitor, + count(*) as entry_nb_visits, + sum(visit_total_actions) as entry_nb_actions, + sum(visit_total_time) as entry_sum_visit_length, + sum(case visit_total_actions when 1 then 1 else 0 end) as entry_bounce_count + FROM ".$archiveProcessing->logTable." + JOIN ".$archiveProcessing->logActionTable." ON (visit_entry_idaction = idaction) + WHERE visit_server_date = ? + AND idsite = ? GROUP BY visit_entry_idaction - ";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
-
-
- /*
- * Exit actions
- */
- $query = "SELECT name,
- type,
- count(distinct visitor_idcookie) as exit_nb_unique_visitor,
- count(*) as exit_nb_visits,
- sum(case visit_total_actions when 1 then 1 else 0 end) as exit_bounce_count
-
- FROM ".$archiveProcessing->logTable."
- JOIN ".$archiveProcessing->logActionTable." ON (visit_exit_idaction = idaction)
- WHERE visit_server_date = ?
- AND idsite = ?
+ "; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + + + /* + * Exit actions + */ + $query = "SELECT name, + type, + count(distinct visitor_idcookie) as exit_nb_unique_visitor, + count(*) as exit_nb_visits, + sum(case visit_total_actions when 1 then 1 else 0 end) as exit_bounce_count + + FROM ".$archiveProcessing->logTable." + JOIN ".$archiveProcessing->logActionTable." ON (visit_exit_idaction = idaction) + WHERE visit_server_date = ? + AND idsite = ? GROUP BY visit_exit_idaction - ";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
-
- /*
- * Time per action
- */
- $query = "SELECT name,
- type,
- sum(time_spent_ref_action) as sum_time_spent
- FROM (".$archiveProcessing->logTable." log_visit
- JOIN ".$archiveProcessing->logVisitActionTable." log_link_visit_action USING (idvisit))
- JOIN ".$archiveProcessing->logActionTable." log_action ON (log_action.idaction = log_link_visit_action.idaction_ref)
- WHERE visit_server_date = ?
- AND idsite = ?
- GROUP BY idaction_ref
- ";
- $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite ));
-
- $modified = $this->updateActionsTableWithRowQuery($query);
+ "; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + + /* + * Time per action + */ + $query = "SELECT name, + type, + sum(time_spent_ref_action) as sum_time_spent + FROM (".$archiveProcessing->logTable." log_visit + JOIN ".$archiveProcessing->logVisitActionTable." log_link_visit_action USING (idvisit)) + JOIN ".$archiveProcessing->logActionTable." log_action ON (log_action.idaction = log_link_visit_action.idaction_ref) + WHERE visit_server_date = ? + AND idsite = ? + GROUP BY idaction_ref + "; + $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); + $modified = $this->updateActionsTableWithRowQuery($query); + $this->archiveDayRecordInDatabase(); + } + + protected function archiveDayRecordInDatabase() + { $maximumRowsInDataTableLevelZero = 200; $maximumRowsInSubDataTable = 50; $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION]); $s = $dataTable->getSerialized( $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable ); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_actions', $s);
+ $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_actions', $s); $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD]); $s = $dataTable->getSerialized($maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable ); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_downloads', $s);
-
- $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK]);
- $s = $dataTable->getSerialized( $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable );
- $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_outlink', $s);
+ $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_downloads', $s); + + $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_OUTLINK]); + $s = $dataTable->getSerialized( $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable ); + $record = new Piwik_ArchiveProcessing_Record_BlobArray('Actions_outlink', $s); - unset($this->actionsTablesByType);
- }
-
- static public function splitUrl($url)
- {
- $matches = $split_arr = array();
- $n = preg_match("#://[^/]+(/)#",$url, $matches, PREG_OFFSET_CAPTURE);
- if( $n )
- {
- $host = substr($url, 0, $matches[1][1]);
- $split_arr = array($host, $url);
- }
- else
- {
- $split_arr = array($url);
- }
- return $split_arr;
- }
-
- static public function getActionCategoryFromName($name)
- {
- $isUrl = false;
- // case the name is an URL we dont clean the name the same way
- if(Piwik_Common::isLookLikeUrl($name)
- || preg_match('#^mailto:#',$name))
- {
- $split = self::splitUrl($name);
- $isUrl = true;
- }
- else
+ unset($this->actionsTablesByType); + } + + static public function splitUrl($url) + { + $matches = $split_arr = array(); + $n = preg_match("#://[^/]+(/)#",$url, $matches, PREG_OFFSET_CAPTURE); + if( $n ) + { + $host = substr($url, 0, $matches[1][1]); + $split_arr = array($host, $url); + } + else + { + $split_arr = array($url); + } + return $split_arr; + } + + static public function getActionCategoryFromName($name) + { + $isUrl = false; + // case the name is an URL we dont clean the name the same way + if(Piwik_Common::isLookLikeUrl($name) + || preg_match('#^mailto:#',$name)) + { + $split = self::splitUrl($name); + $isUrl = true; + } + else { if(empty(self::$actionCategoryDelimiter)) { $split = array($name); } else - {
+ { $split = explode(self::$actionCategoryDelimiter, $name, self::$limitLevelSubCategory); - }
- }
- return array( $isUrl, $split);
- }
-
-
- protected function updateActionsTableWithRowQuery($query)
- {
- $rowsProcessed = 0;
-
- while( $row = $query->fetch() )
- {
- // split the actions by category
- $returned = $this->getActionCategoryFromName($row['name']);
- $aActions = $returned[1];
- $isUrl = $returned[0];
-
- // we work on the root table of the given TYPE (either ACTION or DOWNLOAD or OUTLINK etc.)
- $currentTable =& $this->actionsTablesByType[$row['type']];
-
- // go to the level of the subcategory
- $end = count($aActions)-1;
- for($level = 0 ; $level < $end; $level++)
- {
- $actionCategory = $aActions[$level];
- $currentTable =& $currentTable[$actionCategory];
- }
- $actionName = $aActions[$end];
-
- // create a new element in the array for the page
- // we are careful to prefix the pageName with some value so that if a page has the same name
- // as a category we don't overwrite or do other silly things
-
- // if the page name is not a URL we add a / before
- if( !$isUrl )
- {
- // we know that the concatenation of a space and the name of the action
- // will always be unique as all the action names have been trimmed before reaching this point
- $actionName = '/' . $actionName;
- }
- else
- {
- $actionName = ' ' . $actionName;
- }
-
- // currentTable is now the array element corresponding the the action
- // at this point we may be for example at the 4th level of depth in the hierarchy
- $currentTable =& $currentTable[$actionName];
-
- // add the row to the matching sub category subtable
- if(!($currentTable instanceof Piwik_DataTable_Row))
- {
- $currentTable = new Piwik_DataTable_Row(
- array( Piwik_DataTable_Row::COLUMNS =>
- array( 'label' => (string)$actionName,
- 'full_url' => (string)$row['name'],
- )
- )
- );
- }
-
- foreach($row as $name => $value)
- {
- // we don't add this information as itnot pertinent
- // name is already set as the label // and it has been cleaned from the categories and extracted from the initial string
- // type is used to partition the different actions type in different table. Adding the info to the row would be a duplicate.
- if($name != 'name' && $name != 'type')
- {
- // in some very rare case, we actually have twice the same action name with 2 different idaction
- // this happens when 2 visitors visit the same new page at the same time, there is a SELECT and an INSERT for each new page,
- // and in between the two the other visitor comes.
- // here we handle the case where there is already a row for this action name, if this is the case we add the value
- if(($alreadyValue = $currentTable->getColumn($name)) !== false)
- {
- $currentTable->setColumn($name, $alreadyValue+$value);
- }
- else
- {
- $currentTable->addColumn($name, $value);
- }
- }
- }
-
- // if the exit_action was not recorded properly in the log_link_visit_action
- // there would be an error message when getting the nb_hits column
- // we must fake the record and add the columns
- if($currentTable->getColumn('nb_hits') === false)
- {
- // to test this code: delete the entries in log_link_action_visit for
- // a given exit_idaction
- foreach($this->defaultRow->getColumns() as $name => $value)
- {
- $currentTable->addColumn($name, $value);
- }
- }
- // simple count
- $rowsProcessed++;
- }
-
- // just to make sure php copies the last $currentTable in the $parentTable array
- $currentTable =& $this->actionsTablesByType;
-
- return $rowsProcessed;
- }
-}
-
+ } + } + return array( $isUrl, $split); + } + + + protected function updateActionsTableWithRowQuery($query) + { + $rowsProcessed = 0; + + while( $row = $query->fetch() ) + { + // split the actions by category + $returned = $this->getActionCategoryFromName($row['name']); + $aActions = $returned[1]; + $isUrl = $returned[0]; + + // we work on the root table of the given TYPE (either ACTION or DOWNLOAD or OUTLINK etc.) + $currentTable =& $this->actionsTablesByType[$row['type']]; + + // go to the level of the subcategory + $end = count($aActions)-1; + for($level = 0 ; $level < $end; $level++) + { + $actionCategory = $aActions[$level]; + $currentTable =& $currentTable[$actionCategory]; + } + $actionName = $aActions[$end]; + + // create a new element in the array for the page + // we are careful to prefix the pageName with some value so that if a page has the same name + // as a category we don't overwrite or do other silly things + + // if the page name is not a URL we add a / before + if( !$isUrl ) + { + // we know that the concatenation of a space and the name of the action + // will always be unique as all the action names have been trimmed before reaching this point + $actionName = '/' . $actionName; + } + else + { + $actionName = ' ' . $actionName; + } + + // currentTable is now the array element corresponding the the action + // at this point we may be for example at the 4th level of depth in the hierarchy + $currentTable =& $currentTable[$actionName]; + + // add the row to the matching sub category subtable + if(!($currentTable instanceof Piwik_DataTable_Row)) + { + $currentTable = new Piwik_DataTable_Row( + array( Piwik_DataTable_Row::COLUMNS => + array( 'label' => (string)$actionName, + 'full_url' => (string)$row['name'], + ) + ) + ); + } + + foreach($row as $name => $value) + { + // we don't add this information as itnot pertinent + // name is already set as the label // and it has been cleaned from the categories and extracted from the initial string + // type is used to partition the different actions type in different table. Adding the info to the row would be a duplicate. + if($name != 'name' && $name != 'type') + { + // in some very rare case, we actually have twice the same action name with 2 different idaction + // this happens when 2 visitors visit the same new page at the same time, there is a SELECT and an INSERT for each new page, + // and in between the two the other visitor comes. + // here we handle the case where there is already a row for this action name, if this is the case we add the value + if(($alreadyValue = $currentTable->getColumn($name)) !== false) + { + $currentTable->setColumn($name, $alreadyValue+$value); + } + else + { + $currentTable->addColumn($name, $value); + } + } + } + + // if the exit_action was not recorded properly in the log_link_visit_action + // there would be an error message when getting the nb_hits column + // we must fake the record and add the columns + if($currentTable->getColumn('nb_hits') === false) + { + // to test this code: delete the entries in log_link_action_visit for + // a given exit_idaction + foreach($this->defaultRow->getColumns() as $name => $value) + { + $currentTable->addColumn($name, $value); + } + } + // simple count + $rowsProcessed++; + } + + // just to make sure php copies the last $currentTable in the $parentTable array + $currentTable =& $this->actionsTablesByType; + + return $rowsProcessed; + } +} + diff --git a/plugins/Actions/Controller.php b/plugins/Actions/Controller.php index 4957756f3c..85d3cf9466 100644 --- a/plugins/Actions/Controller.php +++ b/plugins/Actions/Controller.php @@ -149,7 +149,7 @@ class Piwik_Actions_Controller extends Piwik_Controller // and each of them has 1 or 2 hits... $nbActionsLowPopulationThreshold = min($visitsInfo->getColumn('max_actions')-1, $nbActionsLowPopulationThreshold-1); - $view->setExcludeLowPopulation( $nbActionsLowPopulationThreshold, 'nb_hits' ); + $view->setExcludeLowPopulation( 'nb_hits', $nbActionsLowPopulationThreshold ); $view->main(); diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php index 26c0875aa4..185cdfb2c2 100644 --- a/plugins/CoreHome/Controller.php +++ b/plugins/CoreHome/Controller.php @@ -1,26 +1,26 @@ -<?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: Controller.php 561 2008-07-21 00:00:35Z matt $
- *
- * @package Piwik_CoreHome
- *
- */
-
-require_once "API/Request.php";
-require_once "ViewDataTable.php";
-
-/**
- * @package Piwik_CoreHome
- */
-class Piwik_CoreHome_Controller extends Piwik_Controller
-{
- function getDefaultAction()
- {
- return 'redirectToIndex';
+<?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: Controller.php 561 2008-07-21 00:00:35Z matt $ + * + * @package Piwik_CoreHome + * + */ + +require_once "API/Request.php"; +require_once "ViewDataTable.php"; + +/** + * @package Piwik_CoreHome + */ +class Piwik_CoreHome_Controller extends Piwik_Controller +{ + function getDefaultAction() + { + return 'redirectToIndex'; } function redirectToIndex() @@ -32,36 +32,36 @@ class Piwik_CoreHome_Controller extends Piwik_Controller return Piwik_FrontController::dispatch('Login', false); } parent::redirectToIndex('CoreHome', 'index'); - }
+ } - protected function setGeneralVariablesView($view)
- {
- parent::setGeneralVariablesView($view);
- $view->menu = Piwik_GetMenu();
- }
+ protected function setGeneralVariablesView($view) + { + parent::setGeneralVariablesView($view); + $view->menu = Piwik_GetMenu(); + } - public function showInContext()
- {
- $controllerName = Piwik_Common::getRequestVar('moduleToLoad');
- $actionName = Piwik_Common::getRequestVar('actionToLoad', 'index');
-
- $view = $this->getDefaultIndexView();
- $view->basicHtmlView = true;
- $view->content = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName );
- echo $view->render();
- }
-
- protected function getDefaultIndexView()
- {
- $view = new Piwik_View('CoreHome/templates/index.tpl');
- $this->setGeneralVariablesView($view);
+ public function showInContext() + { + $controllerName = Piwik_Common::getRequestVar('moduleToLoad'); + $actionName = Piwik_Common::getRequestVar('actionToLoad', 'index'); + + $view = $this->getDefaultIndexView(); + $view->basicHtmlView = true; + $view->content = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName ); + echo $view->render(); + } + + protected function getDefaultIndexView() + { + $view = new Piwik_View('CoreHome/templates/index.tpl'); + $this->setGeneralVariablesView($view); $view->content = ''; - return $view;
+ return $view; + } + + public function index() + { + $view = $this->getDefaultIndexView(); + echo $view->render(); } -
- public function index()
- {
- $view = $this->getDefaultIndexView();
- echo $view->render();
- }
-}
+} diff --git a/plugins/CoreHome/templates/cloud.tpl b/plugins/CoreHome/templates/cloud.tpl index 8989e48a85..b3c208ccc0 100644 --- a/plugins/CoreHome/templates/cloud.tpl +++ b/plugins/CoreHome/templates/cloud.tpl @@ -1,4 +1,4 @@ -<div id="{$id}"> +<div id="{$properties.uniqueId}"> {literal} <style> diff --git a/plugins/CoreHome/templates/datatable.css b/plugins/CoreHome/templates/datatable.css index 364e24cd69..5a3c3577a7 100644 --- a/plugins/CoreHome/templates/datatable.css +++ b/plugins/CoreHome/templates/datatable.css @@ -303,20 +303,6 @@ table thead div { padding: 0.5em; } -#tooltip h3 { - margin:0; - padding:0; -} -#tooltip { - position: absolute; - z-index: 3000; - border: 1px solid #111; - background-color: #eee; - opacity: 0.85; - font-size: 0.7em; - padding:7px; -} - /* Actions table */ table.dataTableActions tr td.labelodd { background-image: none; diff --git a/plugins/CoreHome/templates/datatable.js b/plugins/CoreHome/templates/datatable.js index c8b7c26079..092d203570 100644 --- a/plugins/CoreHome/templates/datatable.js +++ b/plugins/CoreHome/templates/datatable.js @@ -63,8 +63,7 @@ dataTable.prototype = 'filter_pattern', 'filter_column_recursive', 'filter_pattern_recursive', - 'filter_excludelowpop', - 'filter_excludelowpop_value', + 'enable_filter_excludelowpop', 'filter_offset', 'filter_limit', 'filter_sort_column', @@ -255,19 +254,19 @@ dataTable.prototype = .each( function() { - if(typeof self.param.filter_excludelowpop == 'undefined') + if(typeof self.param.enable_filter_excludelowpop == 'undefined') { - self.param.filter_excludelowpop = 0; + self.param.enable_filter_excludelowpop = 0; } - if(Number(self.param.filter_excludelowpop) != 0) + if(Number(self.param.enable_filter_excludelowpop) != 0) { string = _pk_translate('CoreHome_IncludeAllPopulation'); - self.param.filter_excludelowpop = 1; + self.param.enable_filter_excludelowpop = 1; } else { string = _pk_translate('CoreHome_ExcludeLowPopulation'); - self.param.filter_excludelowpop = 0; + self.param.enable_filter_excludelowpop = 0; } $(this).html(string); } @@ -276,7 +275,7 @@ dataTable.prototype = .click( function() { - self.param.filter_excludelowpop = 1 - self.param.filter_excludelowpop; + self.param.enable_filter_excludelowpop = 1 - self.param.enable_filter_excludelowpop; self.param.filter_offset = 0; self.reloadAjaxDataTable(true, callbackSuccess); } @@ -484,6 +483,19 @@ dataTable.prototype = } ); + $('#tableGoals', domElem) + .show() + .click( + function(){ + // we only reset the limit filter, in case switch to table view from cloud view where limit is custom set to 30 + // this value is stored in config file General->dataTable_default_limit but this is more an edge case so ok to set it to 10 + delete self.param.filter_limit; + delete self.param.enable_filter_excludelowpop; + self.param.viewDataTable = 'tableGoals'; + self.reloadAjaxDataTable(); + } + ); + $('#tableAllColumnsSwitch', domElem) .show() .click( @@ -505,12 +517,17 @@ dataTable.prototype = var method = $(this).attr('methodToCall'); var filter_limit = $(this).attr('filter_limit'); + var param_date = self.param.date; + var date = $(this).attr('date'); + if(typeof date != 'undefined') { + param_date = date; + } var str = '?module=API' +'&method='+method +'&format='+format +'&idSite='+self.param.idSite +'&period='+self.param.period - +'&date='+self.param.date + +'&date='+param_date +'&token_auth='+piwik.token_auth; if( filter_limit ) { @@ -649,7 +666,7 @@ dataTable.prototype = var filtersToRestore = self.resetAllFilters(); self.param.idSubtable = idSubTable; - self.param.action = self.param.actionToLoadTheSubTable; + self.param.action = self.param.controllerActionCalledWhenRequestSubTable; self.reloadAjaxDataTable(false); self.param.action = savedActionVariable; @@ -862,7 +879,7 @@ actionDataTable.prototype = delete self.param.filter_pattern; self.param.idSubtable = idSubTable; - self.param.action = self.param.actionToLoadTheSubTable; + self.param.action = self.param.controllerActionCalledWhenRequestSubTable; self.reloadAjaxDataTable(false, function(resp){self.actionsSubDataTableLoaded(resp)}); self.param.action = savedActionVariable; diff --git a/plugins/CoreHome/templates/datatable.tpl b/plugins/CoreHome/templates/datatable.tpl index 49e233f82e..55291ee9ea 100644 --- a/plugins/CoreHome/templates/datatable.tpl +++ b/plugins/CoreHome/templates/datatable.tpl @@ -1,12 +1,12 @@ -<div id="{$id}">
- <div class="{if isset($javascriptVariablesToSet.idSubtable)&& $javascriptVariablesToSet.idSubtable!=0}sub{/if}{if $javascriptVariablesToSet.viewDataTable=='tableAllColumns'}dataTableAllColumnsWrapper{else}dataTableWrapper{/if}">
+<div id="{$properties.uniqueId}">
+ <div class="{if isset($javascriptVariablesToSet.idSubtable)&& $javascriptVariablesToSet.idSubtable!=0}sub{/if}{if $javascriptVariablesToSet.viewDataTable=='tableAllColumns'}dataTableAllColumnsWrapper{elseif $javascriptVariablesToSet.viewDataTable=='tableGoals'}dataTableAllColumnsWrapper{else}dataTableWrapper{/if}">
{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
{$arrayDataTable.message}
{else}
{if count($arrayDataTable) == 0}
<div id="emptyDatatable">{'CoreHome_TableNoData'|translate}</div>
{else}
- <a name="{$id}"></a>
+ <a name="{$properties.uniqueId}"></a>
<table cellspacing="0" class="dataTable">
<thead>
<tr>
@@ -18,11 +18,11 @@ <tbody>
{foreach from=$arrayDataTable item=row}
- <tr {if $row.idsubdatatable}class="subDataTable" id="{$row.idsubdatatable}"{/if}>
+ <tr {if $row.idsubdatatable && $javascriptVariablesToSet.controllerActionCalledWhenRequestSubTable != null}class="subDataTable" id="{$row.idsubdatatable}"{/if}>
{foreach from=$dataTableColumns item=column}
<td>
{if !$row.idsubdatatable && $column.name=='label' && isset($row.metadata.url)}<span id="urlLink">{$row.metadata.url}</span>{/if}
-{if $column.name=='label' && isset($row.metadata.logo)}<img {if isset($row.metadata.logoWidth)}width="{$row.metadata.logoWidth}"{/if} {if isset($row.metadata.logoHeight)}height="{$row.metadata.logoHeight}"{/if} src="{$row.metadata.logo}" />{/if}
+{if $column.name=='label'}{logoHtml metadata=$row.metadata alt=$row.columns.label}{/if}
{* sometimes all columns are not set in the datatable, we assume the value 0 *}
{if isset($row.columns[$column.name])}{$row.columns[$column.name]}{else}0{/if}
</td>
diff --git a/plugins/CoreHome/templates/datatable_actions.tpl b/plugins/CoreHome/templates/datatable_actions.tpl index 94341cddff..c2dbe1868f 100644 --- a/plugins/CoreHome/templates/datatable_actions.tpl +++ b/plugins/CoreHome/templates/datatable_actions.tpl @@ -1,4 +1,4 @@ -<div id="{$id}">
+<div id="{$properties.uniqueId}">
<div class="dataTableActionsWrapper">
{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
{$arrayDataTable.message}
diff --git a/plugins/CoreHome/templates/datatable_actions_js.tpl b/plugins/CoreHome/templates/datatable_actions_js.tpl index 5bf5ed2bca..568fe83d7b 100644 --- a/plugins/CoreHome/templates/datatable_actions_js.tpl +++ b/plugins/CoreHome/templates/datatable_actions_js.tpl @@ -1,12 +1,12 @@ <script type="text/javascript" defer="defer"> $(document).ready(function(){literal}{{/literal} - actionDataTables['{$id}'] = new actionDataTable(); - actionDataTables['{$id}'].param = {literal}{{/literal} + actionDataTables['{$properties.uniqueId}'] = new actionDataTable(); + actionDataTables['{$properties.uniqueId}'].param = {literal}{{/literal} {foreach from=$javascriptVariablesToSet key=name item=value name=loop} {$name}: '{$value}'{if !$smarty.foreach.loop.last},{/if} {/foreach} {literal}};{/literal} - actionDataTables['{$id}'].init('{$id}'); + actionDataTables['{$properties.uniqueId}'].init('{$properties.uniqueId}'); {literal}}{/literal}); </script> diff --git a/plugins/CoreHome/templates/datatable_actions_recursive.tpl b/plugins/CoreHome/templates/datatable_actions_recursive.tpl index bcec62eb2c..01a22255ed 100644 --- a/plugins/CoreHome/templates/datatable_actions_recursive.tpl +++ b/plugins/CoreHome/templates/datatable_actions_recursive.tpl @@ -1,4 +1,4 @@ -<div id="{$id}">
+<div id="{$properties.uniqueId}">
<div class="dataTableActionsWrapper">
{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
{$arrayDataTable.message}
diff --git a/plugins/CoreHome/templates/datatable_actions_subdatable.tpl b/plugins/CoreHome/templates/datatable_actions_subdatable.tpl index 39e103f691..1cfe58edc9 100644 --- a/plugins/CoreHome/templates/datatable_actions_subdatable.tpl +++ b/plugins/CoreHome/templates/datatable_actions_subdatable.tpl @@ -1,4 +1,4 @@ -<tr id="{$id}"></tr>
+<tr id="{$properties.uniqueId}"></tr>
{if isset($arrayDataTable.result) and $arrayDataTable.result == 'error'}
{$arrayDataTable.message}
{else}
diff --git a/plugins/CoreHome/templates/datatable_footer.tpl b/plugins/CoreHome/templates/datatable_footer.tpl index 355fbe2a3e..1d92fbcad5 100644 --- a/plugins/CoreHome/templates/datatable_footer.tpl +++ b/plugins/CoreHome/templates/datatable_footer.tpl @@ -23,10 +23,11 @@ <span id="exportToFormat" style="display:none;padding-left:4px;">
<img width="16" height="16" src="{$piwikUrl}themes/default/images/export.png" title="{'General_Export'|translate}" />
<span id="linksExportToFormat" style="display:none;">
- <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="CSV" filter_limit="100">CSV</a> |
- <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="XML" filter_limit="100">XML</a> |
- <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="JSON" filter_limit="100">Json</a> |
- <a target="_blank" class="exportToFormat" methodToCall="{$method}" format="PHP" filter_limit="100">Php</a>
+ <a target="_blank" class="exportToFormat" methodToCall="{$properties.apiMethodToRequestDataTable}" format="CSV" filter_limit="100">CSV</a> |
+ <a target="_blank" class="exportToFormat" methodToCall="{$properties.apiMethodToRequestDataTable}" format="XML" filter_limit="100">XML</a> |
+ <a target="_blank" class="exportToFormat" methodToCall="{$properties.apiMethodToRequestDataTable}" format="JSON" filter_limit="100">Json</a> |
+ <a target="_blank" class="exportToFormat" methodToCall="{$properties.apiMethodToRequestDataTable}" format="PHP" filter_limit="100">Php</a> |
+ <a target="_blank" class="exportToFormat" methodToCall="{$properties.apiMethodToRequestDataTable}" format="RSS" filter_limit="100" date="last10"><img border="0" src="{$piwikUrl}themes/default/images/feed.png"></a>
</span>
<a class="viewDataTable" format="cloud"><img width="16" height="16" src="{$piwikUrl}themes/default/images/tagcloud.png" title="{'General_TagCloud'|translate}" /></a>
<a class="viewDataTable" format="graphVerticalBar"><img width="16" height="16" src="{$piwikUrl}themes/default/images/chart_bar.png" title="{'General_VBarGraph'|translate}" /></a>
@@ -44,6 +45,13 @@ {/if}
</span>
{/if}
+ {if $properties.show_goals}
+ <span id="tableGoals" style="display:none;float:right;padding-right:4px;">
+ {if $javascriptVariablesToSet.viewDataTable != 'tableGoals'}
+ <img title="View Goals" src="{$piwikUrl}themes/default/images/goal.png" />
+ {/if}
+ </span>
+ {/if}
</span>
</div>
{/if}
diff --git a/plugins/CoreHome/templates/datatable_js.tpl b/plugins/CoreHome/templates/datatable_js.tpl index c3e5c9e5c3..acae875cf4 100644 --- a/plugins/CoreHome/templates/datatable_js.tpl +++ b/plugins/CoreHome/templates/datatable_js.tpl @@ -1,13 +1,13 @@ <script type="text/javascript" defer="defer"> $(document).ready(function(){literal}{{/literal} - dataTables['{$id}'] = new dataTable(); - dataTables['{$id}'].param = {literal}{{/literal} + dataTables['{$properties.uniqueId}'] = new dataTable(); + dataTables['{$properties.uniqueId}'].param = {literal}{{/literal} {foreach from=$javascriptVariablesToSet key=name item=value name=loop} {$name}: '{$value}'{if !$smarty.foreach.loop.last},{/if} {/foreach} {literal}};{/literal} - dataTables['{$id}'].init('{$id}'); + dataTables['{$properties.uniqueId}'].init('{$properties.uniqueId}'); {literal}}{/literal}); </script> diff --git a/plugins/CoreHome/templates/graph.tpl b/plugins/CoreHome/templates/graph.tpl index 74e6df6e1f..4e2bd9cda4 100644 --- a/plugins/CoreHome/templates/graph.tpl +++ b/plugins/CoreHome/templates/graph.tpl @@ -1,4 +1,4 @@ -<div id="{$id}">
+<div id="{$properties.uniqueId}">
<div class="{if $graphType=='evolution'}dataTableGraphEvolutionWrapper{else}dataTableGraphWrapper{/if}">
{$jsInvocationTag}
diff --git a/plugins/ExampleAPI/API.php b/plugins/ExampleAPI/API.php index 40e0924cf2..ca052d42aa 100644 --- a/plugins/ExampleAPI/API.php +++ b/plugins/ExampleAPI/API.php @@ -35,6 +35,7 @@ class Piwik_ExampleAPI_API public function getPiwikVersion() { + Piwik::checkUserHasSomeViewAccess(); return Piwik_Version::VERSION; } diff --git a/plugins/ExamplePlugin/ExamplePlugin.php b/plugins/ExamplePlugin/ExamplePlugin.php index 106a6cdbb8..a2cc37bf15 100644 --- a/plugins/ExamplePlugin/ExamplePlugin.php +++ b/plugins/ExamplePlugin/ExamplePlugin.php @@ -21,6 +21,24 @@ class Piwik_ExamplePlugin extends Piwik_Plugin 'version' => '0.1', ); } + + public function getListHooksRegistered() + { + return array( +// 'Controller.renderView' => 'addUniqueVisitorsColumnToGivenReport', + ); + } + + function addUniqueVisitorsColumnToGivenReport($notification) + { + $view = $notification->getNotificationInfo(); + $view = $view['view']; + if($view->getCurrentControllerName() == 'Referers' + && $view->getCurrentControllerAction() == 'getWebsites') + { + $view->addColumnToDisplay('nb_uniq_visitors'); + } + } } // we register the widgets so they appear in the "Add a new widget" window in the dashboard diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php index f843263eb9..1bee5cdbbf 100644 --- a/plugins/Installation/Controller.php +++ b/plugins/Installation/Controller.php @@ -1,135 +1,135 @@ -<?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_Installation
- */
-
-require_once "Installation/View.php";
-
-/**
- *
- * @package Piwik_Installation
- */
-class Piwik_Installation_Controller extends Piwik_Controller
-{
- // public so plugins can add/delete installation steps
- public $steps = array(
- 'welcome',
- 'systemCheck',
- 'databaseSetup',
- 'tablesCreation',
- 'generalSetup',
- 'firstWebsiteSetup',
- 'displayJavascriptCode',
- 'finished'
- );
-
- protected $pathView = 'Installation/templates/';
-
- public function __construct()
- {
- if(!isset($_SESSION['currentStepDone']))
- {
- $_SESSION['currentStepDone'] = '';
- }
-
- Piwik_PostEvent('InstallationController.construct', $this);
- }
-
- public function getInstallationSteps()
- {
- return $this->steps;
- }
-
- function getDefaultAction()
- {
- return $this->steps[0];
- }
-
- function welcome()
+<?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_Installation + */ + +require_once "Installation/View.php"; + +/** + * + * @package Piwik_Installation + */ +class Piwik_Installation_Controller extends Piwik_Controller +{ + // public so plugins can add/delete installation steps + public $steps = array( + 'welcome', + 'systemCheck', + 'databaseSetup', + 'tablesCreation', + 'generalSetup', + 'firstWebsiteSetup', + 'displayJavascriptCode', + 'finished' + ); + + protected $pathView = 'Installation/templates/'; + + public function __construct() + { + if(!isset($_SESSION['currentStepDone'])) + { + $_SESSION['currentStepDone'] = ''; + } + + Piwik_PostEvent('InstallationController.construct', $this); + } + + public function getInstallationSteps() + { + return $this->steps; + } + + function getDefaultAction() + { + return $this->steps[0]; + } + + function welcome() { require_once "Login/Controller.php"; Piwik_Login_Controller::clearSession(); -
- $view = new Piwik_Install_View(
- $this->pathView . 'welcome.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->skipThisStep( __FUNCTION__ );
- $view->showNextStep = true;
-
- $_SESSION['currentStepDone'] = __FUNCTION__;
- echo $view->render();
- }
-
- function systemCheck()
- {
- $view = new Piwik_Install_View(
- $this->pathView . 'systemCheck.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $this->skipThisStep( __FUNCTION__ );
-
- $view->infos = $this->getSystemInformation();
- $view->problemWithSomeDirectories = (false !== array_search(false, $view->infos['directories']));
-
- $view->showNextStep = !$view->problemWithSomeDirectories
- && $view->infos['phpVersion_ok']
- && $view->infos['pdo_ok']
- && $view->infos['pdo_mysql_ok']
-
- ;
- $_SESSION['currentStepDone'] = __FUNCTION__;
-
- echo $view->render();
- }
-
-
- function databaseSetup()
- {
- // case the user hits the back button
- $_SESSION['skipThisStep']['firstWebsiteSetup'] = false;
- $_SESSION['skipThisStep']['displayJavascriptCode'] = false;
-
- $view = new Piwik_Install_View(
- $this->pathView . 'databaseSetup.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $this->skipThisStep( __FUNCTION__ );
-
- $view->showNextStep = false;
- require_once "FormDatabaseSetup.php";
- $form = new Piwik_Installation_FormDatabaseSetup;
-
- if($form->validate())
- {
- $dbInfos = array(
- 'host' => $form->getSubmitValue('host'),
- 'username' => $form->getSubmitValue('username'),
- 'password' => $form->getSubmitValue('password'),
- 'dbname' => $form->getSubmitValue('dbname'),
- 'tables_prefix' => $form->getSubmitValue('tables_prefix'),
- 'adapter' => Zend_Registry::get('config')->database->adapter,
- 'port' => Zend_Registry::get('config')->database->port,
- );
-
- $dbInfos['password'] = '"'.htmlspecialchars($form->getSubmitValue('password')).'"';
-
- if(($portIndex = strpos($dbInfos['host'],':')) !== false)
- {
- $dbInfos['port'] = substr($dbInfos['host'], $portIndex + 1 );
- $dbInfos['host'] = substr($dbInfos['host'], 0, $portIndex);
- }
+ + $view = new Piwik_Install_View( + $this->pathView . 'welcome.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->skipThisStep( __FUNCTION__ ); + $view->showNextStep = true; + + $_SESSION['currentStepDone'] = __FUNCTION__; + echo $view->render(); + } + + function systemCheck() + { + $view = new Piwik_Install_View( + $this->pathView . 'systemCheck.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->checkPreviousStepIsValid( __FUNCTION__ ); + $this->skipThisStep( __FUNCTION__ ); + + $view->infos = $this->getSystemInformation(); + $view->problemWithSomeDirectories = (false !== array_search(false, $view->infos['directories'])); + + $view->showNextStep = !$view->problemWithSomeDirectories + && $view->infos['phpVersion_ok'] + && $view->infos['pdo_ok'] + && $view->infos['pdo_mysql_ok'] + + ; + $_SESSION['currentStepDone'] = __FUNCTION__; + + echo $view->render(); + } + + + function databaseSetup() + { + // case the user hits the back button + $_SESSION['skipThisStep']['firstWebsiteSetup'] = false; + $_SESSION['skipThisStep']['displayJavascriptCode'] = false; + + $view = new Piwik_Install_View( + $this->pathView . 'databaseSetup.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->checkPreviousStepIsValid( __FUNCTION__ ); + $this->skipThisStep( __FUNCTION__ ); + + $view->showNextStep = false; + require_once "FormDatabaseSetup.php"; + $form = new Piwik_Installation_FormDatabaseSetup; + + if($form->validate()) + { + $dbInfos = array( + 'host' => $form->getSubmitValue('host'), + 'username' => $form->getSubmitValue('username'), + 'password' => $form->getSubmitValue('password'), + 'dbname' => $form->getSubmitValue('dbname'), + 'tables_prefix' => $form->getSubmitValue('tables_prefix'), + 'adapter' => Zend_Registry::get('config')->database->adapter, + 'port' => Zend_Registry::get('config')->database->port, + ); + + $dbInfos['password'] = '"'.htmlspecialchars($form->getSubmitValue('password')).'"'; + + if(($portIndex = strpos($dbInfos['host'],':')) !== false) + { + $dbInfos['port'] = substr($dbInfos['host'], $portIndex + 1 ); + $dbInfos['host'] = substr($dbInfos['host'], 0, $portIndex); + } try{ try { @@ -152,64 +152,64 @@ class Piwik_Installation_Controller extends Piwik_Controller { throw new Exception(vsprintf("Your MySQL version is %s but Piwik requires at least %s.", array($mysqlVersion, $minimumMysqlVersion))); } -
+ $_SESSION['db_infos'] = $dbInfos; - $this->redirectToNextStep( __FUNCTION__ );
- } catch(Exception $e) {
- $view->errorMessage = $e->getMessage();
- }
- }
- $view->addForm($form);
-
- $view->infos = $this->getSystemInformation();
-
- echo $view->render();
- }
-
- function tablesCreation()
- {
- $view = new Piwik_Install_View(
- $this->pathView . 'tablesCreation.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $this->skipThisStep( __FUNCTION__ );
- $this->createDbFromSessionInformation();
-
- if(Piwik_Common::getRequestVar('deleteTables', 0, 'int') == 1)
- {
- Piwik::dropTables();
- $view->existingTablesDeleted = true;
-
- // when the user decides to drop the tables then we dont skip the next steps anymore
- $_SESSION['skipThisStep']['firstWebsiteSetup'] = false;
- $_SESSION['skipThisStep']['displayJavascriptCode'] = false;
- }
-
- $tablesInstalled = Piwik::getTablesInstalled();
- $tablesToInstall = Piwik::getTablesNames();
-
- if(count($tablesInstalled) > 0)
- {
- $view->someTablesInstalled = true;
- $view->tablesInstalled = implode(", ", $tablesInstalled);
-
- // when the user reuses the same tables we skip the website creation step
- $_SESSION['skipThisStep']['firstWebsiteSetup'] = true;
- $_SESSION['skipThisStep']['displayJavascriptCode'] = true;
- }
- else
- {
- Piwik::createTables();
- Piwik::createAnonymousUser();
+ $this->redirectToNextStep( __FUNCTION__ ); + } catch(Exception $e) { + $view->errorMessage = $e->getMessage(); + } + } + $view->addForm($form); + + $view->infos = $this->getSystemInformation(); + + echo $view->render(); + } + + function tablesCreation() + { + $view = new Piwik_Install_View( + $this->pathView . 'tablesCreation.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->checkPreviousStepIsValid( __FUNCTION__ ); + $this->skipThisStep( __FUNCTION__ ); + $this->createDbFromSessionInformation(); + + if(Piwik_Common::getRequestVar('deleteTables', 0, 'int') == 1) + { + Piwik::dropTables(); + $view->existingTablesDeleted = true; + + // when the user decides to drop the tables then we dont skip the next steps anymore + $_SESSION['skipThisStep']['firstWebsiteSetup'] = false; + $_SESSION['skipThisStep']['displayJavascriptCode'] = false; + } + + $tablesInstalled = Piwik::getTablesInstalled(); + $tablesToInstall = Piwik::getTablesNames(); + + if(count($tablesInstalled) > 0) + { + $view->someTablesInstalled = true; + $view->tablesInstalled = implode(", ", $tablesInstalled); + + // when the user reuses the same tables we skip the website creation step + $_SESSION['skipThisStep']['firstWebsiteSetup'] = true; + $_SESSION['skipThisStep']['displayJavascriptCode'] = true; + } + else + { + Piwik::createTables(); + Piwik::createAnonymousUser(); require_once "Version.php"; - require_once "Updater.php";
+ require_once "Updater.php"; $updater = new Piwik_Updater(); - $updater->recordComponentSuccessfullyUpdated('core', Piwik_Version::VERSION);
- $view->tablesCreated = true;
- $view->showNextStep = true;
- }
+ $updater->recordComponentSuccessfullyUpdated('core', Piwik_Version::VERSION); + $view->tablesCreated = true; + $view->showNextStep = true; + } if(isset($_SESSION['databaseCreated']) && $_SESSION['databaseCreated'] === true) @@ -218,294 +218,294 @@ class Piwik_Installation_Controller extends Piwik_Controller $view->databaseCreated = true; $_SESSION['databaseCreated'] = null; } -
- $_SESSION['currentStepDone'] = __FUNCTION__;
- echo $view->render();
- }
-
- function generalSetup()
- {
- $view = new Piwik_Install_View(
- $this->pathView . 'generalSetup.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $this->skipThisStep( __FUNCTION__ );
-
- require_once "FormGeneralSetup.php";
- $form = new Piwik_Installation_FormGeneralSetup;
-
- if($form->validate())
- {
- $superUserInfos = array(
- 'login' => $form->getSubmitValue('login'),
- 'password' => md5( $form->getSubmitValue('password') ),
- 'email' => $form->getSubmitValue('email'),
- );
-
- $_SESSION['superuser_infos'] = $superUserInfos;
- $this->redirectToNextStep( __FUNCTION__ );
- }
- $view->addForm($form);
-
- echo $view->render();
- }
-
- public function firstWebsiteSetup()
- {
-
- $view = new Piwik_Install_View(
- $this->pathView . 'firstWebsiteSetup.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $this->skipThisStep( __FUNCTION__ );
-
- require_once "FormFirstWebsiteSetup.php";
- $form = new Piwik_Installation_FormFirstWebsiteSetup;
-
- if( !isset($_SESSION['generalSetupSuccessMessage']))
- {
- $view->displayGeneralSetupSuccess = true;
- $_SESSION['generalSetupSuccessMessage'] = true;
- }
-
- if($form->validate())
- {
- // we setup the superuser login & password in the config that will be checked by the
- // API authentication process
- Zend_Registry::get('config')->superuser = $_SESSION['superuser_infos'];
-
- $name = urlencode($form->getSubmitValue('siteName'));
- $url = urlencode($form->getSubmitValue('url'));
-
- $this->initObjectsToCallAPI();
-
- require_once "API/Request.php";
- $request = new Piwik_API_Request("
- method=SitesManager.addSite
- &siteName=$name
- &urls=$url
- &format=original
- ");
-
- try {
- $result = $request->process();
- $_SESSION['site_idSite'] = $result;
- $_SESSION['site_name'] = $name;
- $_SESSION['site_url'] = $url;
-
- $this->redirectToNextStep( __FUNCTION__ );
- } catch(Exception $e) {
- $view->errorMessage = $e->getMessage();
- }
-
- }
- $view->addForm($form);
- echo $view->render();
+ + $_SESSION['currentStepDone'] = __FUNCTION__; + echo $view->render(); + } + + function generalSetup() + { + $view = new Piwik_Install_View( + $this->pathView . 'generalSetup.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->checkPreviousStepIsValid( __FUNCTION__ ); + $this->skipThisStep( __FUNCTION__ ); + + require_once "FormGeneralSetup.php"; + $form = new Piwik_Installation_FormGeneralSetup; + + if($form->validate()) + { + $superUserInfos = array( + 'login' => $form->getSubmitValue('login'), + 'password' => md5( $form->getSubmitValue('password') ), + 'email' => $form->getSubmitValue('email'), + ); + + $_SESSION['superuser_infos'] = $superUserInfos; + $this->redirectToNextStep( __FUNCTION__ ); + } + $view->addForm($form); + + echo $view->render(); } -
- public function displayJavascriptCode()
- {
- $view = new Piwik_Install_View(
- $this->pathView . 'displayJavascriptCode.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $this->skipThisStep( __FUNCTION__ );
-
- if( !isset($_SESSION['firstWebsiteSetupSuccessMessage']))
- {
- $view->displayfirstWebsiteSetupSuccess = true;
- $_SESSION['firstWebsiteSetupSuccessMessage'] = true;
- }
-
-
- $view->websiteName = urldecode($_SESSION['site_name']);
-
- $jsTag = Piwik::getJavascriptCode($_SESSION['site_idSite'], Piwik_Url::getCurrentUrlWithoutFileName());
-
- $view->javascriptTag = $jsTag;
- $view->showNextStep = true;
-
- $_SESSION['currentStepDone'] = __FUNCTION__;
- echo $view->render();
- }
-
- public function finished()
- {
- $view = new Piwik_Install_View(
- $this->pathView . 'finished.tpl',
- $this->getInstallationSteps(),
- __FUNCTION__
- );
- $this->checkPreviousStepIsValid( __FUNCTION__ );
- $this->skipThisStep( __FUNCTION__ );
- $this->writeConfigFileFromSession();
-
- $_SESSION['currentStepDone'] = __FUNCTION__;
- $view->showNextStep = false;
-
- setcookie(session_name(), session_id(), 1, '/');
- @session_destroy();
- echo $view->render();
- }
-
- protected function initObjectsToCallAPI()
- {
- // connect to the database using the DB infos currently in the session
- $this->createDbFromSessionInformation();
-
- // create the fake access to grant super user privilege
- Zend_Registry::set('access', new Piwik_FakeAccess_SetSuperUser);
-
- // we need to create the logs otherwise the API request throws an exception
- Piwik::createLogObject();
- }
-
- protected function writeConfigFileFromSession()
+ + public function firstWebsiteSetup() + { + + $view = new Piwik_Install_View( + $this->pathView . 'firstWebsiteSetup.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->checkPreviousStepIsValid( __FUNCTION__ ); + $this->skipThisStep( __FUNCTION__ ); + + require_once "FormFirstWebsiteSetup.php"; + $form = new Piwik_Installation_FormFirstWebsiteSetup; + + if( !isset($_SESSION['generalSetupSuccessMessage'])) + { + $view->displayGeneralSetupSuccess = true; + $_SESSION['generalSetupSuccessMessage'] = true; + } + + if($form->validate()) + { + // we setup the superuser login & password in the config that will be checked by the + // API authentication process + Zend_Registry::get('config')->superuser = $_SESSION['superuser_infos']; + + $name = urlencode($form->getSubmitValue('siteName')); + $url = urlencode($form->getSubmitValue('url')); + + $this->initObjectsToCallAPI(); + + require_once "API/Request.php"; + $request = new Piwik_API_Request(" + method=SitesManager.addSite + &siteName=$name + &urls=$url + &format=original + "); + + try { + $result = $request->process(); + $_SESSION['site_idSite'] = $result; + $_SESSION['site_name'] = $name; + $_SESSION['site_url'] = $url; + + $this->redirectToNextStep( __FUNCTION__ ); + } catch(Exception $e) { + $view->errorMessage = $e->getMessage(); + } + + } + $view->addForm($form); + echo $view->render(); + } + + public function displayJavascriptCode() + { + $view = new Piwik_Install_View( + $this->pathView . 'displayJavascriptCode.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->checkPreviousStepIsValid( __FUNCTION__ ); + $this->skipThisStep( __FUNCTION__ ); + + if( !isset($_SESSION['firstWebsiteSetupSuccessMessage'])) + { + $view->displayfirstWebsiteSetupSuccess = true; + $_SESSION['firstWebsiteSetupSuccessMessage'] = true; + } + + + $view->websiteName = urldecode($_SESSION['site_name']); + + $jsTag = Piwik::getJavascriptCode($_SESSION['site_idSite'], Piwik_Url::getCurrentUrlWithoutFileName()); + + $view->javascriptTag = $jsTag; + $view->showNextStep = true; + + $_SESSION['currentStepDone'] = __FUNCTION__; + echo $view->render(); + } + + public function finished() + { + $view = new Piwik_Install_View( + $this->pathView . 'finished.tpl', + $this->getInstallationSteps(), + __FUNCTION__ + ); + $this->checkPreviousStepIsValid( __FUNCTION__ ); + $this->skipThisStep( __FUNCTION__ ); + $this->writeConfigFileFromSession(); + + $_SESSION['currentStepDone'] = __FUNCTION__; + $view->showNextStep = false; + + setcookie(session_name(), session_id(), 1, '/'); + @session_destroy(); + echo $view->render(); + } + + protected function initObjectsToCallAPI() + { + // connect to the database using the DB infos currently in the session + $this->createDbFromSessionInformation(); + + // create the fake access to grant super user privilege + Zend_Registry::set('access', new Piwik_FakeAccess_SetSuperUser); + + // we need to create the logs otherwise the API request throws an exception + Piwik::createLogObject(); + } + + protected function writeConfigFileFromSession() { if(!isset($_SESSION['superuser_infos']) || !isset($_SESSION['db_infos'])) { return; - }
- $configFile = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n";
- $configFile .= "; file automatically generated during the piwik installation process\n";
-
- // super user information
- $configFile .= "[superuser]\n";
- foreach( $_SESSION['superuser_infos'] as $key => $value)
- {
- $configFile .= "$key = $value\n";
- }
- $configFile .= "\n";
-
- // database information
- $configFile .= "[database]\n";
- foreach($_SESSION['db_infos'] as $key => $value)
- {
- $configFile .= "$key = $value\n";
- }
-
- file_put_contents(Piwik_Config::getDefaultUserConfigPath(), $configFile);
- }
- /**
- * The previous step is valid if it is either
- * - any step before (OK to go back)
- * - the current step (case when validating a form)
- */
- function checkPreviousStepIsValid( $currentStep )
+ } + $configFile = "; <?php exit; ?> DO NOT REMOVE THIS LINE\n"; + $configFile .= "; file automatically generated during the piwik installation process\n"; + + // super user information + $configFile .= "[superuser]\n"; + foreach( $_SESSION['superuser_infos'] as $key => $value) + { + $configFile .= "$key = $value\n"; + } + $configFile .= "\n"; + + // database information + $configFile .= "[database]\n"; + foreach($_SESSION['db_infos'] as $key => $value) + { + $configFile .= "$key = $value\n"; + } + + file_put_contents(Piwik_Config::getDefaultUserConfigPath(), $configFile); + } + /** + * The previous step is valid if it is either + * - any step before (OK to go back) + * - the current step (case when validating a form) + */ + function checkPreviousStepIsValid( $currentStep ) { if(empty($_SESSION['currentStepDone'])) { return; - }
- // the currentStep
- $currentStepId = array_search($currentStep, $this->steps);
-
- // the step before
- $previousStepId = array_search($_SESSION['currentStepDone'], $this->steps);
+ } + // the currentStep + $currentStepId = array_search($currentStep, $this->steps); + + // the step before + $previousStepId = array_search($_SESSION['currentStepDone'], $this->steps); - // not OK if currentStepId > previous+1
- if( $currentStepId > $previousStepId + 1 )
- {
- $message = "Error: it seems you try to skip a step of the Installation process,
- or your cookies are disabled.
- <br /><b>Make sure your cookies are enabled</b> and go back
- <a href='".Piwik_Url::getCurrentUrlWithoutFileName()."'>
- to the first page of the installation</a>.";
- Piwik::exitWithErrorMessage( $message );
- }
- }
-
- protected function redirectToNextStep($currentStep)
- {
- $_SESSION['currentStepDone'] = $currentStep;
- $nextStep = $this->steps[1 + array_search($currentStep, $this->steps)];
- Piwik::redirectToModule('Installation' , $nextStep);
- }
-
- protected function createDbFromSessionInformation()
- {
- $dbInfos = $_SESSION['db_infos'];
-
- Zend_Registry::get('config')->database = $dbInfos;
- Piwik::createDatabaseObject($dbInfos);
- }
-
- protected function getSystemInformation()
- {
- $minimumPhpVersion = Zend_Registry::get('config')->General->minimum_php_version;
- $minimumMemoryLimit = Zend_Registry::get('config')->General->minimum_memory_limit;
-
- $infos = array();
-
- $infos['directories'] = Piwik::checkDirectoriesWritable();
- $infos['phpVersion_minimum'] = $minimumPhpVersion;
- $infos['phpVersion'] = phpversion();
- $infos['phpVersion_ok'] = version_compare( $minimumPhpVersion, $infos['phpVersion']) === -1;
-
- $extensions = @get_loaded_extensions();
-
- $infos['pdo_ok'] = false;
- if (in_array('PDO', $extensions))
- {
- $infos['pdo_ok'] = true;
- }
-
- $infos['pdo_mysql_ok'] = false;
- if (in_array('pdo_mysql', $extensions))
- {
- $infos['pdo_mysql_ok'] = true;
- }
- - $infos['gd_ok'] = false;
- if (in_array('gd', $extensions))
- {
- $gdInfo = gd_info();
- $infos['gd_version'] = $gdInfo['GD Version'];
- ereg ("([0-9]{1})", $gdInfo['GD Version'], $gdVersion);
- if($gdVersion[0] >= 2)
- {
- $infos['gd_ok'] = true;
- }
- }
-
- $infos['serverVersion'] = addslashes($_SERVER['SERVER_SOFTWARE']);
- $infos['serverOs'] = @php_uname();
- $infos['serverTime'] = date('H:i:s');
-
- $infos['setTimeLimit_ok'] = false;
- if(function_exists( 'set_time_limit'))
- {
- $infos['setTimeLimit_ok'] = true;
- }
-
- $infos['mail_ok'] = false;
- if(function_exists('mail'))
- {
- $infos['mail_ok'] = true;
- }
-
- $infos['registerGlobals_ok'] = ini_get('register_globals') == 0;
- $infos['memoryMinimum'] = $minimumMemoryLimit;
-
- $infos['memory_ok'] = true;
- // on windows the ini_get is not working?
- $infos['memoryCurrent'] = '?M';
-
- $raised = Piwik::raiseMemoryLimitIfNecessary();
- if( $memoryValue = Piwik::getMemoryLimitValue() )
- {
- $infos['memoryCurrent'] = $memoryValue."M";
- $infos['memory_ok'] = $memoryValue >= $minimumMemoryLimit;
- }
-
- return $infos;
+ // not OK if currentStepId > previous+1 + if( $currentStepId > $previousStepId + 1 ) + { + $message = "Error: it seems you try to skip a step of the Installation process, + or your cookies are disabled. + <br /><b>Make sure your cookies are enabled</b> and go back + <a href='".Piwik_Url::getCurrentUrlWithoutFileName()."'> + to the first page of the installation</a>."; + Piwik::exitWithErrorMessage( $message ); + } + } + + protected function redirectToNextStep($currentStep) + { + $_SESSION['currentStepDone'] = $currentStep; + $nextStep = $this->steps[1 + array_search($currentStep, $this->steps)]; + Piwik::redirectToModule('Installation' , $nextStep); + } + + protected function createDbFromSessionInformation() + { + $dbInfos = $_SESSION['db_infos']; + + Zend_Registry::get('config')->database = $dbInfos; + Piwik::createDatabaseObject($dbInfos); + } + + protected function getSystemInformation() + { + $minimumPhpVersion = Zend_Registry::get('config')->General->minimum_php_version; + $minimumMemoryLimit = Zend_Registry::get('config')->General->minimum_memory_limit; + + $infos = array(); + + $infos['directories'] = Piwik::checkDirectoriesWritable(); + $infos['phpVersion_minimum'] = $minimumPhpVersion; + $infos['phpVersion'] = phpversion(); + $infos['phpVersion_ok'] = version_compare( $minimumPhpVersion, $infos['phpVersion']) === -1; + + $extensions = @get_loaded_extensions(); + + $infos['pdo_ok'] = false; + if (in_array('PDO', $extensions)) + { + $infos['pdo_ok'] = true; + } + + $infos['pdo_mysql_ok'] = false; + if (in_array('pdo_mysql', $extensions)) + { + $infos['pdo_mysql_ok'] = true; + } + + $infos['gd_ok'] = false; + if (in_array('gd', $extensions)) + { + $gdInfo = gd_info(); + $infos['gd_version'] = $gdInfo['GD Version']; + ereg ("([0-9]{1})", $gdInfo['GD Version'], $gdVersion); + if($gdVersion[0] >= 2) + { + $infos['gd_ok'] = true; + } + } + + $infos['serverVersion'] = addslashes($_SERVER['SERVER_SOFTWARE']); + $infos['serverOs'] = @php_uname(); + $infos['serverTime'] = date('H:i:s'); + + $infos['setTimeLimit_ok'] = false; + if(function_exists( 'set_time_limit')) + { + $infos['setTimeLimit_ok'] = true; + } + + $infos['mail_ok'] = false; + if(function_exists('mail')) + { + $infos['mail_ok'] = true; + } + + $infos['registerGlobals_ok'] = ini_get('register_globals') == 0; + $infos['memoryMinimum'] = $minimumMemoryLimit; + + $infos['memory_ok'] = true; + // on windows the ini_get is not working? + $infos['memoryCurrent'] = '?M'; + + $raised = Piwik::raiseMemoryLimitIfNecessary(); + if( $memoryValue = Piwik::getMemoryLimitValue() ) + { + $infos['memoryCurrent'] = $memoryValue."M"; + $infos['memory_ok'] = $memoryValue >= $minimumMemoryLimit; + } + + return $infos; } @@ -516,18 +516,18 @@ class Piwik_Installation_Controller extends Piwik_Controller { $this->redirectToNextStep($step); } - }
-}
-
-
-/**
- *
- * @package Piwik_Installation
- */
-class Piwik_FakeAccess_SetSuperUser {
- function checkUserIsSuperUser()
- {
- return true;
- }
- function loadAccess() {}
-}
+ } +} + + +/** + * + * @package Piwik_Installation + */ +class Piwik_FakeAccess_SetSuperUser { + function checkUserIsSuperUser() + { + return true; + } + function loadAccess() {} +} diff --git a/plugins/Provider/API.php b/plugins/Provider/API.php index eafea2151e..074ca64994 100644 --- a/plugins/Provider/API.php +++ b/plugins/Provider/API.php @@ -38,18 +38,5 @@ class Piwik_Provider_API $dataTable->queueFilter('Piwik_DataTable_Filter_ReplaceColumnNames'); return $dataTable; } - - /** - * Example of getting a RAW BLOB - * - * @return blob - */ - public function getProviderBlob( $idSite, $period, $date ) - { - Piwik::checkUserHasViewAccess( $idSite ); - $archive = Piwik_Archive::build($idSite, $period, $date ); - $dataTable = $archive->getBlob('Provider_hostnameExt'); - return $dataTable; - } } diff --git a/plugins/Provider/Controller.php b/plugins/Provider/Controller.php index cc08f0b3e9..c585fd6e88 100644 --- a/plugins/Provider/Controller.php +++ b/plugins/Provider/Controller.php @@ -1,6 +1,4 @@ <?php -require_once "ViewDataTable.php"; - class Piwik_Provider_Controller extends Piwik_Controller { /** @@ -9,7 +7,7 @@ class Piwik_Provider_Controller extends Piwik_Controller function getProvider($fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( 'Provider', __FUNCTION__, "Provider.getProvider" ); + $view->init( $this->pluginName, __FUNCTION__, "Provider.getProvider" ); $view->setColumnsToDisplay( array('label','nb_uniq_visitors') ); $view->setSortedColumn( 1 ); diff --git a/plugins/Provider/Provider.php b/plugins/Provider/Provider.php index a819797c31..0de4d5b8f8 100644 --- a/plugins/Provider/Provider.php +++ b/plugins/Provider/Provider.php @@ -97,7 +97,8 @@ class Piwik_Provider extends Piwik_Plugin $recordName = 'Provider_hostnameExt'; $labelSQL = "location_provider"; - $tableProvider = $archiveProcessing->getDataTableInterestForLabel($labelSQL); + $interestByProvider = $archiveProcessing->getArrayInterestForLabel($labelSQL); + $tableProvider = $archiveProcessing->getDataTableFromArray($interestByProvider); $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableProvider->getSerialized()); } diff --git a/plugins/Referers/API.php b/plugins/Referers/API.php index 5df0dfe834..ab60cc8c83 100644 --- a/plugins/Referers/API.php +++ b/plugins/Referers/API.php @@ -56,10 +56,10 @@ class Piwik_Referers_API $dataTable->queueFilter('Piwik_DataTable_Filter_ColumnCallbackReplace', array('label', 'Piwik_getRefererTypeLabel')); return $dataTable; } - + function getKeywords($idSite, $period, $date, $expanded = false) { - $dataTable = $this->getDataTable('Referers_searchEngineByKeyword',$idSite, $period, $date, $expanded); + $dataTable = $this->getDataTable('Referers_searchEngineByKeyword', $idSite, $period, $date, $expanded); return $dataTable; } diff --git a/plugins/Referers/Controller.php b/plugins/Referers/Controller.php index c76d596b19..14bb8e7185 100644 --- a/plugins/Referers/Controller.php +++ b/plugins/Referers/Controller.php @@ -1,10 +1,9 @@ <?php -require_once "ViewDataTable.php"; class Piwik_Referers_Controller extends Piwik_Controller { function index() { - $view = new Piwik_View('Referers/index.tpl'); + $view = new Piwik_View('Referers/templates/index.tpl'); $view->graphEvolutionReferers = $this->getLastDirectEntryGraph(true); $view->nameGraphEvolutionReferers = 'ReferersgetLastDirectEntryGraph'; // must be the function name used above @@ -40,19 +39,17 @@ class Piwik_Referers_Controller extends Piwik_Controller function getSearchEnginesAndKeywords() { - $view = new Piwik_View('Referers/searchEngines_Keywords.tpl'); + $view = new Piwik_View('Referers/templates/searchEngines_Keywords.tpl'); $view->searchEngines = $this->getSearchEngines(true) ; $view->keywords = $this->getKeywords(true); echo $view->render(); } - /** - * Referers - */ + function getRefererType( $fetch = false) { $view = Piwik_ViewDataTable::factory('cloud'); $view->init( $this->pluginName, - 'getRefererType', + __FUNCTION__, 'Referers.getRefererType' ); $view->disableSearchBox(); @@ -68,21 +65,21 @@ class Piwik_Referers_Controller extends Piwik_Controller function getKeywords( $fetch = false) { $view = Piwik_ViewDataTable::factory(); - - $view->init( $this->pluginName, 'getKeywords', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getKeywords', 'getSearchEnginesFromKeywordId' ); $view->disableExcludeLowPopulation(); $view->setColumnsToDisplay( array('label','nb_visits') ); - + $view->enableShowGoals(); + $view->disableSubTableWhenShowGoals(); return $this->renderView($view, $fetch); } function getSearchEnginesFromKeywordId( $fetch = false ) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, 'getSearchEnginesFromKeywordId', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getSearchEnginesFromKeywordId' ); $view->disableSearchBox(); @@ -96,34 +93,36 @@ class Piwik_Referers_Controller extends Piwik_Controller function getSearchEngines( $fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, 'getSearchEngines', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getSearchEngines', 'getKeywordsFromSearchEngineId' ); $view->disableSearchBox(); $view->disableExcludeLowPopulation(); + $view->enableShowGoals(); + $view->disableSubTableWhenShowGoals(); $view->setColumnsToDisplay( array('label','nb_visits') ); return $this->renderView($view, $fetch); } - public function getSearchEnginesEvolution($fetch = false)
- {
- $view = Piwik_ViewDataTable::factory('graphEvolution');
- $view->init( 'Referers', __FUNCTION__, 'Referers.getSearchEngines' );
-
- $view->setColumnsToDisplay( 'nb_uniq_visitors' );
- $view->setExactPattern( array('Google','Yahoo!'), 'label');
- //$view->setExactPattern( array('Google'), 'label');
-
- return $this->renderView($view, $fetch);
- }
+ public function getSearchEnginesEvolution($fetch = false) + { + $view = Piwik_ViewDataTable::factory('graphEvolution'); + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getSearchEngines' ); + + $view->setColumnsToDisplay( 'nb_uniq_visitors' ); + $view->setExactPattern( array('Google','Yahoo!'), 'label'); + //$view->setExactPattern( array('Google'), 'label'); + + return $this->renderView($view, $fetch); + } function getKeywordsFromSearchEngineId( $fetch = false ) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, 'getKeywordsFromSearchEngineId', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getKeywordsFromSearchEngineId' ); $view->disableSearchBox(); @@ -136,20 +135,23 @@ class Piwik_Referers_Controller extends Piwik_Controller function getWebsites( $fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, 'getWebsites', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getWebsites', 'getUrlsFromWebsiteId' ); $view->disableExcludeLowPopulation(); $view->setColumnsToDisplay( array('label','nb_visits') ); $view->setLimit(10); + $view->enableShowGoals(); + $view->disableSubTableWhenShowGoals(); + return $this->renderView($view, $fetch); } function getCampaigns( $fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, 'getCampaigns', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getCampaigns', 'getKeywordsFromCampaignId' ); @@ -157,16 +159,15 @@ class Piwik_Referers_Controller extends Piwik_Controller $view->disableSearchBox(); $view->disableExcludeLowPopulation(); $view->setLimit( 5 ); - + $view->enableShowGoals(); $view->setColumnsToDisplay( array('label','nb_visits') ); - return $this->renderView($view, $fetch); } function getKeywordsFromCampaignId( $fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, 'getKeywordsFromCampaignId', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getKeywordsFromCampaignId' ); @@ -180,7 +181,7 @@ class Piwik_Referers_Controller extends Piwik_Controller function getUrlsFromWebsiteId( $fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, 'getUrlsFromWebsiteId', + $view->init( $this->pluginName, __FUNCTION__, 'Referers.getUrlsFromWebsiteId' ); $view->disableSearchBox(); @@ -198,6 +199,7 @@ class Piwik_Referers_Controller extends Piwik_Controller &format=original &disable_queued_filters=1"; $request = new Piwik_API_Request($requestString); + $view->enableShowGoals(); return $request->process(); } diff --git a/plugins/Referers/Referers.php b/plugins/Referers/Referers.php index fe23422512..339a75f73f 100644 --- a/plugins/Referers/Referers.php +++ b/plugins/Referers/Referers.php @@ -122,10 +122,22 @@ class Piwik_Referers extends Piwik_Plugin ); } } - + + public function archiveDay( $notification ) { + /** + * @var Piwik_ArchiveProcessing_Day + */ $archiveProcessing = $notification->getNotificationObject(); + + $this->archiveDayAggregateVisits($archiveProcessing); + $this->archiveDayAggregateGoals($archiveProcessing); + $this->archiveDayRecordInDatabase($archiveProcessing); + } + + protected function archiveDayAggregateVisits($archiveProcessing) + { $query = "SELECT referer_type, referer_name, referer_keyword, @@ -135,7 +147,8 @@ class Piwik_Referers extends Piwik_Plugin sum(visit_total_actions) as nb_actions, max(visit_total_actions) as max_actions, sum(visit_total_time) as sum_visit_length, - sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count + sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, + sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted FROM ".$archiveProcessing->logTable." WHERE visit_server_date = ? AND idsite = ? @@ -143,111 +156,164 @@ class Piwik_Referers extends Piwik_Plugin ORDER BY nb_visits DESC"; $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); - $interestBySearchEngine = - $interestByKeyword = - $keywordBySearchEngine = - $searchEngineByKeyword = - $interestByWebsite[Piwik_Common::REFERER_TYPE_WEBSITE] = - $urlByWebsite[Piwik_Common::REFERER_TYPE_WEBSITE] = - $keywordByCampaign = - $interestByCampaign = - $interestByType = - $distinctUrls[Piwik_Common::REFERER_TYPE_WEBSITE] = array(); + $this->interestBySearchEngine = + $this->interestByKeyword = + $this->interestBySearchEngineAndKeyword = + $this->interestByKeywordAndSearchEngine = + $this->interestByWebsite = + $this->interestByWebsiteAndUrl = + $this->interestByCampaignAndKeyword = + $this->interestByCampaign = + $this->interestByType = + $this->distinctUrls = array(); - while($rowBefore = $query->fetch() ) + while($row = $query->fetch() ) { - $row = array( - Piwik_Archive::INDEX_NB_UNIQ_VISITORS => $rowBefore['nb_uniq_visitors'], - Piwik_Archive::INDEX_NB_VISITS => $rowBefore['nb_visits'], - Piwik_Archive::INDEX_NB_ACTIONS => $rowBefore['nb_actions'], - Piwik_Archive::INDEX_MAX_ACTIONS => $rowBefore['max_actions'], - Piwik_Archive::INDEX_SUM_VISIT_LENGTH => $rowBefore['sum_visit_length'], - Piwik_Archive::INDEX_BOUNCE_COUNT => $rowBefore['bounce_count'], - 'referer_type' => $rowBefore['referer_type'], - 'referer_name' => $rowBefore['referer_name'], - 'referer_keyword' => $rowBefore['referer_keyword'], - 'referer_url' => $rowBefore['referer_url'], - ); - - switch($row['referer_type']) + if(empty($row['referer_type'])) { - case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE: - - if(!isset($interestBySearchEngine[$row['referer_name']])) $interestBySearchEngine[$row['referer_name']]= $archiveProcessing->getNewInterestRow(); - if(!isset($interestByKeyword[$row['referer_keyword']])) $interestByKeyword[$row['referer_keyword']]= $archiveProcessing->getNewInterestRow(); - if(!isset($keywordBySearchEngine[$row['referer_name']][$row['referer_keyword']])) $keywordBySearchEngine[$row['referer_name']][$row['referer_keyword']]= $archiveProcessing->getNewInterestRow(); - if(!isset($searchEngineByKeyword[$row['referer_keyword']][$row['referer_name']])) $searchEngineByKeyword[$row['referer_keyword']][$row['referer_name']]= $archiveProcessing->getNewInterestRow(); - - $archiveProcessing->updateInterestStats( $row, $interestBySearchEngine[$row['referer_name']]); - $archiveProcessing->updateInterestStats( $row, $interestByKeyword[$row['referer_keyword']]); - $archiveProcessing->updateInterestStats( $row, $keywordBySearchEngine[$row['referer_name']][$row['referer_keyword']]); - $archiveProcessing->updateInterestStats( $row, $searchEngineByKeyword[$row['referer_keyword']][$row['referer_name']]); - break; - - case Piwik_Common::REFERER_TYPE_WEBSITE: + $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY; + } + else + { + switch($row['referer_type']) + { + case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE: - if(!isset($interestByWebsite[$row['referer_type']][$row['referer_name']])) $interestByWebsite[$row['referer_type']][$row['referer_name']]= $archiveProcessing->getNewInterestRow(); - $archiveProcessing->updateInterestStats( $row, $interestByWebsite[$row['referer_type']][$row['referer_name']]); + if(!isset($this->interestBySearchEngine[$row['referer_name']])) $this->interestBySearchEngine[$row['referer_name']]= $archiveProcessing->getNewInterestRow(); + if(!isset($this->interestByKeyword[$row['referer_keyword']])) $this->interestByKeyword[$row['referer_keyword']]= $archiveProcessing->getNewInterestRow(); + if(!isset($this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']]= $archiveProcessing->getNewInterestRow(); + if(!isset($this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']])) $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']]= $archiveProcessing->getNewInterestRow(); - if(!isset($urlByWebsite[$row['referer_type']][$row['referer_name']][$row['referer_url']])) $urlByWebsite[$row['referer_type']][$row['referer_name']][$row['referer_url']]= $archiveProcessing->getNewInterestRow(); - $archiveProcessing->updateInterestStats( $row, $urlByWebsite[$row['referer_type']][$row['referer_name']][$row['referer_url']]); - - if(!isset($distinctUrls[$row['referer_type']][$row['referer_url']])) - { - $distinctUrls[$row['referer_type']][$row['referer_url']] = true; - } + $archiveProcessing->updateInterestStats( $row, $this->interestBySearchEngine[$row['referer_name']]); + $archiveProcessing->updateInterestStats( $row, $this->interestByKeyword[$row['referer_keyword']]); + $archiveProcessing->updateInterestStats( $row, $this->interestBySearchEngineAndKeyword[$row['referer_name']][$row['referer_keyword']]); + $archiveProcessing->updateInterestStats( $row, $this->interestByKeywordAndSearchEngine[$row['referer_keyword']][$row['referer_name']]); + break; + + case Piwik_Common::REFERER_TYPE_WEBSITE: + + if(!isset($this->interestByWebsite[$row['referer_name']])) $this->interestByWebsite[$row['referer_name']]= $archiveProcessing->getNewInterestRow(); + $archiveProcessing->updateInterestStats( $row, $this->interestByWebsite[$row['referer_name']]); + + if(!isset($this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']])) $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']]= $archiveProcessing->getNewInterestRow(); + $archiveProcessing->updateInterestStats( $row, $this->interestByWebsiteAndUrl[$row['referer_name']][$row['referer_url']]); - break; + if(!isset($this->distinctUrls[$row['referer_url']])) + { + $this->distinctUrls[$row['referer_url']] = true; + } + + break; + + case Piwik_Common::REFERER_TYPE_CAMPAIGN: + if(!empty($row['referer_keyword'])) + { + if(!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']]= $archiveProcessing->getNewInterestRow(); + $archiveProcessing->updateInterestStats( $row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']]); + } + if(!isset($this->interestByCampaign[$row['referer_name']])) $this->interestByCampaign[$row['referer_name']]= $archiveProcessing->getNewInterestRow(); + $archiveProcessing->updateInterestStats( $row, $this->interestByCampaign[$row['referer_name']]); + break; + + case Piwik_Common::REFERER_TYPE_DIRECT_ENTRY: + // direct entry are aggregated below in $this->interestByType array + break; + + default: + throw new Exception("Non expected referer_type = " . $row['referer_type']); + break; + } + } + if(!isset($this->interestByType[$row['referer_type']] )) $this->interestByType[$row['referer_type']] = $archiveProcessing->getNewInterestRow(); + $archiveProcessing->updateInterestStats($row, $this->interestByType[$row['referer_type']]); + } + } + + protected function archiveDayAggregateGoals($archiveProcessing) + { + $query = $archiveProcessing->queryConversionsBySegment("referer_type,referer_name,referer_keyword"); + while($row = $query->fetch() ) + { + if(empty($row['referer_type'])) + { + $row['referer_type'] = Piwik_Common::REFERER_TYPE_DIRECT_ENTRY; + } + else + { + switch($row['referer_type']) + { + case Piwik_Common::REFERER_TYPE_SEARCH_ENGINE: + if(!isset($this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + if(!isset($this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + + $archiveProcessing->updateGoalStats( $row, $this->interestBySearchEngine[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); + $archiveProcessing->updateGoalStats( $row, $this->interestByKeyword[$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); + break; + + case Piwik_Common::REFERER_TYPE_WEBSITE: + if(!isset($this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + $archiveProcessing->updateGoalStats( $row, $this->interestByWebsite[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); + break; + + case Piwik_Common::REFERER_TYPE_CAMPAIGN: + if(!empty($row['referer_keyword'])) + { + if(!isset($this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + $archiveProcessing->updateGoalStats( $row, $this->interestByCampaignAndKeyword[$row['referer_name']][$row['referer_keyword']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); + } + if(!isset($this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + $archiveProcessing->updateGoalStats( $row, $this->interestByCampaign[$row['referer_name']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); + break; - case Piwik_Common::REFERER_TYPE_CAMPAIGN: - if(!empty($row['referer_keyword'])) - { - if(!isset($keywordByCampaign[$row['referer_name']][$row['referer_keyword']])) $keywordByCampaign[$row['referer_name']][$row['referer_keyword']]= $archiveProcessing->getNewInterestRow(); - $archiveProcessing->updateInterestStats( $row, $keywordByCampaign[$row['referer_name']][$row['referer_keyword']]); - } - if(!isset($interestByCampaign[$row['referer_name']])) $interestByCampaign[$row['referer_name']]= $archiveProcessing->getNewInterestRow(); - $archiveProcessing->updateInterestStats( $row, $interestByCampaign[$row['referer_name']]); - break; + default: + throw new Exception("Non expected referer_type = " . $row['referer_type']); + break; + } } - if(!isset($interestByType[$row['referer_type']] )) $interestByType[$row['referer_type']] = $archiveProcessing->getNewInterestRow(); - $archiveProcessing->updateInterestStats($row, $interestByType[$row['referer_type']]); + if(!isset($this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] )) $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + $archiveProcessing->updateGoalStats($row, $this->interestByType[$row['referer_type']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); } - - $numberOfDistinctSearchEngines = count($keywordBySearchEngine); - $numberOfDistinctKeywords = count($searchEngineByKeyword); - - $numberOfDistinctCampaigns = count($interestByCampaign); - $numberOfDistinctWebsites = count($interestByWebsite[Piwik_Common::REFERER_TYPE_WEBSITE]); - $numberOfDistinctWebsitesUrls = count($distinctUrls[Piwik_Common::REFERER_TYPE_WEBSITE]); - + + $archiveProcessing->enrichConversionsByLabelArray($this->interestByType); + $archiveProcessing->enrichConversionsByLabelArray($this->interestBySearchEngine); + $archiveProcessing->enrichConversionsByLabelArray($this->interestByKeyword); + $archiveProcessing->enrichConversionsByLabelArray($this->interestByWebsite); + $archiveProcessing->enrichConversionsByLabelArray($this->interestByCampaign); + $archiveProcessing->enrichConversionsByLabelArrayHasTwoLevels($this->interestByCampaignAndKeyword); + } + + protected function archiveDayRecordInDatabase($archiveProcessing) + { $numericRecords = array( - 'Referers_distinctSearchEngines' => $numberOfDistinctSearchEngines, - 'Referers_distinctKeywords' => $numberOfDistinctKeywords, - 'Referers_distinctCampaigns' => $numberOfDistinctCampaigns, - 'Referers_distinctWebsites' => $numberOfDistinctWebsites, - 'Referers_distinctWebsitesUrls' => $numberOfDistinctWebsitesUrls, + 'Referers_distinctSearchEngines' => count($this->interestBySearchEngineAndKeyword), + 'Referers_distinctKeywords' => count($this->interestByKeywordAndSearchEngine), + 'Referers_distinctCampaigns' => count($this->interestByCampaign), + 'Referers_distinctWebsites' => count($this->interestByWebsite), + 'Referers_distinctWebsitesUrls' => count($this->distinctUrls), ); + foreach($numericRecords as $name => $value) { $record = new Piwik_ArchiveProcessing_Record_Numeric($name, $value); } - $data = $archiveProcessing->getDataTableSerialized($interestByType); + $data = $archiveProcessing->getDataTableSerialized($this->interestByType); $record = new Piwik_ArchiveProcessing_Record_BlobArray('Referers_type', $data); $maximumRowsInDataTableLevelZero = 500; $maximumRowsInSubDataTable = 50; - $data = $archiveProcessing->getDataTablesSerialized($keywordBySearchEngine, $interestBySearchEngine, $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Referers_keywordBySearchEngine', $data); - - $data = $archiveProcessing->getDataTablesSerialized($searchEngineByKeyword, $interestByKeyword, $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Referers_searchEngineByKeyword', $data); - - $data = $archiveProcessing->getDataTablesSerialized($keywordByCampaign, $interestByCampaign, $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Referers_keywordByCampaign', $data); + $blobRecords = array( + 'Referers_keywordBySearchEngine' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestBySearchEngineAndKeyword, $this->interestBySearchEngine), + 'Referers_searchEngineByKeyword' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByKeywordAndSearchEngine, $this->interestByKeyword), + 'Referers_keywordByCampaign' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByCampaignAndKeyword, $this->interestByCampaign), + 'Referers_urlByWebsite' => $archiveProcessing->getDataTableWithSubtablesFromArraysIndexedByLabel($this->interestByWebsiteAndUrl, $this->interestByWebsite), + ); - $data = $archiveProcessing->getDataTablesSerialized($urlByWebsite[Piwik_Common::REFERER_TYPE_WEBSITE], $interestByWebsite[Piwik_Common::REFERER_TYPE_WEBSITE], $maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); - $record = new Piwik_ArchiveProcessing_Record_BlobArray('Referers_urlByWebsite', $data); + foreach($blobRecords as $recordName => $table ) + { + $dataToRecord = $table->getSerialized($maximumRowsInDataTableLevelZero, $maximumRowsInSubDataTable); + $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $dataToRecord); + } } } diff --git a/plugins/Referers/functions.php b/plugins/Referers/functions.php index 9df808e0b1..735e7865e1 100644 --- a/plugins/Referers/functions.php +++ b/plugins/Referers/functions.php @@ -63,4 +63,5 @@ function Piwik_getRefererTypeLabel($label) break; } return Piwik_Translate($indexTranslation); -}
\ No newline at end of file +} + diff --git a/plugins/Referers/index.tpl b/plugins/Referers/templates/index.tpl index df1995c81e..df1995c81e 100644 --- a/plugins/Referers/index.tpl +++ b/plugins/Referers/templates/index.tpl diff --git a/plugins/Referers/searchEngines_Keywords.tpl b/plugins/Referers/templates/searchEngines_Keywords.tpl index a664aefb50..a664aefb50 100644 --- a/plugins/Referers/searchEngines_Keywords.tpl +++ b/plugins/Referers/templates/searchEngines_Keywords.tpl diff --git a/plugins/UserCountry/Controller.php b/plugins/UserCountry/Controller.php index 29e7f56b07..a2395620f8 100644 --- a/plugins/UserCountry/Controller.php +++ b/plugins/UserCountry/Controller.php @@ -19,39 +19,41 @@ class Piwik_UserCountry_Controller extends Piwik_Controller function getCountry( $fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( 'UserCountry', __FUNCTION__, "UserCountry.getCountry" ); + $view->init( $this->pluginName, __FUNCTION__, "UserCountry.getCountry" ); $view->disableExcludeLowPopulation(); $view->setColumnsToDisplay( array('label','nb_uniq_visitors') ); $view->setSortedColumn( 1 ); $view->disableSearchBox(); $view->setLimit( 5 ); + $view->enableShowGoals(); return $this->renderView($view, $fetch); } - function getNumberOfDistinctCountries( $fetch = false) - { - return $this->getNumericValue('UserCountry.getNumberOfDistinctCountries'); - } - - function getLastDistinctCountriesGraph( $fetch = false ) - { - $view = $this->getLastUnitGraph('UserCountry',__FUNCTION__, "UserCountry.getNumberOfDistinctCountries"); - return $this->renderView($view, $fetch); - } - function getContinent( $fetch = false) { $view = Piwik_ViewDataTable::factory( 'graphVerticalBar' ); - $view->init( 'UserCountry', __FUNCTION__, "UserCountry.getContinent" ); + $view->init( $this->pluginName, __FUNCTION__, "UserCountry.getContinent" ); $view->disableExcludeLowPopulation(); $view->disableSearchBox(); $view->disableOffsetInformation(); $view->disableSort(); $view->setColumnsToDisplay( array('label','nb_uniq_visitors') ); $view->setSortedColumn( 1 ); + $view->enableShowGoals(); return $this->renderView($view, $fetch); } + + function getNumberOfDistinctCountries( $fetch = false) + { + return $this->getNumericValue('UserCountry.getNumberOfDistinctCountries'); + } + + function getLastDistinctCountriesGraph( $fetch = false ) + { + $view = $this->getLastUnitGraph('UserCountry',__FUNCTION__, "UserCountry.getNumberOfDistinctCountries"); + return $this->renderView($view, $fetch); + } } diff --git a/plugins/UserCountry/UserCountry.php b/plugins/UserCountry/UserCountry.php index bd69f64067..93a052ca1e 100644 --- a/plugins/UserCountry/UserCountry.php +++ b/plugins/UserCountry/UserCountry.php @@ -68,17 +68,42 @@ class Piwik_UserCountry extends Piwik_Plugin function archiveDay($notification) { $archiveProcessing = $notification->getNotificationObject(); - - $recordName = 'UserCountry_country'; + $this->archiveDayAggregateVisits($archiveProcessing); + $this->archiveDayAggregateGoals($archiveProcessing); + $this->archiveDayRecordInDatabase($archiveProcessing); + } + + protected function archiveDayAggregateVisits($archiveProcessing) + { $labelSQL = "location_country"; - $tableCountry = $archiveProcessing->getDataTableInterestForLabel($labelSQL); - $record = new Piwik_ArchiveProcessing_Record_Numeric('UserCountry_distinctCountries', $tableCountry->getRowsCount()); - $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableCountry->getSerialized()); - - $recordName = 'UserCountry_continent'; + $this->interestByCountry = $archiveProcessing->getArrayInterestForLabel($labelSQL); + $labelSQL = "location_continent"; - $tableContinent = $archiveProcessing->getDataTableInterestForLabel($labelSQL); - $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableContinent->getSerialized()); + $this->interestByContinent = $archiveProcessing->getArrayInterestForLabel($labelSQL); + } + + protected function archiveDayAggregateGoals($archiveProcessing) + { + $query = $archiveProcessing->queryConversionsBySegment("location_continent,location_country"); + while($row = $query->fetch() ) + { + if(!isset($this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + if(!isset($this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']])) $this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getNewGoalRow(); + $archiveProcessing->updateGoalStats($row, $this->interestByCountry[$row['location_country']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); + $archiveProcessing->updateGoalStats($row, $this->interestByContinent[$row['location_continent']][Piwik_Archive::INDEX_GOALS][$row['idgoal']]); + } + $archiveProcessing->enrichConversionsByLabelArray($this->interestByCountry); + $archiveProcessing->enrichConversionsByLabelArray($this->interestByContinent); + } + + protected function archiveDayRecordInDatabase($archiveProcessing) + { + $tableCountry = $archiveProcessing->getDataTableFromArray($this->interestByCountry); + $record = new Piwik_ArchiveProcessing_Record_BlobArray('UserCountry_country', $tableCountry->getSerialized()); + $record = new Piwik_ArchiveProcessing_Record_Numeric('UserCountry_distinctCountries', $tableCountry->getRowsCount()); + + $tableContinent = $archiveProcessing->getDataTableFromArray($this->interestByContinent); + $record = new Piwik_ArchiveProcessing_Record_BlobArray('UserCountry_continent', $tableContinent->getSerialized()); } } diff --git a/plugins/UserSettings/UserSettings.php b/plugins/UserSettings/UserSettings.php index 1b64be1415..b56ed2d8b2 100644 --- a/plugins/UserSettings/UserSettings.php +++ b/plugins/UserSettings/UserSettings.php @@ -78,17 +78,20 @@ class Piwik_UserSettings extends Piwik_Plugin $recordName = 'UserSettings_configuration'; $labelSQL = "CONCAT(config_os, ';', config_browser_name, ';', config_resolution)"; - $tableConfiguration = $archiveProcessing->getDataTableInterestForLabel($labelSQL); + $interestByConfiguration = $archiveProcessing->getArrayInterestForLabel($labelSQL); + $tableConfiguration = $archiveProcessing->getDataTableFromArray($interestByConfiguration); $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableConfiguration->getSerialized()); $recordName = 'UserSettings_os'; $labelSQL = "config_os"; - $tableOs = $archiveProcessing->getDataTableInterestForLabel($labelSQL); + $interestByOs = $archiveProcessing->getArrayInterestForLabel($labelSQL); + $tableOs = $archiveProcessing->getDataTableFromArray($interestByOs); $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableOs->getSerialized()); $recordName = 'UserSettings_browser'; $labelSQL = "CONCAT(config_browser_name, ';', config_browser_version)"; - $tableBrowser = $archiveProcessing->getDataTableInterestForLabel($labelSQL); + $interestByBrowser = $archiveProcessing->getArrayInterestForLabel($labelSQL); + $tableBrowser = $archiveProcessing->getDataTableFromArray($interestByBrowser); $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableBrowser->getSerialized()); $recordName = 'UserSettings_browserType'; @@ -97,7 +100,8 @@ class Piwik_UserSettings extends Piwik_Plugin $recordName = 'UserSettings_resolution'; $labelSQL = "config_resolution"; - $tableResolution = $archiveProcessing->getDataTableInterestForLabel($labelSQL); + $interestByResolution = $archiveProcessing->getArrayInterestForLabel($labelSQL); + $tableResolution = $archiveProcessing->getDataTableFromArray($interestByResolution); $filter = new Piwik_DataTable_Filter_ColumnCallbackDeleteRow($tableResolution, 'label', 'Piwik_UserSettings_keepStrlenGreater'); $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableResolution->getSerialized()); diff --git a/plugins/VisitFrequency/API.php b/plugins/VisitFrequency/API.php index 06acc4090f..70e6010a02 100644 --- a/plugins/VisitFrequency/API.php +++ b/plugins/VisitFrequency/API.php @@ -36,6 +36,7 @@ class Piwik_VisitFrequency_API 'max_actions_returning', 'sum_visit_length_returning', 'bounce_count_returning', + 'nb_visits_converted_returning', ); $dataTable = $archive->getDataTableFromNumeric($toFetch); return $dataTable; @@ -73,4 +74,9 @@ class Piwik_VisitFrequency_API { return $this->getNumeric( $idSite, $period, $date, 'bounce_count_returning'); } + + public function getConvertedVisitsReturning( $idSite, $period, $date ) + { + return $this->getNumeric( $idSite, $period, $date, 'nb_visits_converted_returning'); + } } diff --git a/plugins/VisitFrequency/Controller.php b/plugins/VisitFrequency/Controller.php index c3ce1a01bb..366ed17ab4 100644 --- a/plugins/VisitFrequency/Controller.php +++ b/plugins/VisitFrequency/Controller.php @@ -37,8 +37,7 @@ class Piwik_VisitFrequency_Controller extends Piwik_Controller protected function getSummary() { - $requestString = "method=VisitFrequency.getSummary - &format=original"; + $requestString = "method=VisitFrequency.getSummary&format=original"; $request = new Piwik_API_Request($requestString); return $request->process(); } diff --git a/plugins/VisitFrequency/VisitFrequency.php b/plugins/VisitFrequency/VisitFrequency.php index 94aa1329d9..68bb7fb02a 100644 --- a/plugins/VisitFrequency/VisitFrequency.php +++ b/plugins/VisitFrequency/VisitFrequency.php @@ -58,6 +58,7 @@ class Piwik_VisitFrequency extends Piwik_Plugin 'nb_actions_returning', 'sum_visit_length_returning', 'bounce_count_returning', + 'nb_visits_converted_returning', ); $archiveProcessing->archiveNumericValuesSum($numericToSum); $archiveProcessing->archiveNumericValuesMax('max_actions_returning'); @@ -72,7 +73,8 @@ class Piwik_VisitFrequency extends Piwik_Plugin sum(visit_total_actions) as nb_actions_returning, max(visit_total_actions) as max_actions_returning, sum(visit_total_time) as sum_visit_length_returning, - sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count_returning + sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count_returning, + sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted_returning FROM ".$archiveProcessing->logTable." WHERE visit_server_date = ? AND idsite = ? @@ -87,6 +89,7 @@ class Piwik_VisitFrequency extends Piwik_Plugin $row['max_actions_returning'] = 0; $row['sum_visit_length_returning'] = 0; $row['bounce_count_returning'] = 0; + $row['nb_visits_converted_returning'] = 0; } foreach($row as $name => $value) diff --git a/plugins/VisitTime/Controller.php b/plugins/VisitTime/Controller.php index d08458a706..923c406a1e 100644 --- a/plugins/VisitTime/Controller.php +++ b/plugins/VisitTime/Controller.php @@ -23,6 +23,7 @@ class Piwik_VisitTime_Controller extends Piwik_Controller $view->disableSearchBox(); $view->disableExcludeLowPopulation(); $view->disableOffsetInformation(); + $view->enableShowGoals(); return $this->renderView($view, $fetch); } diff --git a/plugins/VisitTime/VisitTime.php b/plugins/VisitTime/VisitTime.php index 0160b838c4..9ee7f006d9 100644 --- a/plugins/VisitTime/VisitTime.php +++ b/plugins/VisitTime/VisitTime.php @@ -52,41 +52,58 @@ class Piwik_VisitTime extends Piwik_Plugin function archivePeriod( $notification ) { $archiveProcessing = $notification->getNotificationObject(); - $dataTableToSum = array( 'VisitTime_localTime', 'VisitTime_serverTime', ); - $archiveProcessing->archiveDataTable($dataTableToSum); } public function archiveDay( $notification ) { $archiveProcessing = $notification->getNotificationObject(); - - $this->archiveProcessing = $archiveProcessing; - - $recordName = 'VisitTime_localTime'; + $this->archiveDayAggregateVisits($archiveProcessing); + $this->archiveDayAggregateGoals($archiveProcessing); + $this->archiveDayRecordInDatabase($archiveProcessing); + } + + protected function archiveDayAggregateVisits($archiveProcessing) + { $labelSQL = "HOUR(visitor_localtime)"; - $tableLocalTime = $archiveProcessing->getDataTableInterestForLabel($labelSQL); - $this->makeSureAllHoursAreSet($tableLocalTime); - $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableLocalTime->getSerialized()); + $this->interestByLocalTime = $archiveProcessing->getArrayInterestForLabel($labelSQL); - $recordName = 'VisitTime_serverTime'; $labelSQL = "HOUR(visit_first_action_time)"; - $tableServerTime = $archiveProcessing->getDataTableInterestForLabel($labelSQL); - $this->makeSureAllHoursAreSet($tableServerTime); - $record = new Piwik_ArchiveProcessing_Record_BlobArray($recordName, $tableServerTime->getSerialized()); + $this->interestByServerTime = $archiveProcessing->getArrayInterestForLabel($labelSQL); + } + + protected function archiveDayAggregateGoals($archiveProcessing) + { + $query = $archiveProcessing->queryConversionsBySingleSegment("HOUR(server_time)"); + while($row = $query->fetch()) + { + $this->interestByServerTime[$row['label']][Piwik_Archive::INDEX_GOALS][$row['idgoal']] = $archiveProcessing->getGoalRowFromQueryRow($row); + } + $archiveProcessing->enrichConversionsByLabelArray($this->interestByServerTime); } - private function makeSureAllHoursAreSet($table) + protected function archiveDayRecordInDatabase($archiveProcessing) + { + $tableLocalTime = $archiveProcessing->getDataTableFromArray($this->interestByLocalTime); + $this->makeSureAllHoursAreSet($tableLocalTime, $archiveProcessing); + $record = new Piwik_ArchiveProcessing_Record_BlobArray('VisitTime_localTime', $tableLocalTime->getSerialized()); + + $tableServerTime = $archiveProcessing->getDataTableFromArray($this->interestByServerTime); + $this->makeSureAllHoursAreSet($tableServerTime, $archiveProcessing); + $record = new Piwik_ArchiveProcessing_Record_BlobArray('VisitTime_serverTime', $tableServerTime->getSerialized()); + } + + private function makeSureAllHoursAreSet($table, $archiveProcessing) { for($i=0;$i<=23;$i++) { if($table->getRowFromLabel($i) === false) { - $row = $this->archiveProcessing->getNewInterestRowLabeled($i); + $row = $archiveProcessing->getNewInterestRowLabeled($i); $table->addRow( $row ); } } diff --git a/plugins/VisitsSummary/API.php b/plugins/VisitsSummary/API.php index 5424ad8c2a..ed9a75df7c 100644 --- a/plugins/VisitsSummary/API.php +++ b/plugins/VisitsSummary/API.php @@ -36,6 +36,7 @@ class Piwik_VisitsSummary_API 'nb_actions', 'sum_visit_length', 'bounce_count', + 'nb_visits_converted', ); $dataTable = $archive->getDataTableFromNumeric($toFetch); return $dataTable; @@ -83,4 +84,9 @@ class Piwik_VisitsSummary_API { return self::getNumeric( $idSite, $period, $date, 'bounce_count'); } + + public function getVisitsConverted( $idSite, $period, $date ) + { + return self::getNumeric( $idSite, $period, $date, 'nb_visits_converted'); + } } diff --git a/plugins/VisitsSummary/Controller.php b/plugins/VisitsSummary/Controller.php index 4b2cc72e56..953c90a7d9 100644 --- a/plugins/VisitsSummary/Controller.php +++ b/plugins/VisitsSummary/Controller.php @@ -29,6 +29,7 @@ class Piwik_VisitsSummary_Controller extends Piwik_Controller $view->sumVisitLength = $dataTableVisit->getColumn('sum_visit_length'); $view->bounceCount = $dataTableVisit->getColumn('bounce_count'); $view->maxActions = $dataTableVisit->getColumn('max_actions'); + //TODO visits with conversion } function getSparklines() diff --git a/tests/core/DataTable.test.php b/tests/core/DataTable.test.php index 16960ae488..092fb7e7d3 100644 --- a/tests/core/DataTable.test.php +++ b/tests/core/DataTable.test.php @@ -256,7 +256,7 @@ class Test_Piwik_DataTable extends UnitTestCase 'test_stringint'=> "145", "test" => 'string fake', 'super'=>array('this column has an array string that will be 0 when algorithm sums the value'), - 'integerArrayToSum'=>array( 1=>1, 2=>10.0), + 'integerArrayToSum'=>array( 1 => 1, 2 => 10.0, 3 => array(1 => 2, 2 => 3)), ); $metadata = array('logo'=> 'piwik.png', 'super'=>array('this column has an array value, amazing')); @@ -273,7 +273,7 @@ class Test_Piwik_DataTable extends UnitTestCase 'test_stringint'=> "5", 0925824 => 'toto', 'super'=>array('this column has geagaean array value, amazing'), - 'integerArrayToSum'=>array( 1=> 5, 2=>5.5), + 'integerArrayToSum'=>array( 1 => 5, 2 => 5.5, 3 => array(2 => 4)), ); $finalRow = new Piwik_DataTable_Row( array(Piwik_DataTable_Row::COLUMNS => $columns2)); $finalRow->sumRow($row1); @@ -283,10 +283,10 @@ class Test_Piwik_DataTable extends UnitTestCase 'test_float3'=> 1.5, 'test_stringint'=> 150, //add also strings!! 'super'=>array(0), - 'integerArrayToSum' => array( 1=> 6, 2=>15.5), + 'test' => 0, + 'integerArrayToSum' => array( 1 => 6, 2 => 15.5, 3 => array(1 => 2, 2 => 7)), 0925824 => 'toto', ); - $rowWanted = new Piwik_DataTable_Row( array(Piwik_DataTable_Row::COLUMNS => $columnsWanted)); $this->assertTrue( Piwik_DataTable_Row::isEqual($rowWanted, $finalRow)); } diff --git a/tests/core/DataTable/Renderer.test.php b/tests/core/DataTable/Renderer.test.php index 28721ec5cd..182eec74b9 100644 --- a/tests/core/DataTable/Renderer.test.php +++ b/tests/core/DataTable/Renderer.test.php @@ -50,7 +50,7 @@ class Test_Piwik_DataTable_Renderer extends UnitTestCase $subtable = $array = array ( - array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google©', 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9), + array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Google©', 'goals' => array('idgoal=1' => array('revenue'=> 5.5, 'nb_conversions' => 10)), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, 'max_actions' => '5', 'sum_visit_length' => 517, 'bounce_count' => 9), Piwik_DataTable_Row::METADATA => array('url' => 'http://www.google.com', 'logo' => './plugins/Referers/images/searchEngines/www.google.com.png'), ), array ( Piwik_DataTable_Row::COLUMNS => array( 'label' => 'Yahoo!', 'nb_uniq_visitors' => 15, 'nb_visits' => 151, 'nb_actions' => 147, 'max_actions' => '50', 'sum_visit_length' => 517, 'bounce_count' => 90), @@ -107,6 +107,12 @@ class Test_Piwik_DataTable_Renderer extends UnitTestCase <result> <row> <label>Google©</label> + <goals> + <row idgoal=\'1\'> + <revenue>5.5</revenue> + <nb_conversions>10</nb_conversions> + </row> + </goals> <nb_uniq_visitors>11</nb_uniq_visitors> <nb_visits>11</nb_visits> <nb_actions>17</nb_actions> @@ -190,9 +196,9 @@ class Test_Piwik_DataTable_Renderer extends UnitTestCase $dataTable = $this->getDataTableTest(); $render = new Piwik_DataTable_Renderer_Csv($dataTable); $render->convertToUnicode = false; - $expected = 'label,nb_uniq_visitors,nb_visits,nb_actions,max_actions,sum_visit_length,bounce_count,metadata_url,metadata_logo -Google©,11,11,17,5,517,9,http://www.google.com,./plugins/Referers/images/searchEngines/www.google.com.png -Yahoo!,15,151,147,50,517,90,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png'; + $expected = 'label,goals_idgoal=1_revenue,goals_idgoal=1_nb_conversions,nb_uniq_visitors,nb_visits,nb_actions,max_actions,sum_visit_length,bounce_count,metadata_url,metadata_logo +Google©,5.5,10,11,11,17,5,517,9,http://www.google.com,./plugins/Referers/images/searchEngines/www.google.com.png +Yahoo!,,,15,151,147,50,517,90,http://www.yahoo.com,./plugins/Referers/images/searchEngines/www.yahoo.com.png'; $this->assertEqual( $expected,$render->render()); } @@ -243,7 +249,7 @@ bounce_count,44'; { $dataTable = $this->getDataTableTest(); $render = new Piwik_DataTable_Renderer_Json($dataTable, true); - $expected = '[{"label":"Google©","nb_uniq_visitors":11,"nb_visits":11,"nb_actions":17,"max_actions":"5","sum_visit_length":517,"bounce_count":9,"url":"http:\/\/www.google.com","logo":".\/plugins\/Referers\/images\/searchEngines\/www.google.com.png"},{"label":"Yahoo!","nb_uniq_visitors":15,"nb_visits":151,"nb_actions":147,"max_actions":"50","sum_visit_length":517,"bounce_count":90,"url":"http:\/\/www.yahoo.com","logo":".\/plugins\/Referers\/images\/searchEngines\/www.yahoo.com.png","idsubdatatable":0,"subtable":[{"label":"sub1","count":1},{"label":"sub2","count":2}]}]'; + $expected = '[{"label":"Google©","goals":{"idgoal=1":{"revenue":5.5,"nb_conversions":10}},"nb_uniq_visitors":11,"nb_visits":11,"nb_actions":17,"max_actions":"5","sum_visit_length":517,"bounce_count":9,"url":"http:\/\/www.google.com","logo":".\/plugins\/Referers\/images\/searchEngines\/www.google.com.png"},{"label":"Yahoo!","nb_uniq_visitors":15,"nb_visits":151,"nb_actions":147,"max_actions":"50","sum_visit_length":517,"bounce_count":90,"url":"http:\/\/www.yahoo.com","logo":".\/plugins\/Referers\/images\/searchEngines\/www.yahoo.com.png","idsubdatatable":0,"subtable":[{"label":"sub1","count":1},{"label":"sub2","count":2}]}]'; $rendered = $render->render(); $this->assertEqual( $expected,$rendered); @@ -289,6 +295,12 @@ bounce_count,44'; 0 => array ( 'label' => 'Google©', + 'goals' => array( + 'idgoal=1' => array( + 'revenue' => 5.5, + 'nb_conversions' => 10, + ), + ), 'nb_uniq_visitors' => 11, 'nb_visits' => 11, 'nb_actions' => 17, diff --git a/tests/core/Piwik.test.php b/tests/core/Piwik.test.php index f3313e7a81..6f53b1dcb3 100644 --- a/tests/core/Piwik.test.php +++ b/tests/core/Piwik.test.php @@ -29,17 +29,18 @@ class Test_Piwik extends UnitTestCase (float)-1, (float)0 , (float)1, (float)1.5, (float)-1.5, (float)21111, (float)89898, (float)99999999999, (float)-4565656, (int)-1, (int)0 , (int)1, (int)1.5, (int)-1.5, (int)21111, (int)89898, (int)99999999999, (int)-4565656, '-1', '0' , '1', '1.5', '-1.5', '21111', '89898', '99999999999', '-4565656', + '1e3','0x123', "-1e-2", ); foreach($valid as $toTest) { - $this->assertTrue(Piwik::isNumeric($toTest), $toTest." not valid!"); + $this->assertTrue(Piwik::isNumeric($toTest), $toTest." not valid but should!"); } } public function test_isNumericNotValid() { $notvalid = array( - '-1.0.0', '1e3','1,2', '0x123', '--1', '-.', "-1e-2", '- 1', '1-', + '-1.0.0', '1,2', '--1', '-.', '- 1', '1-', ); foreach($notvalid as $toTest) { diff --git a/tests/datasets/referer-xss.txt b/tests/datasets/referer-xss.txt new file mode 100644 index 0000000000..b396c04813 --- /dev/null +++ b/tests/datasets/referer-xss.txt @@ -0,0 +1,7 @@ +INSERT INTO `piwik_log_visit` (`idvisit`, `idsite`, `visitor_localtime`, `visitor_idcookie`, `visitor_returning`, `visit_first_action_time`, `visit_last_action_time`, `visit_server_date`, `visit_exit_idaction`, `visit_entry_idaction`, `visit_total_actions`, `visit_total_time`, `visit_goal_converted`, `referer_type`, `referer_name`, `referer_url`, `referer_keyword`, `config_md5config`, `config_os`, `config_browser_name`, `config_browser_version`, `config_resolution`, `config_pdf`, `config_flash`, `config_java`, `config_director`, `config_quicktime`, `config_realplayer`, `config_windowsmedia`, `config_cookie`, `location_ip`, `location_browser_lang`, `location_country`, `location_continent`, `location_provider`) VALUES +(5, 1, '14:38:01', 'fb9af1315358d20049619db26b6f1ff9', 1, '2008-11-14 15:48:40', '2008-11-14 15:48:40', '2008-11-14', 2, 2, 1, 10, '', 2, 'Google', 'http://www.google.co.uk/search?hl=en&q=%3Cscript%3Ealert(%27test%27);%3C/script%3E', '%3cscript%3ealert(%27test%27);%3c/script%3e', '231ea91c00491cb5e6484f00c274b037', 'WXP', 'FF', '3.0', '1440x900', 1, 1, 0, 0, 0, 0, 1, 1, 2130706433, 'en-gb,fr;q=0.8,ja;q=', 'uk', 'eur', 'Ip'), +(15, 1, '18:03:29', 'fff3699b375db5e7cea33a58454cb8a0', 0, '2008-11-14 18:04:39', '2008-11-14 18:04:39', '2008-11-14', 2, 2, 1, 10, '', 3, 'htmlentities', 'http://example.com/"<script>alert(''test'');</script>', '', '00b29dee0697cb1eeb1931d04813f5f1', 'WXP', 'FF', '3.0', '1440x900', 1, 1, 1, 0, 0, 0, 1, 1, 2130706433, 'en-gb,fr;q=0.8,ja;q=', 'uk', 'eur', 'Ip'), +(16, 1, '19:51:00', 'fb9af1315358d20049619db26b6f1ff9', 1, '2008-11-14 19:51:00', '2008-11-14 19:51:00', '2008-11-14', 5, 5, 1, 10, '', 3, 'example1.com', 'http://example.com/%22%3E%3Cscript%3Ealert(%27yo%27)%3C%2Fscript%3E', '', '00b29dee0697cb1eeb1931d04813f5f1', 'WXP', 'FF', '3.0', '1440x900', 1, 1, 1, 0, 0, 0, 1, 1, 2130706433, 'en-gb,fr;q=0.8,ja;q=', 'uk', 'eur', 'Ip'), +(17, 1, '10:25:48', 'fb9af1315358d20049619db26b6f1ff9', 1, '2008-11-14 10:25:48', '2008-11-14 10:25:48', '2008-11-14', 5, 5, 1, 10, '', 3, 'urlencode', 'http://example3.com/test%3cscript%3ealert(%27test%27);%3c/script%3e', '', '00b29dee0697cb1eeb1931d04813f5f1', 'WXP', 'FF', '3.0', '1440x900', 1, 1, 1, 0, 0, 0, 1, 1, 2130706433, 'en-gb,fr;q=0.8,ja;q=', 'uk', 'eur', 'Ip'), +(21, 1, '12:35:41', 'fb9af1315358d20049619db26b6f1ff9', 1, '2008-11-14 12:35:41', '2008-11-14 13:27:14', '2008-11-14', 5, 2, 26, 3093, '', 3, 'example2.com', 'http://example.com/"><script>alert(''hi'')</script>', '', '00b29dee0697cb1eeb1931d04813f5f1', 'WXP', 'FF', '3.0', '1440x900', 1, 1, 1, 0, 0, 0, 1, 1, 2130706433, 'en-gb,fr;q=0.8,ja;q=', 'uk', 'eur', 'Ip'), +(53615, 1, '14:38:01', 'fb9af1315358d20049619db26b6f1ff9', 1, '2008-11-24 14:04:42', '2008-11-24 14:21:20', '2008-11-24', 2, 2, 3, 998, '', 3, 'localhost<script>alert(''test'')<', 'http://localhost<script>alert(''test'')</script>/test<script>alert(''test'')</script>', '', '231ea91c00491cb5e6484f00c274b037', 'WXP', 'FF', '3.0', '1440x900', 1, 1, 0, 0, 0, 0, 1, 1, 2130706433, 'en-gb,fr;q=0.8,ja;q=', 'uk', 'eur', 'Ip'); diff --git a/themes/default/common.css b/themes/default/common.css index 353a9ab503..ead4c1d439 100644 --- a/themes/default/common.css +++ b/themes/default/common.css @@ -135,3 +135,19 @@ a { color: #000000; margin-bottom: 0px; } +#tooltip h3 { + font-weight:none; + font-size:1em; + color:#162540; + margin:0; + padding:0; +} +#tooltip { + position: absolute; + z-index: 3000; + border: 1px solid #111; + padding:7px; + background-color:#F5F7FF; + opacity:0.95; +} + diff --git a/themes/default/images/feed.png b/themes/default/images/feed.png Binary files differnew file mode 100644 index 0000000000..315c4f4fa6 --- /dev/null +++ b/themes/default/images/feed.png diff --git a/themes/default/images/goal.png b/themes/default/images/goal.png Binary files differnew file mode 100644 index 0000000000..e4bc611f87 --- /dev/null +++ b/themes/default/images/goal.png |