diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index 1c2d9a62a43..ff830133e77 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -132,6 +132,7 @@ public function getExampleUrl($class, $methodName, $parametersToSet = array())
'lastMinutes' => '30',
'abandonedCarts' => '0',
'ip' => '194.57.91.215',
+// 'segmentName' => 'browserCode',
);
foreach ($parametersToSet as $name => $value) {
@@ -231,4 +232,5 @@ public function getParametersString($class, $name)
$sParameters = implode(", ", $asParameters);
return "($sParameters)";
}
+
}
diff --git a/core/API/Request.php b/core/API/Request.php
index 1ce66abf379..b30fd663c16 100644
--- a/core/API/Request.php
+++ b/core/API/Request.php
@@ -111,7 +111,7 @@ private function sanitizeRequest()
* It then calls the API Proxy which will call the requested method.
*
* @throws Piwik_FrontController_PluginDeactivatedException
- * @return mixed The data resulting from the API call
+ * @return Piwik_DataTable|mixed The data resulting from the API call
*/
public function process()
{
diff --git a/core/DataTable.php b/core/DataTable.php
index 1784802799a..b5e2064980e 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -675,6 +675,26 @@ public function getColumn($name)
return $columnValues;
}
+ /**
+ * Returns the array containing all rows values of all columns which name starts with $name
+ *
+ * @param $name
+ * @return array
+ */
+ public function getColumnsStartingWith($name)
+ {
+ $columnValues = array();
+ foreach ($this->getRows() as $row) {
+ $columns = $row->getColumns();
+ foreach($columns as $column => $value) {
+ if(strpos($column, $name) === 0) {
+ $columnValues[] = $row->getColumn($column);
+ }
+ }
+ }
+ return $columnValues;
+ }
+
/**
* Returns an array containing the rows Metadata values
*
diff --git a/core/DataTable/Filter/ColumnDelete.php b/core/DataTable/Filter/ColumnDelete.php
index 3ac3f5b244a..709d56a02e1 100644
--- a/core/DataTable/Filter/ColumnDelete.php
+++ b/core/DataTable/Filter/ColumnDelete.php
@@ -33,6 +33,14 @@ class Piwik_DataTable_Filter_ColumnDelete extends Piwik_DataTable_Filter
*/
private $columnsToKeep;
+ /**
+ * Hack: when specifying "showColumns", sometimes we'd like to also keep columns that "look" like a given column,
+ * without manually specifying all these columns (which may not be possible if column names are generated dynamically)
+ *
+ * Column will be kept, if they match any name in the $columnsToKeep, or if they look like anyColumnToKeep__anythingHere
+ */
+ const APPEND_TO_COLUMN_NAME_TO_KEEP = '__';
+
/**
* Delete the column, only if the value was zero
*
@@ -101,8 +109,18 @@ public function filter($table)
if (!empty($this->columnsToKeep)) {
foreach ($table->getRows() as $row) {
foreach ($row->getColumns() as $name => $value) {
- // label cannot be removed via whitelisting
- if ($name != 'label' && !isset($this->columnsToKeep[$name])) {
+
+ $keep = false;
+ // @see self::APPEND_TO_COLUMN_NAME_TO_KEEP
+ foreach($this->columnsToKeep as $nameKeep => $true) {
+ if(strpos($name, $nameKeep . self::APPEND_TO_COLUMN_NAME_TO_KEEP) === 0) {
+ $keep = true;
+ }
+ }
+
+ if (!$keep
+ && $name != 'label' // label cannot be removed via whitelisting
+ && !isset($this->columnsToKeep[$name])) {
$row->deleteColumn($name);
}
}
diff --git a/core/Segment.php b/core/Segment.php
index 7802ba440d3..6ba00c64781 100644
--- a/core/Segment.php
+++ b/core/Segment.php
@@ -58,11 +58,6 @@ public function __construct($string, $idSites)
$segment->setSubExpressionsAfterCleanup($cleanedExpressions);
}
- public function getPrettyString()
- {
- //@TODO segment.getPrettyString
- }
-
public function isEmpty()
{
return empty($this->string);
@@ -71,16 +66,6 @@ public function isEmpty()
protected $availableSegments = array();
protected $segmentsHumanReadable = '';
- private function getUniqueSqlFields()
- {
- $expressions = $this->segment->parsedSubExpressions;
- $uniqueFields = array();
- foreach ($expressions as $expression) {
- $uniqueFields[] = $expression[Piwik_SegmentExpression::INDEX_OPERAND][0];
- }
- return $uniqueFields;
- }
-
protected function getCleanedExpression($expression)
{
if (empty($this->availableSegments)) {
@@ -114,7 +99,7 @@ protected function getCleanedExpression($expression)
if (isset($segment['sqlFilter'])
&& !empty($segment['sqlFilter'])
) {
- $value = call_user_func($segment['sqlFilter'], $value, $segment['sqlSegment'], $matchType);
+ $value = call_user_func($segment['sqlFilter'], $value, $segment['sqlSegment'], $matchType, $name);
// sqlFilter-callbacks might return arrays for more complex cases
// e.g. see Piwik_Actions::getIdActionFromSegment()
diff --git a/lang/en.php b/lang/en.php
index bdba2286c3e..4063febf0b5 100644
--- a/lang/en.php
+++ b/lang/en.php
@@ -413,6 +413,7 @@
'Actions_ColumnSearchCategory' => 'Search Category',
'Actions_ColumnSearchResultsCount' => 'Search Results Count',
'Actions_ColumnSearchKeyword' => 'Keyword',
+ 'Actions_SiteSearchKeyword' => 'Keyword (Site Search)',
'Actions_ColumnClickedURL' => 'Clicked URL',
'Actions_ColumnDownloadURL' => 'Download URL',
'Actions_ColumnEntryPageURL' => 'Entry Page URL',
diff --git a/libs/PiwikTracker/PiwikTracker.php b/libs/PiwikTracker/PiwikTracker.php
index 97e5acbc82a..7f609317ca1 100644
--- a/libs/PiwikTracker/PiwikTracker.php
+++ b/libs/PiwikTracker/PiwikTracker.php
@@ -839,7 +839,7 @@ public function setBrowserHasCookies($bool)
*/
public function setDebugStringAppend($string)
{
- $this->DEBUG_APPEND_URL = $string;
+ $this->DEBUG_APPEND_URL = '&' . $string;
}
/**
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 5ca4e2156c1..9c69be2d5c7 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -78,7 +78,8 @@ public function getCssFiles($notification)
*
the list of metrics that will be returned by each method, along with their human readable name, via "getDefaultMetrics" and "getDefaultProcessedMetrics"
* the list of segments metadata supported by all functions that have a 'segment' parameter
* the (truly magic) method "getProcessedReport" will return a human readable version of any other report, and include the processed metrics such as
- * conversion rate, time on site, etc. which are not directly available in other methods.
+ * conversion rate, time on site, etc. which are not directly available in other methods.
+ * the method "getSuggestedValuesForSegment" returns top suggested values for a particular segment. It uses the Live.getLastVisitsDetails API to fetch the most recently used values, and will return the most often used values first.
*
* The Metadata API is for example used by the Piwik Mobile App to automatically display all Piwik reports, with translated report & columns names and nicely formatted values.
* More information on the Metadata API documentation page
@@ -912,7 +913,7 @@ private function hideShowMetrics($columns, $emptyColumns = array())
foreach ($columns as $name => $ignore) {
// if the current column should not be kept, remove it
$idx = array_search($name, $columnsToKeep);
- if ($idx === FALSE) // if $name is not in $columnsToKeep
+ if ($idx === false) // if $name is not in $columnsToKeep
{
unset($columns[$name]);
}
@@ -1625,4 +1626,65 @@ public function getBulkRequest($urls)
}
return $result;
}
+
+ /**
+ * Given a segment, will return a list of the most used values for this particular segment.
+ * @param $segmentName
+ * @param $idSite
+ * @throws Exception
+ */
+ public function getSuggestedValuesForSegment($segmentName, $idSite)
+ {
+ Piwik::checkUserHasViewAccess($idSite);
+ $maxSuggestionsToReturn = 30;
+ $segmentsMetadata = $this->getSegmentsMetadata($idSite, $_hideImplementationData = false);
+
+ $segmentFound = false;
+ foreach($segmentsMetadata as $segmentMetadata) {
+ if($segmentMetadata['segment'] == $segmentName) {
+ $segmentFound = $segmentMetadata;
+ break;
+ }
+ }
+ if(empty($segmentFound)) {
+ throw new Exception("Requested segment not found.");
+ }
+
+ $startDate = Piwik_Date::now()->subDay(60)->toString();
+
+ // we know which SQL field this segment matches to: call the LIVE api to get last 1000 visitors values
+ $request = new Piwik_API_Request("method=Live.getLastVisitsDetails
+ &idSite=$idSite
+ &period=range
+ &date=$startDate,today
+ &filter_limit=10000
+ &format=original
+ &serialize=0
+ &flat=1
+ &segment=");
+ $table = $request->process();
+ if(empty($table)) {
+ throw new Exception("There was no data to suggest for $segmentName");
+ }
+
+ // Cleanup data to return the top suggested (non empty) labels for this segment
+ $values = $table->getColumn($segmentName);
+
+ // Select also flattened keys (custom variables "page" scope, page URLs for one visit, page titles for one visit)
+ $valuesBis = $table->getColumnsStartingWith($segmentName . Piwik_DataTable_Filter_ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP);
+ $values = array_merge($values, $valuesBis);
+
+ // remove false values (while keeping zeros)
+ $values = array_filter( $values, 'strlen' );
+
+ // we have a list of all values. let's show the most frequently used first.
+ $values = array_count_values( $values );
+ arsort($values);
+ $values = array_keys($values);
+
+ $values = array_map( array('Piwik_Common', 'unsanitizeInputValue'), $values);
+
+ $values = array_slice($values, 0, $maxSuggestionsToReturn);
+ return $values;
+ }
}
diff --git a/plugins/Actions/Actions.php b/plugins/Actions/Actions.php
index 097d2e3dee0..8a9be015d73 100644
--- a/plugins/Actions/Actions.php
+++ b/plugins/Actions/Actions.php
@@ -103,7 +103,14 @@ public function getSegmentsMetadata($notification)
'sqlSegment' => 'log_link_visit_action.idaction_name',
'sqlFilter' => $sqlFilter,
);
- // TODO here could add keyword segment and hack $sqlFilter to make it select the right idaction
+ $segments[] = array(
+ 'type' => 'dimension',
+ 'category' => 'Actions_Actions',
+ 'name' => 'Actions_SiteSearchKeyword',
+ 'segment' => 'siteSearchKeyword',
+ 'sqlSegment' => 'log_link_visit_action.idaction_name',
+ 'sqlFilter' => $sqlFilter,
+ );
}
/**
@@ -113,30 +120,29 @@ public function getSegmentsMetadata($notification)
* Usually, these callbacks only return a value that should be compared to the
* column in the database. In this case, that doesn't work since multiple IDs
* can match an expression (e.g. "pageUrl=@foo").
- * @param string $string
+ * @param string $valueToMatch
* @param string $sqlField
* @param string $matchType
* @throws Exception
* @return array|int|string
*/
- public function getIdActionFromSegment($string, $sqlField, $matchType = '==')
+ public function getIdActionFromSegment($valueToMatch, $sqlField, $matchType, $segmentName)
{
- // Field is visit_*_idaction_url or visit_*_idaction_name
- $actionType = strpos($sqlField, '_name') === false
- ? Piwik_Tracker_Action::TYPE_ACTION_URL
- : Piwik_Tracker_Action::TYPE_ACTION_NAME;
+ $actionType = $this->guessActionTypeFromSegment($segmentName);
if ($actionType == Piwik_Tracker_Action::TYPE_ACTION_URL) {
// for urls trim protocol and www because it is not recorded in the db
- $string = preg_replace('@^http[s]?://(www\.)?@i', '', $string);
+ $valueToMatch = preg_replace('@^http[s]?://(www\.)?@i', '', $valueToMatch);
}
+ $valueToMatch = Piwik_Common::sanitizeInputValue(Piwik_Common::unsanitizeInputValue($valueToMatch));
+
// exact matches work by returning the id directly
if ($matchType == Piwik_SegmentExpression::MATCH_EQUAL
|| $matchType == Piwik_SegmentExpression::MATCH_NOT_EQUAL
) {
$sql = Piwik_Tracker_Action::getSqlSelectActionId();
- $bind = array($string, $string, $actionType);
+ $bind = array($valueToMatch, $valueToMatch, $actionType);
$idAction = Piwik_FetchOne($sql, $bind);
// if the action is not found, we hack -100 to ensure it tries to match against an integer
// otherwise binding idaction_name to "false" returns some rows for some reasons (in case &segment=pageTitle==Větrnásssssss)
@@ -150,23 +156,24 @@ public function getIdActionFromSegment($string, $sqlField, $matchType = '==')
// build the expression based on the match type
$sql = 'SELECT idaction FROM ' . Piwik_Common::prefixTable('log_action') . ' WHERE ';
+ $sqlMatchType = 'AND type = ' . $actionType;
switch ($matchType) {
case '=@':
// use concat to make sure, no %s occurs because some plugins use %s in their sql
- $sql .= '( name LIKE CONCAT(\'%\', ?, \'%\') AND type = ' . $actionType . ' )';
+ $sql .= '( name LIKE CONCAT(\'%\', ?, \'%\') ' . $sqlMatchType . ' )';
break;
case '!@':
- $sql .= '( name NOT LIKE CONCAT(\'%\', ?, \'%\') AND type = ' . $actionType . ' )';
+ $sql .= '( name NOT LIKE CONCAT(\'%\', ?, \'%\') ' . $sqlMatchType . ' )';
break;
default:
- throw new Exception("This match type is not available for action-segments.");
+ throw new Exception("This match type $matchType is not available for action-segments.");
break;
}
return array(
// mark that the returned value is an sql-expression instead of a literal value
'SQL' => $sql,
- 'bind' => $string
+ 'bind' => $valueToMatch,
);
}
@@ -600,5 +607,26 @@ static protected function isCustomVariablesPluginsEnabled()
{
return Piwik_PluginsManager::getInstance()->isPluginActivated('CustomVariables');
}
+
+ /**
+ * @param $segmentName
+ * @return int
+ * @throws Exception
+ */
+ protected function guessActionTypeFromSegment($segmentName)
+ {
+ if (stripos($segmentName, 'pageurl') !== false) {
+ $actionType = Piwik_Tracker_Action::TYPE_ACTION_URL;
+ return $actionType;
+ } elseif (stripos($segmentName, 'pagetitle') !== false) {
+ $actionType = Piwik_Tracker_Action::TYPE_ACTION_NAME;
+ return $actionType;
+ } elseif (stripos($segmentName, 'sitesearch') !== false) {
+ $actionType = Piwik_Tracker_Action::TYPE_SITE_SEARCH;
+ return $actionType;
+ } else {
+ throw new Exception(" The segment $segmentName has an unexpected value.");
+ }
+ }
}
diff --git a/plugins/CoreUpdater/CoreUpdater.php b/plugins/CoreUpdater/CoreUpdater.php
index 30a28f76a62..865c3da4adb 100644
--- a/plugins/CoreUpdater/CoreUpdater.php
+++ b/plugins/CoreUpdater/CoreUpdater.php
@@ -69,7 +69,8 @@ function dispatch()
&& $action == 'saveLanguage')
) {
if (Piwik_FrontController::shouldRethrowException()) {
- throw new Exception("Piwik and/or some plugins have been upgraded to a new version. Please run the update process first. See documentation: http://piwik.org/docs/update/");
+ throw new Exception("Piwik and/or some plugins have been upgraded to a new version. \n".
+ "--> Please run the update process first. See documentation: http://piwik.org/docs/update/ \n");
} else {
Piwik::redirectToModule('CoreUpdater');
}
diff --git a/plugins/Live/API.php b/plugins/Live/API.php
index 919afb0682a..5cb3043839f 100644
--- a/plugins/Live/API.php
+++ b/plugins/Live/API.php
@@ -97,13 +97,15 @@ public function getCounters($idSite, $lastMinutes, $segment = false)
* @param int $visitorId
* @param int $idSite
* @param int $filter_limit
+ * @param bool $flat Whether to flatten the visitor details array
+ *
* @return Piwik_DataTable
*/
- public function getLastVisitsForVisitor($visitorId, $idSite, $filter_limit = 10)
+ public function getLastVisitsForVisitor($visitorId, $idSite, $filter_limit = 10, $flat = false)
{
Piwik::checkUserHasViewAccess($idSite);
$visitorDetails = $this->loadLastVisitorDetailsFromDatabase($idSite, $period = false, $date = false, $segment = false, $filter_limit, $filter_offset = false, $visitorId);
- $table = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite);
+ $table = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite, $flat);
return $table;
}
@@ -121,25 +123,23 @@ public function getLastVisitsForVisitor($visitorId, $idSite, $filter_limit = 10)
*
* @return Piwik_DataTable
*/
- public function getLastVisitsDetails($idSite, $period, $date, $segment = false, $filter_limit = false, $filter_offset = false, $minTimestamp = false)
+ public function getLastVisitsDetails($idSite, $period, $date, $segment = false, $filter_limit = false, $filter_offset = false, $minTimestamp = false, $flat = false)
{
if (empty($filter_limit)) {
$filter_limit = 10;
}
Piwik::checkUserHasViewAccess($idSite);
$visitorDetails = $this->loadLastVisitorDetailsFromDatabase($idSite, $period, $date, $segment, $filter_limit, $filter_offset, $visitorId = false, $minTimestamp);
- $dataTable = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite);
-
+ $dataTable = $this->getCleanedVisitorsFromDetails($visitorDetails, $idSite, $flat);
return $dataTable;
}
/**
* @deprecated
*/
-
public function getLastVisits($idSite, $filter_limit = 10, $minTimestamp = false)
{
- return $this->getLastVisitsDetails($idSite, $period = false, $date = false, $segment = false, $filter_limit, $filter_offset = false, $minTimestamp);
+ return $this->getLastVisitsDetails($idSite, $period = false, $date = false, $segment = false, $filter_limit, $filter_offset = false, $minTimestamp, $flat = false);
}
/**
@@ -147,9 +147,10 @@ public function getLastVisits($idSite, $filter_limit = 10, $minTimestamp = false
* as well as make the data human readable
* @param array $visitorDetails
* @param int $idSite
+ * @param bool $flat whether to flatten the array (eg. 'customVariables' names/values will appear in the root array rather than in 'customVariables' key
* @return Piwik_DataTable
*/
- private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
+ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite, $flat = false)
{
$actionsLimit = (int)Piwik_Config::getInstance()->General['visitor_log_maximum_actions_per_visit'];
@@ -200,6 +201,7 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
LEFT JOIN " . Piwik_Common::prefixTable('log_action') . " AS log_action_title
ON log_link_visit_action.idaction_name = log_action_title.idaction
WHERE log_link_visit_action.idvisit = ?
+ ORDER BY server_time ASC
LIMIT 0, $actionsLimit
";
$actionDetails = Piwik_FetchAll($sql, array($idvisit));
@@ -212,8 +214,8 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
$cvarKey = $actionDetail['custom_var_k' . $i];
$cvarKey = $this->getCustomVariablePrettyKey($cvarKey);
$customVariablesPage[$i] = array(
- 'customVariableName' . $i => $cvarKey,
- 'customVariableValue' . $i => $actionDetail['custom_var_v' . $i],
+ 'customVariablePageName' . $i => $cvarKey,
+ 'customVariablePageValue' . $i => $actionDetail['custom_var_v' . $i],
);
}
unset($actionDetail['custom_var_k' . $i]);
@@ -222,21 +224,30 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
if (!empty($customVariablesPage)) {
$actionDetail['customVariables'] = $customVariablesPage;
}
- // reconstruct url from prefix
+
+ // Reconstruct url from prefix
$actionDetail['url'] = Piwik_Tracker_Action::reconstructNormalizedUrl($actionDetail['url'], $actionDetail['url_prefix']);
unset($actionDetail['url_prefix']);
- // set the time spent for this action (which is the timeSpentRef of the next action)
+
+ // Set the time spent for this action (which is the timeSpentRef of the next action)
if (isset($actionDetails[$actionIdx + 1])) {
$actionDetail['timeSpent'] = $actionDetails[$actionIdx + 1]['timeSpentRef'];
$actionDetail['timeSpentPretty'] = Piwik::getPrettyTimeFromSeconds($actionDetail['timeSpent']);
}
unset($actionDetails[$actionIdx]['timeSpentRef']); // not needed after timeSpent is added
- // handle generation time
+
+ // Handle generation time
if ($actionDetail['custom_float'] > 0) {
$actionDetail['generationTime'] = Piwik::getPrettyTimeFromSeconds($actionDetail['custom_float'] / 1000);
}
unset($actionDetail['custom_float']);
+
+ // Handle Site Search
+ if($actionDetail['type'] == Piwik_Tracker_Action::TYPE_SITE_SEARCH) {
+ $actionDetail['siteSearchKeyword'] = $actionDetail['pageTitle'];
+ unset($actionDetail['pageTitle']);
+ }
}
// If the visitor converted a goal, we shall select all Goals
@@ -244,6 +255,7 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
SELECT
'goal' as type,
goal.name as goalName,
+ goal.idgoal as goalId,
goal.revenue as revenue,
log_conversion.idlink_va as goalPageId,
log_conversion.server_time as serverTimePretty,
@@ -256,6 +268,7 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
AND goal.deleted = 0
WHERE log_conversion.idvisit = ?
AND log_conversion.idgoal > 0
+ ORDER BY server_time ASC
LIMIT 0, $actionsLimit
";
$goalDetails = Piwik_FetchAll($sql, array($idvisit));
@@ -274,6 +287,7 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
FROM " . Piwik_Common::prefixTable('log_conversion') . " AS log_conversion
WHERE idvisit = ?
AND idgoal <= " . Piwik_Tracker_GoalManager::IDGOAL_ORDER . "
+ ORDER BY server_time ASC
LIMIT 0, $actionsLimit";
$ecommerceDetails = Piwik_FetchAll($sql, array($idvisit));
@@ -365,9 +379,13 @@ private function getCleanedVisitorsFromDetails($visitorDetails, $idSite)
// Convert datetimes to the site timezone
$dateTimeVisit = Piwik_Date::factory($details['serverTimePretty'], $timezone);
$details['serverTimePretty'] = $dateTimeVisit->getLocalized(Piwik_Translate('CoreHome_ShortDateFormat') . ' %time%');
+
}
$visitorDetailsArray['goalConversions'] = count($goalDetails);
+ if($flat) {
+ $visitorDetailsArray = $this->flattenVisitorDetailsArray($visitorDetailsArray);
+ }
$table->addRowFromArray(array(Piwik_DataTable_Row::COLUMNS => $visitorDetailsArray));
}
return $table;
@@ -385,6 +403,92 @@ private function getCustomVariablePrettyKey($key)
return $key;
}
+ /**
+ * The &flat=1 feature is used by API.getSuggestedValuesForSegment
+ *
+ * @param $visitorDetailsArray
+ * @return array
+ */
+ private function flattenVisitorDetailsArray($visitorDetailsArray)
+ {
+ // flatten visit custom variables
+ if (is_array($visitorDetailsArray['customVariables'])) {
+ foreach ($visitorDetailsArray['customVariables'] as $thisCustomVar) {
+ $visitorDetailsArray = array_merge($visitorDetailsArray, $thisCustomVar);
+ }
+ unset($visitorDetailsArray['customVariables']);
+ }
+
+ // flatten page views custom variables
+ $count = 1;
+ foreach ($visitorDetailsArray['actionDetails'] as $action) {
+ if (!empty($action['customVariables'])) {
+ foreach ($action['customVariables'] as $thisCustomVar) {
+ foreach ($thisCustomVar as $cvKey => $cvValue) {
+ $flattenedKeyName = $cvKey . Piwik_DataTable_Filter_ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count;
+ $visitorDetailsArray[$flattenedKeyName] = $cvValue;
+ $count++;
+ }
+ }
+ }
+ }
+ // Flatten Goals
+ $count = 1;
+ foreach($visitorDetailsArray['actionDetails'] as $action) {
+ if(!empty($action['goalId'])) {
+ $flattenedKeyName = 'visitConvertedGoalId' . Piwik_DataTable_Filter_ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count;
+ $visitorDetailsArray[$flattenedKeyName] = $action['goalId'];
+ $count++;
+ }
+ }
+
+ // Flatten Page Titles/URLs
+ $count = 1;
+ foreach($visitorDetailsArray['actionDetails'] as $action) {
+ if(!empty($action['url'])) {
+ $flattenedKeyName = 'pageUrl' . Piwik_DataTable_Filter_ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count;
+ $visitorDetailsArray[$flattenedKeyName] = $action['url'];
+ }
+
+ if(!empty($action['pageTitle'])) {
+ $flattenedKeyName = 'pageTitle' . Piwik_DataTable_Filter_ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count;
+ $visitorDetailsArray[$flattenedKeyName] = $action['pageTitle'];
+ }
+
+ if(!empty($action['siteSearchKeyword'])) {
+ $flattenedKeyName = 'siteSearchKeyword' . Piwik_DataTable_Filter_ColumnDelete::APPEND_TO_COLUMN_NAME_TO_KEEP . $count;
+ $visitorDetailsArray[$flattenedKeyName] = $action['siteSearchKeyword'];
+ }
+ $count++;
+ }
+
+ // Entry/exit pages
+ $firstAction = $lastAction = false;
+ foreach($visitorDetailsArray['actionDetails'] as $action) {
+ if($action['type'] == 'action') {
+ if(empty($firstAction)) {
+ $firstAction = $action;
+ }
+ $lastAction = $action;
+ }
+ }
+
+ if(!empty($firstAction['pageTitle'])) {
+ $visitorDetailsArray['entryPageTitle'] = $firstAction['pageTitle'];
+ }
+ if(!empty($firstAction['url'])) {
+ $visitorDetailsArray['entryPageUrl'] = $firstAction['url'];
+ }
+ if(!empty($lastAction['pageTitle'])) {
+ $visitorDetailsArray['exitPageTitle'] = $lastAction['pageTitle'];
+ }
+ if(!empty($lastAction['url'])) {
+ $visitorDetailsArray['exitPageUrl'] = $lastAction['url'];
+ }
+
+ return $visitorDetailsArray;
+ }
+
private function sortByServerTime($a, $b)
{
$ta = strtotime($a['serverTimePretty']);
diff --git a/plugins/Live/Visitor.php b/plugins/Live/Visitor.php
index aaa4e8a61da..45a3ec7f080 100644
--- a/plugins/Live/Visitor.php
+++ b/plugins/Live/Visitor.php
@@ -60,6 +60,8 @@ function getAllVisitorDetails()
// all time entries
'serverDate' => $this->getServerDate(),
'visitLocalTime' => $this->getVisitLocalTime(),
+ 'visitLocalHour' => $this->getVisitLocalHour(),
+ 'visitServerHour' => $this->getVisitServerHour(),
'firstActionTimestamp' => $this->getTimestampFirstAction(),
'lastActionTimestamp' => $this->getTimestampLastAction(),
'lastActionDateTime' => $this->getDateTimeLastAction(),
@@ -77,6 +79,7 @@ function getAllVisitorDetails()
'countryCode' => $this->getCountryCode(),
'countryFlag' => $this->getCountryFlag(),
'region' => $this->getRegionName(),
+ 'regionCode' => $this->getRegionCode(),
'city' => $this->getCityName(),
'location' => $this->getPrettyLocation(),
'latitude' => $this->getLatitude(),
@@ -93,12 +96,15 @@ function getAllVisitorDetails()
'referrerSearchEngineUrl' => $this->getSearchEngineUrl(),
'referrerSearchEngineIcon' => $this->getSearchEngineIcon(),
'operatingSystem' => $this->getOperatingSystem(),
+ 'operatingSystemCode' => $this->getOperatingSystemCode(),
'operatingSystemShortName' => $this->getOperatingSystemShortName(),
'operatingSystemIcon' => $this->getOperatingSystemIcon(),
'browserFamily' => $this->getBrowserFamily(),
'browserFamilyDescription' => $this->getBrowserFamilyDescription(),
'browserName' => $this->getBrowser(),
'browserIcon' => $this->getBrowserIcon(),
+ 'browserCode' => $this->getBrowserCode(),
+ 'browserVersion' => $this->getBrowserVersion(),
'screenType' => $this->getScreenType(),
'resolution' => $this->getResolution(),
'screenTypeIcon' => $this->getScreenTypeIcon(),
@@ -120,6 +126,16 @@ function getVisitLocalTime()
return $this->details['visitor_localtime'];
}
+ function getVisitServerHour()
+ {
+ return date('G', strtotime($this->details['visit_last_action_time']));
+ }
+
+ function getVisitLocalHour()
+ {
+ return date('G', strtotime('2012-12-21 ' . $this->details['visitor_localtime']));
+ }
+
function getVisitCount()
{
return $this->details['visitor_count_visits'];
@@ -249,7 +265,7 @@ function getCityName()
public function getRegionName()
{
- $region = $this->details['location_region'];
+ $region = $this->getRegionCode();
if ($region != '' && $region != Piwik_Tracker_Visit::UNKNOWN_CODE) {
return Piwik_UserCountry_LocationProvider_GeoIp::getRegionNameFromCodes(
$this->details['location_country'], $region);
@@ -257,6 +273,11 @@ public function getRegionName()
return null;
}
+ public function getRegionCode()
+ {
+ return $this->details['location_region'];
+ }
+
function getPrettyLocation()
{
$parts = array();
@@ -430,6 +451,11 @@ function getPluginIcons()
return null;
}
+ function getOperatingSystemCode()
+ {
+ return $this->details['config_os'];
+ }
+
function getOperatingSystem()
{
return Piwik_getOSLabel($this->details['config_os']);
@@ -455,6 +481,16 @@ function getBrowserFamily()
return Piwik_getBrowserFamily($this->details['config_browser_name']);
}
+ function getBrowserCode()
+ {
+ return $this->details['config_browser_name'];
+ }
+
+ function getBrowserVersion()
+ {
+ return $this->details['config_browser_version'];
+ }
+
function getBrowser()
{
return Piwik_getBrowserLabel($this->details['config_browser_name'] . ";" . $this->details['config_browser_version']);
diff --git a/plugins/Transitions/API.php b/plugins/Transitions/API.php
index 80afa16d945..4457d71046e 100644
--- a/plugins/Transitions/API.php
+++ b/plugins/Transitions/API.php
@@ -134,25 +134,25 @@ private function deriveIdAction($actionName, $actionType)
case 'url':
$originalActionName = $actionName;
$actionName = Piwik_Common::unsanitizeInputValue($actionName);
- $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url');
+ $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url', Piwik_SegmentExpression::MATCH_EQUAL, 'pageUrl');
if ($id < 0) {
// an example where this is needed is urls containing < or >
$actionName = $originalActionName;
- $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url');
+ $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_url', Piwik_SegmentExpression::MATCH_EQUALs, 'pageUrl');
}
return $id;
case 'title':
- $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_name');
+ $id = $actionsPlugin->getIdActionFromSegment($actionName, 'idaction_name', Piwik_SegmentExpression::MATCH_EQUAL, 'pageTitle');
if ($id < 0) {
- $unkown = Piwik_Actions_ArchivingHelper::getUnknownActionName(
+ $unknown = Piwik_Actions_ArchivingHelper::getUnknownActionName(
Piwik_Tracker_Action::TYPE_ACTION_NAME);
- if (trim($actionName) == trim($unkown)) {
- $id = $actionsPlugin->getIdActionFromSegment('', 'idaction_name');
+ if (trim($actionName) == trim($unknown)) {
+ $id = $actionsPlugin->getIdActionFromSegment('', 'idaction_name', Piwik_SegmentExpression::MATCH_EQUAL, 'pageTitle');
}
}
diff --git a/plugins/UserCountry/UserCountry.php b/plugins/UserCountry/UserCountry.php
index 20d2935248a..34003af178b 100644
--- a/plugins/UserCountry/UserCountry.php
+++ b/plugins/UserCountry/UserCountry.php
@@ -164,7 +164,7 @@ public function getSegmentsMetadata($notification)
'type' => 'dimension',
'category' => 'Visit Location',
'name' => Piwik_Translate('UserCountry_Country'),
- 'segment' => 'country',
+ 'segment' => 'countryCode',
'sqlSegment' => 'log_visit.location_country',
'acceptedValues' => 'de, us, fr, in, es, etc.',
);
@@ -172,7 +172,7 @@ public function getSegmentsMetadata($notification)
'type' => 'dimension',
'category' => 'Visit Location',
'name' => Piwik_Translate('UserCountry_Continent'),
- 'segment' => 'continent',
+ 'segment' => 'continentCode',
'sqlSegment' => 'log_visit.location_country',
'acceptedValues' => 'eur, asi, amc, amn, ams, afr, ant, oce',
'sqlFilter' => array('Piwik_UserCountry', 'getCountriesForContinent'),
@@ -181,7 +181,7 @@ public function getSegmentsMetadata($notification)
'type' => 'dimension',
'category' => 'Visit Location',
'name' => Piwik_Translate('UserCountry_Region'),
- 'segment' => 'region',
+ 'segment' => 'regionCode',
'sqlSegment' => 'log_visit.location_region',
'acceptedValues' => '01 02, OR, P8, etc.
eg. region=A1;country=fr',
);
@@ -197,7 +197,7 @@ public function getSegmentsMetadata($notification)
'type' => 'dimension',
'category' => 'Visit Location',
'name' => Piwik_Translate('UserCountry_Latitude'),
- 'segment' => 'lat',
+ 'segment' => 'latitude',
'sqlSegment' => 'log_visit.location_latitude',
'acceptedValues' => '-33.578, 40.830, etc.
You can select visitors within a lat/long range using &segment=lat>X;lat<Y;long>M;long<N.',
);
@@ -205,7 +205,7 @@ public function getSegmentsMetadata($notification)
'type' => 'dimension',
'category' => 'Visit Location',
'name' => Piwik_Translate('UserCountry_Longitude'),
- 'segment' => 'long',
+ 'segment' => 'longitude',
'sqlSegment' => 'log_visit.location_longitude',
'acceptedValues' => '-70.664, 14.326, etc.',
);
@@ -489,9 +489,8 @@ private function setLongCityRegionId(&$row)
*/
public static function getCountriesForContinent($continent)
{
- $continent = strtolower($continent);
-
$result = array();
+ $continent = strtolower($continent);
foreach (Piwik_Common::getCountriesList() as $countryCode => $continentCode) {
if ($continent == $continentCode) {
$result[] = $countryCode;
diff --git a/plugins/UserSettings/UserSettings.php b/plugins/UserSettings/UserSettings.php
index d46b952c567..90be882806a 100644
--- a/plugins/UserSettings/UserSettings.php
+++ b/plugins/UserSettings/UserSettings.php
@@ -59,7 +59,7 @@ public function getInformation()
'UserSettings',
'getBrowser',
'UserSettings_ColumnBrowser',
- 'browserName',
+ 'browserCode',
'log_visit.config_browser_name',
'FF, IE, CH, SF, OP, etc.',
null,),
@@ -110,7 +110,7 @@ public function getInformation()
'UserSettings',
'getOS',
'UserSettings_ColumnOperatingSystem',
- 'operatingSystem',
+ 'operatingSystemCode',
'log_visit.config_os',
'WXP, WI7, MAC, LIN, AND, IPD, etc.',
null,),
diff --git a/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php b/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
index bd7983cdf6e..7c2c0e598c9 100644
--- a/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
+++ b/tests/PHPUnit/Core/PluginsFunctions/WidgetsListTest.php
@@ -24,9 +24,7 @@ public function testGet()
$_GET['idSite'] = 1;
- $pluginsManager = Piwik_PluginsManager::getInstance();
- $pluginsToLoad = Piwik_Config::getInstance()->Plugins['Plugins'];
- $pluginsManager->loadPlugins($pluginsToLoad);
+ IntegrationTestCase::loadAllPlugins();
Piwik_WidgetsList::_reset();
$widgets = Piwik_GetWidgetsList();
@@ -52,6 +50,7 @@ public function testGet()
foreach ($numberOfWidgets AS $category => $widgetCount) {
$this->assertEquals($widgetCount, count($widgets[$category]), sprintf("Widget: %s", $category));
}
+ IntegrationTestCase::unloadAllPlugins();
}
/**
@@ -132,4 +131,5 @@ public function testGetWithGoalsAndEcommerce()
}
}
+
}
diff --git a/tests/PHPUnit/Core/SegmentTest.php b/tests/PHPUnit/Core/SegmentTest.php
index 2d021ea65cc..35da2a58a32 100644
--- a/tests/PHPUnit/Core/SegmentTest.php
+++ b/tests/PHPUnit/Core/SegmentTest.php
@@ -44,17 +44,17 @@ public function getCommonTestData()
{
return array(
// Normal segment
- array('country==France', array(
+ array('countryCode==France', array(
'where' => ' log_visit.location_country = ? ',
'bind' => array('France'))),
// unescape the comma please
- array('country==a\,==', array(
+ array('countryCode==a\,==', array(
'where' => ' log_visit.location_country = ? ',
'bind' => array('a,=='))),
// AND, with 2 values rewrites
- array('country==a;visitorType!=returning;visitorType==new', array(
+ array('countryCode==a;visitorType!=returning;visitorType==new', array(
'where' => ' log_visit.location_country = ? AND log_visit.visitor_returning <> ? AND log_visit.visitor_returning = ? ',
'bind' => array('a', '1', '0'))),
diff --git a/tests/PHPUnit/DatabaseTestCase.php b/tests/PHPUnit/DatabaseTestCase.php
index 2b0627f251b..9f91c7910c5 100644
--- a/tests/PHPUnit/DatabaseTestCase.php
+++ b/tests/PHPUnit/DatabaseTestCase.php
@@ -15,6 +15,7 @@
*/
class DatabaseTestCase extends PHPUnit_Framework_TestCase
{
+
/**
* Setup the database and create the base tables for all tests
*/
@@ -58,14 +59,7 @@ public function setUp()
public function tearDown()
{
parent::tearDown();
- try {
- $plugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins();
- foreach ($plugins AS $plugin) {
- $plugin->uninstall();
- }
- Piwik_PluginsManager::getInstance()->unloadPlugins();
- } catch (Exception $e) {
- }
+ IntegrationTestCase::unloadAllPlugins();
Piwik::dropDatabase();
Piwik_DataTable_Manager::getInstance()->deleteAll();
Piwik_Option::getInstance()->clearCache();
@@ -76,4 +70,5 @@ public function tearDown()
Piwik_TablePartitioning::$tablesAlreadyInstalled = null;
Zend_Registry::_unsetInstance();
}
+
}
diff --git a/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php b/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php
index 33f2f7006cc..88cd783ae12 100644
--- a/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php
+++ b/tests/PHPUnit/Fixtures/ManyVisitsWithGeoIP.php
@@ -30,6 +30,9 @@ class Test_Piwik_Fixture_ManyVisitsWithGeoIP extends Test_Piwik_BaseFixture
'103.29.196.229', // in Indonesia (Bali), (only Indonesia will show up)
);
+ protected $idGoal;
+ protected $idGoal2;
+
public function setUp()
{
$this->setUpWebsitesAndGoals();
@@ -58,7 +61,8 @@ public function tearDown()
private function setUpWebsitesAndGoals()
{
self::createWebsite($this->dateTime, 0, "Site 1");
- Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
+ $this->idGoal = Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5);
+ $this->idGoal2 = Piwik_Goals_API::getInstance()->addGoal($this->idSite, 'two', 'url', 'xxxxxxxxxxxxx', 'contains', false, 5);
}
private function trackVisits($visitorCount, $setIp = false, $useLocal = true, $doBulk = false)
@@ -73,7 +77,7 @@ private function trackVisits($visitorCount, $setIp = false, $useLocal = true, $d
$t->setTokenAuth(self::getTokenAuth());
}
for ($i = 0; $i != $visitorCount; ++$i) {
- $t->setNewVisitorId();
+ $t->setVisitorId( substr(md5($i + 1000), 0, $t::LENGTH_VISITOR_ID));
if ($setIp) {
$t->setIp(current($this->ips));
next($this->ips);
@@ -85,7 +89,12 @@ private function trackVisits($visitorCount, $setIp = false, $useLocal = true, $d
$date = Piwik_Date::factory($dateTime)->addDay($i);
$t->setForceVisitDateTime($date->getDatetime());
$t->setUrl("http://piwik.net/grue/lair");
- $r = $t->doTrackPageView('It\'s pitch black...');
+ $t->setCustomVariable(1, 'Cvar 1 name', 'Cvar1 value is ' .$i , 'visit');
+ $t->setCustomVariable(5, 'Cvar 5 name', 'Cvar5 value is ' .$i , 'visit');
+ $t->setCustomVariable(2, 'Cvar 2 PAGE name', 'Cvar2 PAGE value is ' .$i, 'page');
+ $t->setCustomVariable(5, 'Cvar 5 PAGE name', 'Cvar5 PAGE value is ' .$i, 'page');
+
+ $r = $t->doTrackPageView('It\'s