diff --git a/config/global.ini.php b/config/global.ini.php index ffff48ea6bc6bf7fa6b4e40dc868d6f19a0d524f..14742744e7b84e9cd605d70e9b024f64c854959e 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -72,6 +72,14 @@ enable_browser_archiving_triggering = 1 ; the page first-post in the subcategory development which belongs to the blog category action_category_delimiter = / +; this action name is used when the URL ends with a slash / +; it is useful to have an actual string to write in the UI +action_default_name = index + +; this action name is used when the URL has no page title or page URL defined +action_default_name_when_not_defined = "page title not defined" +action_default_url_when_not_defined = "page url not defined" + ; currency used by default when reporting money in Piwik ; the trailing space is required for php 5.2.x vs 5.3 compatibility default_currency = "$ " @@ -139,9 +147,6 @@ swfobject_version = 2.2 ; set to 0 if you want to stop tracking the visitors. Useful if you need to stop all the connections on the DB. record_statistics = 1 -; this action name is used when the javascript variable piwik_action_name is not specified in the piwik javascript code, and when the URL has no path. -default_action_name = index - ; length of a visit in seconds. If a visitor comes back on the website visit_standard_length seconds after his last page view, it will be recorded as a new visit visit_standard_length = 1800 diff --git a/core/API/Proxy.php b/core/API/Proxy.php index 0e83592378adc8e1ca5c58ac2d9ade443bbd45a8..26c11288c9787e11b2c037b4bd71f9f5a04e0661 100644 --- a/core/API/Proxy.php +++ b/core/API/Proxy.php @@ -246,7 +246,9 @@ class Piwik_API_Proxy { if($method->isPublic() && !$method->isConstructor() - && $method->getName() != 'getInstance' ) + && $method->getName() != 'getInstance' + && false === strstr($method->getDocComment(), '@deprecated') + ) { $name = $method->getName(); $parameters = $method->getParameters(); diff --git a/core/ArchiveProcessing/Day.php b/core/ArchiveProcessing/Day.php index 051fedad186ea3117f5b9818c295601edc95199b..2e3c0bbab72cdfb8f2f3d8723cdd30f70cbc074a 100644 --- a/core/ArchiveProcessing/Day.php +++ b/core/ArchiveProcessing/Day.php @@ -28,7 +28,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $this->db = Zend_Registry::get('db'); $this->debugAlwaysArchive = Zend_Registry::get('config')->Debug->always_archive_data_day; } - + /** * Main method to process logs for a day. The only logic done here is computing the number of visits, actions, etc. * All the other reports are computed inside plugins listening to the event 'ArchiveProcessing_Day.compute'. diff --git a/core/Piwik.php b/core/Piwik.php index 0d3595a452964c0c2fe28546348c9c44eafe6b5b..09a6b8d109a51121a4a3684cc93001843e2e8dd5 100644 --- a/core/Piwik.php +++ b/core/Piwik.php @@ -609,9 +609,10 @@ class Piwik 'log_action' => "CREATE TABLE {$prefixTables}log_action ( idaction INTEGER(10) UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(255) NOT NULL, + hash INTEGER(10) UNSIGNED NOT NULL, type TINYINT UNSIGNED NULL, PRIMARY KEY(idaction), - INDEX index_type_name (type, name(15)) + INDEX index_type_hash (type, hash) ) DEFAULT CHARSET=utf8 ", @@ -624,8 +625,8 @@ class Piwik visit_first_action_time DATETIME NOT NULL, visit_last_action_time DATETIME NOT NULL, visit_server_date DATE NOT NULL, - visit_exit_idaction INTEGER(11) NOT NULL, - visit_entry_idaction INTEGER(11) NOT NULL, + visit_exit_idaction_url INTEGER(11) NOT NULL, + visit_entry_idaction_url INTEGER(11) NOT NULL, visit_total_actions SMALLINT(5) UNSIGNED NOT NULL, visit_total_time SMALLINT(5) UNSIGNED NOT NULL, visit_goal_converted TINYINT(1) NOT NULL, @@ -663,7 +664,7 @@ class Piwik `visitor_idcookie` char(32) NOT NULL, `server_time` datetime NOT NULL, `visit_server_date` date NOT NULL, - `idaction` int(11) default NULL, + `idaction_url` int(11) default NULL, `idlink_va` int(11) default NULL, `referer_idvisit` int(10) unsigned default NULL, `referer_visit_server_date` date default NULL, @@ -684,8 +685,9 @@ class Piwik 'log_link_visit_action' => "CREATE TABLE {$prefixTables}log_link_visit_action ( idlink_va INTEGER(11) NOT NULL AUTO_INCREMENT, idvisit INTEGER(10) UNSIGNED NOT NULL, - idaction INTEGER(10) UNSIGNED NOT NULL, - idaction_ref INTEGER(11) UNSIGNED NOT NULL, + idaction_url INTEGER(10) UNSIGNED NOT NULL, + idaction_url_ref INTEGER(10) UNSIGNED NOT NULL, + idaction_name INTEGER(10) UNSIGNED, time_spent_ref_action INTEGER(10) UNSIGNED NOT NULL, PRIMARY KEY(idlink_va), INDEX index_idvisit(idvisit) diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php index 5afe0738cf922bdb67215720fe2b5da30f41f0bf..bd0d924431060a6b7ef25a986db8652a3d506372 100644 --- a/core/Tracker/Action.php +++ b/core/Tracker/Action.php @@ -18,9 +18,10 @@ * @subpackage Piwik_Tracker */ interface Piwik_Tracker_Action_Interface { - const TYPE_ACTION = 1; + const TYPE_ACTION_URL = 1; const TYPE_OUTLINK = 2; const TYPE_DOWNLOAD = 3; + const TYPE_ACTION_NAME = 4; public function setRequest($requestArray); public function setIdSite( $idSite ); @@ -29,7 +30,8 @@ interface Piwik_Tracker_Action_Interface { public function getActionName(); public function getActionType(); public function record( $idVisit, $idRefererAction, $timeSpentRefererAction ); - public function getIdAction(); + public function getIdActionUrl(); + public function getIdActionName(); public function getIdLinkVisitAction(); } @@ -44,10 +46,6 @@ interface Piwik_Tracker_Action_Interface { * - An action is defined by a name. * - The name can be specified in the JS Code in the variable 'action_name' * For example you can decide to use the javascript value document.title as an action_name - * - If the name is not specified, we use the URL(path+query) to build a default name. - * For example for "http://piwik.org/test/my_page/test.html" - * the name would be "test/my_page/test.html" - * - If the name is empty we set it to default_action_name found in global.ini.php * - Handling UTF8 in the action name * PLUGIN_IDEA - An action is associated to URLs and link to the URL from the reports (currently actions do not link to the url of the pages) * PLUGIN_IDEA - An action hit by a visitor is associated to the HTML title of the page that triggered the action and this HTML title is displayed in the interface @@ -60,16 +58,12 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface private $request; private $idSite; private $idLinkVisitAction; - private $idAction = null; + private $idActionName = null; + private $idActionUrl = null; private $actionName; private $actionType; - private $url; - - protected function getDefaultActionName() - { - return Piwik_Tracker_Config::getInstance()->Tracker['default_action_name']; - } + private $actionUrl; public function setRequest($requestArray) { @@ -92,6 +86,29 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface { return $this->actionType; } + public function getActionNameType() + { + $actionNameType = null; + + // we can add here action types for names of other actions than page views (like downloads, outlinks) + switch( $this->getActionType() ) + { + case Piwik_Tracker_Action_Interface::TYPE_ACTION_URL: + $actionNameType = Piwik_Tracker_Action_Interface::TYPE_ACTION_NAME; + break; + } + + return $actionNameType; + } + + public function getIdActionUrl() + { + return $this->idActionUrl; + } + public function getIdActionName() + { + return $this->idActionName; + } protected function setActionName($name) { @@ -115,44 +132,65 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface } /** - * Returns the idaction of the current action name. - * This idaction is used in the visitor logging table to link the visit information + * Loads the idaction of the current action name and the current action url. + * These idactions are used in the visitor logging table to link the visit information * (entry action, exit action) to the actions. - * This idaction is also used in the table that links the visits and their actions. + * These idactions are also used in the table that links the visits and their actions. * - * The methods takes care of creating a new record in the action table if the existing - * action name doesn't exist yet. + * The methods takes care of creating a new record(s) in the action table if the existing + * action name and action url doesn't exist yet. * - * @return int Id action that is associated to this action name in the Actions table lookup */ - function getIdAction() + function loadIdActionNameAndUrl() { - if(!is_null($this->idAction)) + if( !is_null($this->idActionUrl) && !is_null($this->idActionName) ) { - return $this->idAction; + return; } - $idAction = Piwik_Tracker::getDatabase()->fetch("/* SHARDING_ID_SITE = ".$this->idSite." */ SELECT idaction + $idAction = Piwik_Tracker::getDatabase()->fetchAll("/* SHARDING_ID_SITE = ".$this->idSite." */ + SELECT idaction, type FROM ".Piwik_Common::prefixTable('log_action') - ." WHERE name = ? AND type = ?", - array($this->getActionName(), $this->getActionType()) + ." WHERE " + ." ( hash = CRC32(?) AND name = ? AND type = ? ) " + ." OR " + ." ( hash = CRC32(?) AND name = ? AND type = ? ) ", + array($this->getActionName(), $this->getActionName(), $this->getActionNameType(), + $this->getActionUrl(), $this->getActionUrl(), $this->getActionType()) ); - - // the action name has not been found, create it - if($idAction === false || $idAction == '') + + if( $idAction !== false ) { - Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ".$this->idSite." */ - INSERT INTO ". Piwik_Common::prefixTable('log_action'). " ( name, type ) - VALUES (?,?)", - array($this->getActionName(),$this->getActionType()) - ); - $idAction = Piwik_Tracker::getDatabase()->lastInsertId(); + foreach($idAction as $row) + { + if( $row['type'] == Piwik_Tracker_Action_Interface::TYPE_ACTION_NAME ) + { + $this->idActionName = $row['idaction']; + } + else + { + $this->idActionUrl = $row['idaction']; + } + } } - else + + $sql = "/* SHARDING_ID_SITE = ".$this->idSite." */ + INSERT INTO ". Piwik_Common::prefixTable('log_action'). + "( name, hash, type ) VALUES (?,CRC32(?),?)"; + + if( is_null($this->idActionName) + && !is_null($this->getActionNameType()) ) { - $idAction = $idAction['idaction']; + Piwik_Tracker::getDatabase()->query($sql, + array($this->getActionName(), $this->getActionName(), $this->getActionNameType())); + $this->idActionName = Piwik_Tracker::getDatabase()->lastInsertId(); + } + + if( is_null($this->idActionUrl) ) + { + Piwik_Tracker::getDatabase()->query($sql, + array($this->getActionUrl(), $this->getActionUrl(), $this->getActionType())); + $this->idActionUrl = Piwik_Tracker::getDatabase()->lastInsertId(); } - $this->idAction = $idAction; - return $this->idAction; } /** @@ -174,9 +212,11 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface */ public function record( $idVisit, $idRefererAction, $timeSpentRefererAction) { - Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ".$this->idSite." */ INSERT INTO ".Piwik_Common::prefixTable('log_link_visit_action') - ." (idvisit, idaction, idaction_ref, time_spent_ref_action) VALUES (?,?,?,?)", - array($idVisit, $this->getIdAction(), $idRefererAction, $timeSpentRefererAction) + $this->loadIdActionNameAndUrl(); + + Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ".$this->idSite." */ INSERT INTO ".Piwik_Common::prefixTable('log_link_visit_action') + ." (idvisit, idaction_url, idaction_name, idaction_url_ref, time_spent_ref_action) VALUES (?,?,?,?,?)", + array($idVisit, $this->getIdActionUrl(), $this->getIdActionName(), $idRefererAction, $timeSpentRefererAction) ); $this->idLinkVisitAction = Piwik_Tracker::getDatabase()->lastInsertId(); @@ -209,9 +249,13 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface /** * Generates the name of the action from the URL or the specified name. * Sets the name as $this->actionName + * + * @return array */ protected function extractUrlAndActionNameFromRequest() { + $actionName = null; + // download? $downloadUrl = Piwik_Common::getRequestVar( 'download', '', 'string', $this->request); if(!empty($downloadUrl)) @@ -230,26 +274,15 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface $url = $outlinkUrl; } } - + + $actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string', $this->request); + // defaults to page view if(empty($actionType)) { - $actionType = self::TYPE_ACTION; + $actionType = self::TYPE_ACTION_URL; $url = Piwik_Common::getRequestVar( 'url', '', 'string', $this->request); - $actionName = Piwik_Common::getRequestVar( 'action_name', '', 'string', $this->request); - if( empty($actionName) ) - { - $cleanedUrl = str_replace(array("\n", "\r", "\t"), "", $url); - $actionName = Piwik_Common::getPathAndQueryFromUrl($cleanedUrl); - // in case the $actionName is empty or ending with a slash, - // we append the defaultActionName: a/b/ becomes a/b/index - if(empty($actionName) - || substr($actionName, -1) == '/') - { - $actionName .= $this->getDefaultActionName(); - } - } - + // get the delimiter, by default '/' $actionCategoryDelimiter = Piwik_Tracker_Config::getInstance()->General['action_category_delimiter']; @@ -268,13 +301,12 @@ class Piwik_Tracker_Action implements Piwik_Tracker_Action_Interface $url = trim($url); $url = str_replace(array("\n", "\r"), "", $url); - if(empty($actionName)) - { - $actionName = $url; - } + + $actionName = trim($actionName); + $actionName = str_replace(array("\n", "\r"), "", $actionName); return array( - 'name' => $actionName, + 'name' => empty($actionName) ? '' : $actionName, 'type' => $actionType, 'url' => $url, ); diff --git a/core/Tracker/Generator.php b/core/Tracker/Generator.php index d0e5f016d2ee2b64b83223c9c5fce8dd1cb57f77..7a6dfdfe2193f3c27cb3d1ccd92667a98765b912 100644 --- a/core/Tracker/Generator.php +++ b/core/Tracker/Generator.php @@ -220,11 +220,11 @@ class Piwik_Tracker_Generator */ public function end() { - Piwik_Tracker::disconnectDatabase(); if($this->profiling) { Piwik::printSqlProfilingReportTracker(); } + Piwik_Tracker::disconnectDatabase(); } /** @@ -446,19 +446,12 @@ class Piwik_Tracker_Generator // add the parameter to the url $this->setCurrentRequest( $GETParamToAdd , $urlValue); } - - // if we didn't set any campaign NOR any download click - // then we sometimes set a special action name to the current action - elseif(rand(0,2)==1) - { - $this->setCurrentRequest( 'action_name' , $this->getRandomString(1,1)); - } } $this->setCurrentRequest( 'url' ,$url); // setup the title of the page - $this->setCurrentRequest( 'title',$this->getRandomString(15,5)); + $this->setCurrentRequest( 'action_name',$this->getRandomString(15,5)); } /** diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php index 4dc148d86cd9ee2281e04af7c18b2a30c832b7cd..f1ff810891d6577fe07c9692cb7d6a5db0f128c4 100644 --- a/core/Tracker/GoalManager.php +++ b/core/Tracker/GoalManager.php @@ -17,7 +17,7 @@ class Piwik_Tracker_GoalManager { /** - * @var Piwik_Cookie + * @var Piwik_Cookie */ protected $cookie = null; /** @@ -41,7 +41,7 @@ class Piwik_Tracker_GoalManager } return array(); } - + static public function getGoalDefinition( $idSite, $idGoal ) { $goals = self::getGoalDefinitions( $idSite ); @@ -54,7 +54,7 @@ class Piwik_Tracker_GoalManager } throw new Exception("The goal id = $idGoal couldn't be found."); } - + static public function getGoalIds( $idSite ) { $goals = self::getGoalDefinitions( $idSite ); @@ -65,13 +65,13 @@ class Piwik_Tracker_GoalManager } return $goalIds; } - + private function isGoalPluginEnabled() { return Piwik_PluginsManager::getInstance()->isPluginActivated('Goals'); } - - //TODO does this code work for manually triggered goals, with custom revenue? + + //TODO does this code work for manually triggered goals, with custom revenue? function detectGoalsMatchingUrl($idSite, $action) { if(!$this->isGoalPluginEnabled()) @@ -85,7 +85,7 @@ class Piwik_Tracker_GoalManager { $attribute = $goal['match_attribute']; // if the attribute to match is not the type of the current action - if( ($actionType == Piwik_Tracker_Action::TYPE_ACTION && $attribute != 'url') + if( ($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL && $attribute != 'url') || ($actionType == Piwik_Tracker_Action::TYPE_DOWNLOAD && $attribute != 'file') || ($actionType == Piwik_Tracker_Action::TYPE_OUTLINK && $attribute != 'external_website') || ($attribute == 'manually') @@ -93,9 +93,9 @@ class Piwik_Tracker_GoalManager { continue; } - + $pattern_type = $goal['pattern_type']; - + switch($pattern_type) { case 'regex': @@ -141,7 +141,7 @@ class Piwik_Tracker_GoalManager // var_dump($this->convertedGoals);exit; return count($this->convertedGoals) > 0; } - + function detectGoalId($idSite, $idGoal, $request) { if(!$this->isGoalPluginEnabled()) @@ -159,12 +159,12 @@ class Piwik_Tracker_GoalManager $this->convertedGoals[] = $goal; return true; } - + function recordGoals($visitorInformation, $action) { $location_country = isset($visitorInformation['location_country']) ? $visitorInformation['location_country'] : Piwik_Common::getCountry(Piwik_Common::getBrowserLanguage(), $enableLanguageToCountryGuess = Piwik_Tracker_Config::getInstance()->Tracker['enable_language_to_country_guess']); $location_continent = isset($visitorInformation['location_continent']) ? $visitorInformation['location_continent'] : Piwik_Common::getContinent($location_country); - + $goal = array( 'idvisit' => $visitorInformation['idvisit'], 'idsite' => $visitorInformation['idsite'], @@ -197,18 +197,18 @@ class Piwik_Tracker_GoalManager $newGoal['revenue'] = $convertedGoal['revenue']; if(!is_null($action)) { - $newGoal['idaction'] = $action->getIdAction(); + $newGoal['idaction_url'] = $action->getIdActionUrl(); $newGoal['idlink_va'] = $action->getIdLinkVisitAction(); } printDebug($newGoal); - + $fields = implode(", ", array_keys($newGoal)); $bindFields = substr(str_repeat( "?,",count($newGoal)),0,-1); - + try { Piwik_Tracker::getDatabase()->query( - "INSERT INTO " . Piwik_Common::prefixTable('log_conversion') . " ($fields) - VALUES ($bindFields) ", array_values($newGoal) + "INSERT INTO " . Piwik_Common::prefixTable('log_conversion') . " ($fields) + VALUES ($bindFields) ", array_values($newGoal) ); } catch( Exception $e) { if(Piwik_Tracker::isErrNo($e, '1062')) @@ -221,7 +221,6 @@ class Piwik_Tracker_GoalManager throw $e; } } - //$idlog_goal = Piwik_Tracker::getDatabase()->lastInsertId(); } } } diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index 323a720806f890ecf1edaec8691f75c0f2f761e8..2b50bd1baf48c56a72a2ebe4b9fd13b47fe36a9c 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -95,7 +95,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $goalManager = new Piwik_Tracker_GoalManager(); $someGoalsConverted = false; - $actionId = 0; + $actionUrlId = 0; $action = null; $idGoal = Piwik_Common::getRequestVar('idgoal', 0, 'int', $this->request); @@ -116,7 +116,9 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $action = $this->newAction(); $this->handleAction($action); $someGoalsConverted = $goalManager->detectGoalsMatchingUrl($this->idsite, $action); - $actionId = $action->getIdAction(); + + $action->loadIdActionNameAndUrl(); + $actionUrlId = $action->getIdActionUrl(); } // the visitor and session @@ -133,9 +135,9 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface if( $this->isVisitorKnown() && $isLastActionInTheSameVisit) { - $idActionReferer = $this->visitorInfo['visit_exit_idaction']; + $idActionReferer = $this->visitorInfo['visit_exit_idaction_url']; try { - $this->handleKnownVisit($actionId, $someGoalsConverted); + $this->handleKnownVisit($actionUrlId, $someGoalsConverted); if(!is_null($action)) { $action->record( $this->visitorInfo['idvisit'], @@ -156,7 +158,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface if(!$this->isVisitorKnown() || !$isLastActionInTheSameVisit) { - $this->handleNewVisit($actionId, $someGoalsConverted); + $this->handleNewVisit($actionUrlId, $someGoalsConverted); if(!is_null($action)) { $action->record( $this->visitorInfo['idvisit'], 0, 0 ); @@ -189,7 +191,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface if(isset($GLOBALS['PIWIK_TRACKER_DEBUG']) && $GLOBALS['PIWIK_TRACKER_DEBUG']) { switch($action->getActionType()) { - case Piwik_Tracker_Action::TYPE_ACTION: + case Piwik_Tracker_Action::TYPE_ACTION_URL: $type = "normal page view"; break; case Piwik_Tracker_Action::TYPE_DOWNLOAD: @@ -211,7 +213,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface * * 2) Update the visit information */ - protected function handleKnownVisit($actionId, $someGoalsConverted) + protected function handleKnownVisit($actionUrlId, $someGoalsConverted) { $serverTime = $this->getCurrentTimestamp(); $datetimeServer = Piwik_Tracker::getDatetimeFromTimestamp($serverTime); @@ -224,13 +226,12 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface } $sqlActionIdUpdate = ''; - if(!empty($actionId)) + if(!empty($actionUrlId)) { - $sqlActionIdUpdate = "visit_exit_idaction = ". $actionId .", + $sqlActionIdUpdate = "visit_exit_idaction_url = ". $actionUrlId .", visit_total_actions = visit_total_actions + 1, "; - $this->visitorInfo['visit_exit_idaction'] = $actionId; + $this->visitorInfo['visit_exit_idaction_url'] = $actionUrlId; } - $result = Piwik_Tracker::getDatabase()->query("/* SHARDING_ID_SITE = ". $this->idsite ." */ UPDATE ". Piwik_Common::prefixTable('log_visit')." SET $sqlActionIdUpdate @@ -244,7 +245,6 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $this->visitorInfo['idvisit'], $this->visitorInfo['visitor_idcookie'] ) ); - if(Piwik_Tracker::getDatabase()->rowCount($result) == 0) { throw new Piwik_Tracker_Visit_VisitorNotFoundInDatabase("The visitor with visitor_idcookie=".$this->visitorInfo['visitor_idcookie']." and idvisit=".$this->visitorInfo['idvisit']." wasn't found in the DB, we fallback to a new visitor"); @@ -267,7 +267,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface * * 2) Insert the visit information */ - protected function handleNewVisit($actionId, $someGoalsConverted) + protected function handleNewVisit($actionUrlId, $someGoalsConverted) { printDebug("New Visit."); @@ -297,8 +297,8 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface '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_entry_idaction_url' => $actionUrlId, + 'visit_exit_idaction_url' => $actionUrlId, 'visit_total_actions' => 1, 'visit_total_time' => $defaultTimeOnePageVisit, 'visit_goal_converted' => $someGoalsConverted ? 1: 0, @@ -531,7 +531,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $this->visitorInfo['visit_last_action_time'] = $timestampLastAction; $this->visitorInfo['visit_first_action_time'] = $timestampFirstAction; $this->visitorInfo['idvisit'] = $idVisit; - $this->visitorInfo['visit_exit_idaction'] = $idLastAction; + $this->visitorInfo['visit_exit_idaction_url'] = $idLastAction; $this->visitorKnown = true; @@ -569,7 +569,7 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $this->visitorInfo['visit_last_action_time'] = $visitRow['visit_last_action_time']; $this->visitorInfo['visit_first_action_time'] = $visitRow['visit_first_action_time']; $this->visitorInfo['idvisit'] = $visitRow['idvisit']; - $this->visitorInfo['visit_exit_idaction'] = $visitRow['visit_exit_idaction']; + $this->visitorInfo['visit_exit_idaction_url'] = $visitRow['visit_exit_idaction_url']; $this->visitorKnown = true; @@ -719,10 +719,10 @@ class Piwik_Tracker_Visit implements Piwik_Tracker_Visit_Interface $this->visitorInfo['idvisit'] ); // the last action ID is the current exit idaction - if(isset($this->visitorInfo['visit_exit_idaction'] )) + if(isset($this->visitorInfo['visit_exit_idaction_url'] )) { $this->cookie->set( Piwik_Tracker::COOKIE_INDEX_ID_LAST_ACTION, - $this->visitorInfo['visit_exit_idaction'] ); + $this->visitorInfo['visit_exit_idaction_url'] ); } // for a new visit, we flag the visit with visitor_returning diff --git a/core/Updates/0.5.php b/core/Updates/0.5.php new file mode 100644 index 0000000000000000000000000000000000000000..108e651617af8df95437d82380f9a33f869d6c9d --- /dev/null +++ b/core/Updates/0.5.php @@ -0,0 +1,33 @@ +<?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$ + * + * @category Piwik + * @package Updates + */ + +/** + * @package Updates + */ +class Piwik_Updates_0_5 implements Piwik_iUpdate +{ + static function update() + { + Piwik_Updater::updateDatabase(__FILE__, array( + 'ALTER TABLE ' . Piwik::prefixTable('log_action'). ' ADD COLUMN `hash` INTEGER(10) UNSIGNED NOT NULL AFTER `name`;' => false, + 'UPDATE '. Piwik::prefixTable('log_action'). ' SET `hash` = CRC32(name);' => false, + 'CREATE INDEX index_type_hash ON '. Piwik::prefixTable('log_action') .' (type, hash);' => false, + 'DROP INDEX index_type_name ON '. Piwik::prefixTable('log_action') .';' => false, + 'ALTER TABLE '. Piwik::prefixTable('log_visit') .' CHANGE visit_exit_idaction visit_exit_idaction_url INTEGER(11) NOT NULL;' => false, + 'ALTER TABLE '. Piwik::prefixTable('log_visit') .' CHANGE visit_entry_idaction visit_entry_idaction_url INTEGER(11) NOT NULL;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_link_visit_action'). ' CHANGE `idaction_ref` `idaction_url_ref` INTEGER(10) UNSIGNED NOT NULL;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_link_visit_action'). ' CHANGE `idaction` `idaction_url` INTEGER(10) UNSIGNED NOT NULL;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_link_visit_action'). ' ADD COLUMN `idaction_name` INTEGER(10) UNSIGNED AFTER `idaction_url_ref`;' => false, + 'ALTER TABLE ' . Piwik::prefixTable('log_conversion'). ' CHANGE `idaction` `idaction_url` INTEGER(11) UNSIGNED NOT NULL;' => false, + )); + } +} diff --git a/core/Version.php b/core/Version.php index 824001fe868bdefbbde5e1f40a7ac2cf5f2efd70..26e24c4dfc8cf7d082b1562cc9b4eba225a216a9 100644 --- a/core/Version.php +++ b/core/Version.php @@ -17,5 +17,5 @@ */ final class Piwik_Version { - const VERSION = '0.4.5'; + const VERSION = '0.5'; } diff --git a/lang/en.php b/lang/en.php index ba611f0103825e96d8f0e42fd0aa86ef0ed56f1c..c393c91e4358f72d39876679a718471d400f6090 100644 --- a/lang/en.php +++ b/lang/en.php @@ -200,6 +200,7 @@ $translations = array( 'CoreUpdater_ExceptionArchiveIncomplete' => 'Archive is incomplete: some files are missing (eg. %s).', 'Actions_Actions' => 'Actions', 'Actions_SubmenuPages' => 'Pages', + 'Actions_SubmenuPageTitles' => 'Page titles', 'Actions_SubmenuOutlinks' => 'Outlinks', 'Actions_SubmenuDownloads' => 'Downloads', 'Actions_ColumnClicks' => 'Clicks', @@ -207,6 +208,7 @@ $translations = array( 'Actions_ColumnDownloads' => 'Downloads', 'Actions_ColumnUniqueDownloads' => 'Unique Downloads', 'Actions_ColumnPageName' => 'Page Name', + 'Actions_ColumnPageURL' => 'Page URL', 'Actions_ColumnClickedURL' => 'Clicked URL', 'Actions_ColumnDownloadURL' => 'Download URL', 'Dashboard_Dashboard' => 'Dashboard', diff --git a/misc/generateVisits.php b/misc/generateVisits.php index a2d2689129abfa0efb0b5e2dd03bc6fa70d9f4e7..35dec2be442ddb62042c4045e8604a491f5ec0e5 100644 --- a/misc/generateVisits.php +++ b/misc/generateVisits.php @@ -9,12 +9,29 @@ if(file_exists('../bootstrap.php')) require_once '../bootstrap.php'; } +if(empty($_GET['choice']) || $_GET['choice'] != 'yes') { + echo "<div style='color:red;font-size:large'>WARNING!</div> <br>You are about to generate fake visits which will be recorded in your Piwik database. + <br>It will <b>not</b> be possible to easily delete these visits from the piwik logs. + <br><br>Are you sure you want to generate fake visits? + <br><br> + <a href='../index.php'><b>NO</b>, I do not want to generate fake visits</a> + <br><br> + <a href='?choice=yes'><b>YES</b>, I want to generate fake visits</a> + <br><br> + Note: you can edit the source code of this file to specify how many visits to generate, how many days, etc. + "; + return; +} + + // TODO - generator should generate pages with slash, then test that period archiving doesn't show the unique page view // TODO - should generate goals with keyword or referer that are not found for this day, to simulate a referer 5 days ago and conversion today -$minVisitors = 200; -$maxVisitors = 200; +$minVisitors = 20; +$maxVisitors = 100; $nbActions = 10; -$daysToCompute = 5; +$daysToCompute = 1; +$idSite = 1; + //----------------------------------------------------------------------------- error_reporting(E_ALL|E_NOTICE); @@ -42,8 +59,7 @@ require_once PIWIK_INCLUDE_PATH . "/index.php"; require_once "FrontController.php"; Piwik::setMaxExecutionTime(0); - -$idSite = Piwik_Common::getRequestVar('idSite', 1, 'int'); +$idSite = Piwik_Common::getRequestVar('idSite', $idSite, 'int'); try { Piwik_FrontController::getInstance()->init(); diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php index 8a9851cf309a29adec8d71a3b6dc77db6e3de7ed..658d5da13ff3010883810596e7c8eebad3c4fd5d 100644 --- a/plugins/Actions/API.php +++ b/plugins/Actions/API.php @@ -50,10 +50,25 @@ class Piwik_Actions_API $dataTable->queueFilter('ReplaceSummaryRowLabel'); return $dataTable; } - + + /** + * Backward compatibility. Fallsback to getPageTitles() instead. + * @deprecated Deprecated since Piwik 0.5 + */ public function getActions( $idSite, $period, $date, $expanded = false, $idSubtable = false ) { - return $this->getDataTable('Actions_actions', $idSite, $period, $date, $expanded, $idSubtable ); + return $this->getPageTitles( $idSite, $period, $date, $expanded, $idSubtable ); + } + + public function getPageUrls( $idSite, $period, $date, $expanded = false, $idSubtable = false ) + { + return $this->getDataTable('Actions_actions_url', $idSite, $period, $date, $expanded, $idSubtable ); + } + + public function getPageTitles( $idSite, $period, $date, $expanded = false, $idSubtable = false) + { + $dataTable = $this->getDataTable('Actions_actions', $idSite, $period, $date, $expanded, $idSubtable); + return $dataTable; } public function getDownloads( $idSite, $period, $date, $expanded = false, $idSubtable = false ) diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php index 1fc9d2c19e5c3b092a063ced0774f1354c54cd1f..199726675a42449361d106704d0a04b8423981c3 100644 --- a/plugins/Actions/Actions.php +++ b/plugins/Actions/Actions.php @@ -20,6 +20,9 @@ class Piwik_Actions extends Piwik_Plugin { static protected $actionCategoryDelimiter = null; + static protected $defaultActionName = null; + static protected $defaultActionNameWhenNotDefined = null; + static protected $defaultActionUrlWhenNotDefined = null; static protected $limitLevelSubCategory = 10; // must be less than Piwik_DataTable::MAXIMUM_DEPTH_LEVEL_ALLOWED protected $maximumRowsInDataTableLevelZero; protected $maximumRowsInSubDataTable; @@ -47,9 +50,13 @@ class Piwik_Actions extends Piwik_Plugin ); return $hooks; } + public function __construct() { self::$actionCategoryDelimiter = Zend_Registry::get('config')->General->action_category_delimiter; + self::$defaultActionName = Zend_Registry::get('config')->General->action_default_name; + self::$defaultActionNameWhenNotDefined = Zend_Registry::get('config')->General->action_default_name_when_not_defined; + self::$defaultActionUrlWhenNotDefined = Zend_Registry::get('config')->General->action_default_url_when_not_defined; $this->columnToSortByBeforeTruncation = 'nb_visits'; $this->maximumRowsInDataTableLevelZero = Zend_Registry::get('config')->General->datatable_archiving_maximum_rows_actions; $this->maximumRowsInSubDataTable = Zend_Registry::get('config')->General->datatable_archiving_maximum_rows_subtable_actions; @@ -57,16 +64,18 @@ class Piwik_Actions extends Piwik_Plugin function addWidgets() { - Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuPages', 'Actions', 'getActions'); + Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuPages', 'Actions', 'getPageUrls'); + Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuPageTitles', 'Actions', 'getPageTitles'); Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuOutlinks', 'Actions', 'getOutlinks'); Piwik_AddWidget( 'Actions_Actions', 'Actions_SubmenuDownloads', 'Actions', 'getDownloads'); } function addMenus() { - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'getActions')); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPages', array('module' => 'Actions', 'action' => 'getPageUrls')); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuPageTitles', array('module' => 'Actions', 'action' => 'getPageTitles')); Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuOutlinks', array('module' => 'Actions', 'action' => 'getOutlinks')); - Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'getDownloads')); + Piwik_AddMenu('Actions_Actions', 'Actions_SubmenuDownloads', array('module' => 'Actions', 'action' => 'getDownloads')); } static protected $invalidSummedColumnNameToRenamedNameForPeriodArchive = array( @@ -88,6 +97,7 @@ class Piwik_Actions extends Piwik_Plugin 'Actions_actions', 'Actions_downloads', 'Actions_outlink', + 'Actions_actions_url', ); $archiveProcessing->archiveDataTable($dataTableToSum, self::$invalidSummedColumnNameToRenamedNameForPeriodArchive, $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation); } @@ -106,9 +116,10 @@ class Piwik_Actions extends Piwik_Plugin $archiveProcessing = $notification->getNotificationObject(); $this->actionsTablesByType = array( - Piwik_Tracker_Action::TYPE_ACTION => array(), + Piwik_Tracker_Action::TYPE_ACTION_URL => array(), Piwik_Tracker_Action::TYPE_DOWNLOAD => array(), Piwik_Tracker_Action::TYPE_OUTLINK => array(), + Piwik_Tracker_Action::TYPE_ACTION_NAME => array(), ); // This row is used in the case where an action is know as an exit_action @@ -122,7 +133,7 @@ class Piwik_Actions extends Piwik_Plugin ))); /* - * Actions global information + * Actions urls global information */ $query = "SELECT name, type, @@ -131,7 +142,25 @@ class Piwik_Actions extends Piwik_Plugin 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) + LEFT JOIN ".$archiveProcessing->logActionTable." as t3 ON (t2.idaction_url = t3.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); + + /* + * Actions names 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 ON (t2.idaction_name = t3.idaction) WHERE visit_server_date = ? AND idsite = ? GROUP BY t3.idaction @@ -139,7 +168,6 @@ class Piwik_Actions extends Piwik_Plugin $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); - /* * Entry actions */ @@ -151,10 +179,10 @@ class Piwik_Actions extends Piwik_Plugin 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) + JOIN ".$archiveProcessing->logActionTable." ON (visit_entry_idaction_url = idaction) WHERE visit_server_date = ? AND idsite = ? - GROUP BY visit_entry_idaction + GROUP BY visit_entry_idaction_url "; $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); @@ -169,10 +197,10 @@ class Piwik_Actions extends Piwik_Plugin 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) + JOIN ".$archiveProcessing->logActionTable." ON (visit_exit_idaction_url = idaction) WHERE visit_server_date = ? AND idsite = ? - GROUP BY visit_exit_idaction + GROUP BY visit_exit_idaction_url "; $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); @@ -185,10 +213,10 @@ class Piwik_Actions extends Piwik_Plugin 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) + JOIN ".$archiveProcessing->logActionTable." log_action ON (log_action.idaction = log_link_visit_action.idaction_url_ref) WHERE visit_server_date = ? AND idsite = ? - GROUP BY idaction_ref + GROUP BY idaction_url_ref "; $query = $archiveProcessing->db->query($query, array( $archiveProcessing->strDateStart, $archiveProcessing->idsite )); $modified = $this->updateActionsTableWithRowQuery($query); @@ -197,10 +225,10 @@ class Piwik_Actions extends Piwik_Plugin protected function archiveDayRecordInDatabase($archiveProcessing) { - $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION]); + $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_URL]); $this->deleteInvalidSummedColumnsFromDataTable($dataTable); $s = $dataTable->getSerialized( $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation ); - $archiveProcessing->insertBlobRecord('Actions_actions', $s); + $archiveProcessing->insertBlobRecord('Actions_actions_url', $s); destroy($dataTable); $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_DOWNLOAD]); @@ -215,6 +243,12 @@ class Piwik_Actions extends Piwik_Plugin $archiveProcessing->insertBlobRecord('Actions_outlink', $s); destroy($dataTable); + $dataTable = Piwik_ArchiveProcessing_Day::generateDataTable($this->actionsTablesByType[Piwik_Tracker_Action::TYPE_ACTION_NAME]); + $this->deleteInvalidSummedColumnsFromDataTable($dataTable); + $s = $dataTable->getSerialized( $this->maximumRowsInDataTableLevelZero, $this->maximumRowsInSubDataTable, $this->columnToSortByBeforeTruncation ); + $archiveProcessing->insertBlobRecord('Actions_actions', $s); + destroy($dataTable); + unset($this->actionsTablesByType); } @@ -232,30 +266,84 @@ class Piwik_Actions extends Piwik_Plugin } } } + + /** + * Explodes action name into an array of elements. + * + * for downloads: + * we explode link http://piwik.org/some/path/piwik.zip into an array( 'piwik.org', '/some/path/piwik.zip' ); + * + * for outlinks: + * we explode link http://dev.piwik.org/some/path into an array( 'dev.piwik.org', '/some/path' ); + * + * for action urls: + * we explode link http://piwik.org/some/path into an array( 'some', 'path' ); + * + * for action names: + * we explode name 'Piwik / Category 1 / Category 2' into an array('Piwik', 'Category 1', 'Category 2'); + * + * @param string action name + * @param int action type + * @return array of exploded elements from $name + */ static public function getActionExplodedNames($name, $type) { + $matches = array(); + $isUrl = false; + + preg_match('@^http[s]?://([^/]+)[/]?([^#]*)[#]?(.*)$@i', $name, $matches); + + if( count($matches) ) + { + $isUrl = true; + $urlHost = $matches[1]; + $urlPath = $matches[2]; + $urlAnchor = $matches[3]; + } + if($type == Piwik_Tracker_Action::TYPE_DOWNLOAD || $type == Piwik_Tracker_Action::TYPE_OUTLINK) { - $matches = $split_arr = array(); - //TODO optimize with substring count rather than preg_match - if(preg_match("#://[^/]+(/)#", $name, $matches, PREG_OFFSET_CAPTURE)) + if( $isUrl ) + { + return array($urlHost, '/' . $urlPath); + } + } + + if( $isUrl ) + { + $name = $urlPath; + + if( empty($name) || substr($name, -1) == '/' ) { - $host = substr($name, 0, $matches[1][1]); - return array($host, substr($name, strlen($host))); + $name .= self::$defaultActionName; } - return array($name, "/"); } + if(empty(self::$actionCategoryDelimiter)) { - return array($name); + return array( trim($name) ); + } + + $split = explode(self::$actionCategoryDelimiter, $name, self::$limitLevelSubCategory); + + // trim every category and remove empty categories + $split = array_map('trim', $split); + $split = array_filter($split, 'strlen'); + + if( empty($split) ) + { + if($type == Piwik_Tracker_Action::TYPE_ACTION_NAME) { + $defaultName = self::$defaultActionNameWhenNotDefined; + } else { + $defaultName = self::$defaultActionUrlWhenNotDefined; + } + return array( $defaultName ); } - return explode( self::$actionCategoryDelimiter, - $name, - self::$limitLevelSubCategory); + + return array_values( $split ); } - protected function updateActionsTableWithRowQuery($query) { $rowsProcessed = 0; @@ -265,7 +353,7 @@ class Piwik_Actions extends Piwik_Plugin // 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($actionExplodedNames)-1; for($level = 0 ; $level < $end; $level++) @@ -274,15 +362,19 @@ class Piwik_Actions extends Piwik_Plugin $currentTable =& $currentTable[$actionCategory]; } $actionName = $actionExplodedNames[$end]; - - // we are careful to prefix the pageName with some value + + // we are careful to prefix the page URL / name with some value // so that if a page has the same name as a category // we don't merge both entries - if($row['type'] == Piwik_Tracker_Action::TYPE_ACTION) + if($row['type'] == Piwik_Tracker_Action::TYPE_ACTION_URL ) { $actionName = '/' . $actionName; } - + else if( $row['type'] == Piwik_Tracker_Action::TYPE_ACTION_NAME ) + { + $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]; @@ -290,10 +382,19 @@ class Piwik_Actions extends Piwik_Plugin // add the row to the matching sub category subtable if(!($currentTable instanceof Piwik_DataTable_Row)) { - $currentTable = new Piwik_DataTable_Row(array( + if( $row['type'] == Piwik_Tracker_Action::TYPE_ACTION_NAME ) + { + $currentTable = new Piwik_DataTable_Row(array( + Piwik_DataTable_Row::COLUMNS => array('label' => (string)$actionName), + )); + } + else + { + $currentTable = new Piwik_DataTable_Row(array( Piwik_DataTable_Row::COLUMNS => array('label' => (string)$actionName), Piwik_DataTable_Row::METADATA => array('url' => (string)$row['name']), )); + } } foreach($row as $name => $value) @@ -325,7 +426,7 @@ class Piwik_Actions extends Piwik_Plugin if($currentTable->getColumn('nb_hits') === false) { // to test this code: delete the entries in log_link_action_visit for - // a given exit_idaction + // a given exit_idaction_url foreach($this->defaultRow->getColumns() as $name => $value) { $currentTable->addColumn($name, $value); diff --git a/plugins/Actions/Controller.php b/plugins/Actions/Controller.php index 6943e3d53194928cf626b75191485e00467c3ff3..64974199b477095c215262e3377be92fd4f1e1a7 100644 --- a/plugins/Actions/Controller.php +++ b/plugins/Actions/Controller.php @@ -17,41 +17,52 @@ */ class Piwik_Actions_Controller extends Piwik_Controller { - public function index() + public function getPageUrls($fetch = false) { - $view = Piwik_View::factory('index'); - - /* Actions, Downloads, Outlinks */ - $view->dataTableActions = $this->getActions( true ); - $view->dataTableDownloads = $this->getDownloads( true ); - $view->dataTableOutlinks = $this->getOutlinks( true ); - - echo $view->render(); + $view = Piwik_ViewDataTable::factory(); + $view->init( $this->pluginName, + __FUNCTION__, + 'Actions.getPageUrls', + 'getPageUrlsSubDataTable' ); + $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageURL')); + $this->configureViewActions($view); + return $this->renderView($view, $fetch); } - public function getActions($fetch = false) + public function getPageUrlsSubDataTable($fetch = false) { $view = Piwik_ViewDataTable::factory(); $view->init( $this->pluginName, __FUNCTION__, - 'Actions.getActions', - 'getActionsSubDataTable' ); + 'Actions.getPageUrls', + 'getActionsSubDataTable' ); $this->configureViewActions($view); + return $this->renderView($view, $fetch); + } + + public function getPageTitles($fetch = false) + { + $view = Piwik_ViewDataTable::factory(); + $view->init( $this->pluginName, + __FUNCTION__, + 'Actions.getPageTitles', + 'getPageTitlesSubDataTable' ); $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageName')); + $this->configureViewActions($view); return $this->renderView($view, $fetch); } - - public function getActionsSubDataTable($fetch = false) + + public function getPageTitlesSubDataTable($fetch = false) { $view = Piwik_ViewDataTable::factory(); - $view->init( $this->pluginName, + $view->init( $this->pluginName, __FUNCTION__, - 'Actions.getActions', - 'getActionsSubDataTable' ); + 'Actions.getPageTitles', + 'getPageTitlesSubDataTable' ); $this->configureViewActions($view); return $this->renderView($view, $fetch); } - + public function getDownloads($fetch = false) { $view = Piwik_ViewDataTable::factory(); @@ -76,7 +87,7 @@ class Piwik_Actions_Controller extends Piwik_Controller $view->disableSearchBox(); return $this->renderView($view, $fetch); } - + public function getOutlinks($fetch = false) { $view = Piwik_ViewDataTable::factory(); @@ -101,7 +112,7 @@ class Piwik_Actions_Controller extends Piwik_Controller $view->disableSearchBox(); return $this->renderView($view, $fetch); } - + protected function configureViewActions($view) { $view->setTemplate('CoreHome/templates/datatable_actions.tpl'); @@ -123,7 +134,6 @@ class Piwik_Actions_Controller extends Piwik_Controller $view->setLimit( 100 ); $view->setColumnsToDisplay( array('label','nb_hits','nb_visits') ); - $view->setColumnTranslation('label', Piwik_Translate('Actions_ColumnPageName')); $view->setColumnTranslation('nb_hits', Piwik_Translate('General_ColumnPageviews')); $view->setColumnTranslation('nb_visits', Piwik_Translate('General_ColumnUniquePageviews')); @@ -171,7 +181,7 @@ class Piwik_Actions_Controller extends Piwik_Controller $view->disableExcludeLowPopulation(); $view->setLimit( 15 ); } - + protected function getArrayFromRecursiveDataTable( $dataTable, $depth = 0 ) { $table = array(); diff --git a/plugins/Actions/tests/Actions.test.php b/plugins/Actions/tests/Actions.test.php new file mode 100644 index 0000000000000000000000000000000000000000..08f11e81ef721b82e04bf0ef8451201217f40cb4 --- /dev/null +++ b/plugins/Actions/tests/Actions.test.php @@ -0,0 +1,92 @@ +<?php +if(!defined("PIWIK_PATH_TEST_TO_ROOT")) { + define('PIWIK_PATH_TEST_TO_ROOT', getcwd().'/../../..'); +} +if(!defined('PIWIK_CONFIG_TEST_INCLUDED')) +{ + require_once PIWIK_PATH_TEST_TO_ROOT . "/tests/config_test.php"; +} + +require_once 'Actions/Actions.php'; +require_once 'Tracker/Action.php'; +require_once 'Tracker/Config.php'; + +class Test_Piwik_Actions extends UnitTestCase +{ + function test_getActionExplodedNames() + { + $userFile = PIWIK_INCLUDE_PATH . '/tests/resources/plugins/Actions/Actions.config.ini.php'; + + Piwik::createConfigObject($userFile); + + $action = new Test_Piwik_Actions_getActionExplodedNames(); + + $tests = array( + array( + 'params' => array( 'name' => 'http://example.org/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + 'expected' => array('index' ), + ), + array( + 'params' => array( 'name' => 'http://example.org/path/', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + 'expected' => array( 'path', 'index' ), + ), + array( + 'params' => array( 'name' => 'http://example.org/test/path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + 'expected' => array( 'test', 'path' ), + ), + array( + 'params' => array( 'name' => 'Test / Path', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + 'expected' => array( 'Test', 'Path' ), + ), + array( + 'params' => array( 'name' => ' Test trim ', 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + 'expected' => array( 'Test trim' ), + ), + array( + 'params' => array( 'name' => 'Category / Subcategory', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME), + 'expected' => array( 'Category', 'Subcategory' ), + ), + array( + 'params' => array( 'name' => '/path/index.php?var=test', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME), + 'expected' => array( 'path', 'index.php?var=test' ), + ), + array( + 'params' => array( 'name' => 'http://example.org/path/Default.aspx#anchor', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME), + 'expected' => array( 'path', 'Default.aspx' ), + ), + array( + 'params' => array( 'name' => '', 'type' => Piwik_Tracker_Action::TYPE_ACTION_NAME), + 'expected' => array( 'index' ), + ), + array( + 'params' => array( 'name' => 'http://example.org/download.zip', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD), + 'expected' => array( 'example.org', '/download.zip' ), + ), + array( + 'params' => array( 'name' => 'http://example.org/download/1/', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD), + 'expected' => array( 'example.org', '/download/1/' ), + ), + array( + 'params' => array( 'name' => 'http://example.org/link', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK), + 'expected' => array( 'example.org', '/link' ), + ), + array( + 'params' => array( 'name' => 'http://example.org/some/path/', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK), + 'expected' => array( 'example.org', '/some/path/' ), + ), + + ); + foreach($tests as $test) { + $params = $test['params']; + $expected = $test['expected']; + $this->assertEqual($action->public_getActionExplodedNames($params['name'],$params['type']), $expected); + } + } +} + +class Test_Piwik_Actions_getActionExplodedNames extends Piwik_Actions { + public function public_getActionExplodedNames($name, $type) + { + return self::getActionExplodedNames($name, $type); + } +} \ No newline at end of file diff --git a/tests/core/Tracker/Action.test.php b/tests/core/Tracker/Action.test.php index 5f6b510f9db15dbcf8ef7214feadfa3641008d62..2cc1fba21616006f73244ea87c49684532a10602 100644 --- a/tests/core/Tracker/Action.test.php +++ b/tests/core/Tracker/Action.test.php @@ -23,29 +23,53 @@ class Test_Piwik_TrackerAction extends UnitTestCase // outlinks array( 'request' => array( 'link' => 'http://example.org'), - 'expected' => array( 'name' => 'http://example.org', + 'expected' => array( 'name' => null, + 'url' => 'http://example.org', + 'type' => Piwik_Tracker_Action::TYPE_OUTLINK), + ), + // outlinks with custom name + array( + 'request' => array( 'link' => 'http://example.org', 'action_name' => 'Example.org'), + 'expected' => array( 'name' => 'Example.org', 'url' => 'http://example.org', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK), ), // keep the case in urls, but trim array( 'request' => array( 'link' => ' http://example.org/Category/Test/ '), - 'expected' => array( 'name' => 'http://example.org/Category/Test/', + 'expected' => array( 'name' => null, 'url' => 'http://example.org/Category/Test/', 'type' => Piwik_Tracker_Action::TYPE_OUTLINK), ), - + + // trim the custom name + array( + 'request' => array( 'link' => ' http://example.org/Category/Test/ ', 'action_name' => ' Example dot org '), + 'expected' => array( 'name' => 'Example dot org', + 'url' => 'http://example.org/Category/Test/', + 'type' => Piwik_Tracker_Action::TYPE_OUTLINK), + ), + // downloads array( 'request' => array( 'download' => 'http://example.org/*$test.zip'), - 'expected' => array( 'name' => 'http://example.org/*$test.zip', + 'expected' => array( 'name' => null, 'url' => 'http://example.org/*$test.zip', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD), ), + + // downloads with custom name + array( + 'request' => array( 'download' => 'http://example.org/*$test.zip', 'action_name' => 'Download test.zip'), + 'expected' => array( 'name' => 'Download test.zip', + 'url' => 'http://example.org/*$test.zip', + 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD), + ), + // keep the case and multiple / in urls array( 'request' => array( 'download' => 'http://example.org/CATEGORY/test///test.pdf'), - 'expected' => array( 'name' => 'http://example.org/CATEGORY/test///test.pdf', + 'expected' => array( 'name' => null, 'url' => 'http://example.org/CATEGORY/test///test.pdf', 'type' => Piwik_Tracker_Action::TYPE_DOWNLOAD), ), @@ -53,43 +77,62 @@ class Test_Piwik_TrackerAction extends UnitTestCase // page view array( 'request' => array( 'url' => 'http://example.org/'), - 'expected' => array( 'name' => 'index', + 'expected' => array( 'name' => null, + 'url' => 'http://example.org/', + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + ), + array( + 'request' => array( 'url' => 'http://example.org/', 'action_name' => 'Example.org Website'), + 'expected' => array( 'name' => 'Example.org Website', 'url' => 'http://example.org/', - 'type' => Piwik_Tracker_Action::TYPE_ACTION), + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), ), array( 'request' => array( 'url' => 'http://example.org/CATEGORY/'), - 'expected' => array( 'name' => 'CATEGORY/index', + 'expected' => array( 'name' => null, 'url' => 'http://example.org/CATEGORY/', - 'type' => Piwik_Tracker_Action::TYPE_ACTION), + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + ), + array( + 'request' => array( 'url' => 'http://example.org/CATEGORY/TEST', 'action_name' => 'Example.org / Category / test /'), + 'expected' => array( 'name' => 'Example.org/Category/test', + 'url' => 'http://example.org/CATEGORY/TEST', + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), + ), + + // empty request + array( + 'request' => array(), + 'expected' => array( 'name' => null, 'url' => '/', + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), ), array( 'request' => array( 'url' => 'http://example.org/category/', 'action_name' => 'custom name with/one delimiter/two delimiters/'), 'expected' => array( 'name' => 'custom name with/one delimiter/two delimiters', 'url' => 'http://example.org/category/', - 'type' => Piwik_Tracker_Action::TYPE_ACTION), + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), ), array( 'request' => array( 'url' => 'http://example.org/category/', 'action_name' => 'http://custom action name look like url/'), 'expected' => array( 'name' => 'http:/custom action name look like url', 'url' => 'http://example.org/category/', - 'type' => Piwik_Tracker_Action::TYPE_ACTION), + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), ), // testing: delete tab, trimmed, not strtolowered array( 'request' => array( 'url' => "http://example.org/category/test///test wOw "), - 'expected' => array( 'name' => 'category/test/test wOw', + 'expected' => array( 'name' => null, 'url' => 'http://example.org/category/test///test wOw', - 'type' => Piwik_Tracker_Action::TYPE_ACTION), + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), ), // testing: inclusion of zero values in action name array( 'request' => array( 'url' => "http://example.org/category/1/0/t/test"), - 'expected' => array( 'name' => 'category/1/0/t/test', + 'expected' => array( 'name' => null, 'url' => 'http://example.org/category/1/0/t/test', - 'type' => Piwik_Tracker_Action::TYPE_ACTION), + 'type' => Piwik_Tracker_Action::TYPE_ACTION_URL), ), ); foreach($tests as $test) { diff --git a/tests/resources/Tracker/Action.config.ini.php b/tests/resources/Tracker/Action.config.ini.php index a5f8cbd30aba3daef96a581ee2ed8b9254369291..2f3bf5d2c2eb865f1e8503db3eb220fcf3b9d208 100644 --- a/tests/resources/Tracker/Action.config.ini.php +++ b/tests/resources/Tracker/Action.config.ini.php @@ -1,2 +1,4 @@ [Tracker] action_category_delimiter = / +default_action_name = index +default_action_url = / \ No newline at end of file diff --git a/tests/resources/plugins/Actions/Actions.config.ini.php b/tests/resources/plugins/Actions/Actions.config.ini.php new file mode 100644 index 0000000000000000000000000000000000000000..f1e96d26d6799a473021da6f72a5b47fedf20f78 --- /dev/null +++ b/tests/resources/plugins/Actions/Actions.config.ini.php @@ -0,0 +1,5 @@ +[General] +action_category_delimiter = / + +[Tracker] +default_action_name = index