diff --git a/core/Archive/Single.php b/core/Archive/Single.php index 8bbd96f7dafb9851fdd4b6d21f3b7749f217b3e9..a8ac2f1dff74a6de0b11d20a929c716f001459c2 100644 --- a/core/Archive/Single.php +++ b/core/Archive/Single.php @@ -1,19 +1,19 @@ <?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 Piwik */ /** - * Piwik_Archive_Single is used to store the data of a single archive, - * for example the statistics for the 'day' '2008-02-21' for the website idSite '2' + * Piwik_Archive_Single is used to store the data of a single archive, + * for example the statistics for the 'day' '2008-02-21' for the website idSite '2' * * @package Piwik * @subpackage Piwik_Archive @@ -23,7 +23,7 @@ class Piwik_Archive_Single extends Piwik_Archive /** * The Piwik_ArchiveProcessing object used to check that the archive is available * and launch the processing if the archive was not yet processed - * + * * @var Piwik_ArchiveProcessing */ public $archiveProcessing = null; @@ -48,7 +48,7 @@ class Piwik_Archive_Single extends Piwik_Archive protected $cacheEnabledForNumeric = true; /** - * Array of cached numeric values, used to make requests faster + * Array of cached numeric values, used to make requests faster * when requesting the same value again and again * * @var array of numeric @@ -69,6 +69,20 @@ class Piwik_Archive_Single extends Piwik_Archive */ protected $idArchive = null; + /** + * name of requested report + * + * @var string + */ + protected $requestedReport = null; + + /** + * scope of requested report (e.g. page) + * + * @var string + */ + protected $requestedReportScope = null; + /** * Flag set to true once the archive has been checked (when we make sure it is archived) * @@ -115,7 +129,7 @@ class Piwik_Archive_Single extends Piwik_Archive } /** - * Set the period + * Set the period * * @param Piwik_Period $period */ @@ -150,7 +164,7 @@ class Piwik_Archive_Single extends Piwik_Archive /** * Prepares the archive. Gets the idarchive from the ArchiveProcessing. - * + * * This will possibly launch the archiving process if the archive was not available. */ public function prepareArchive() @@ -197,7 +211,8 @@ class Piwik_Archive_Single extends Piwik_Archive $this->archiveProcessing->init(); - $this->archiveProcessing->setRequestedReport( $this->getRequestedReport() ); + $this->archiveProcessing->setRequestedReport( + $this->getRequestedReport(), $this->getRequestedReportScope()); $archivingDisabledArchiveNotProcessed = false; $idArchive = $this->archiveProcessing->loadArchive(); @@ -233,7 +248,7 @@ class Piwik_Archive_Single extends Piwik_Archive } /** - * Returns a value from the current archive with the name = $name + * Returns a value from the current archive with the name = $name * Method used by getNumeric or getBlob * * @param string $name @@ -272,7 +287,7 @@ class Piwik_Archive_Single extends Piwik_Archive return false; } - // select the table to use depending on the type of the data requested + // select the table to use depending on the type of the data requested switch($typeValue) { case 'blob': @@ -286,20 +301,20 @@ class Piwik_Archive_Single extends Piwik_Archive } $db = Zend_Registry::get('db'); - $value = $db->fetchOne("SELECT value + $value = $db->fetchOne("SELECT value FROM $table WHERE idarchive = ? - AND name = ?", - array( $this->idArchive , $name) + AND name = ?", + array( $this->idArchive , $name) ); if($value === false) { - if($typeValue == 'numeric' + if($typeValue == 'numeric' && $this->cacheEnabledForNumeric) { $this->numericCached[$name] = false; - } + } return $value; } @@ -309,7 +324,7 @@ class Piwik_Archive_Single extends Piwik_Archive $value = $this->uncompress($value); } - if($typeValue == 'numeric' + if($typeValue == 'numeric' && $this->cacheEnabledForNumeric) { $this->numericCached[$name] = $value; @@ -321,8 +336,8 @@ class Piwik_Archive_Single extends Piwik_Archive /** * This method loads in memory all the subtables for the main table called $name. * You have to give it the parent table $dataTableToLoad so we can lookup the sub tables ids to load. - * - * If $addMetadataSubtableId set to true, it will add for each row a 'metadata' called 'databaseSubtableId' + * + * If $addMetadataSubtableId set to true, it will add for each row a 'metadata' called 'databaseSubtableId' * containing the child ID of the subtable associated to this row. * * @param string $name @@ -332,7 +347,7 @@ class Piwik_Archive_Single extends Piwik_Archive public function loadSubDataTables($name, Piwik_DataTable $dataTableToLoad, $addMetadataSubtableId = false) { // we have to recursively load all the subtables associated to this table's rows - // and update the subtableID so that it matches the newly instanciated table + // and update the subtableID so that it matches the newly instanciated table foreach($dataTableToLoad->getRows() as $row) { $subTableID = $row->getIdSubDataTable(); @@ -361,7 +376,7 @@ class Piwik_Archive_Single extends Piwik_Archive */ public function freeBlob( $name ) { - $this->blobCached[$name] = null; + $this->blobCached[$name] = null; unset($this->blobCached[$name]); } @@ -372,14 +387,14 @@ class Piwik_Archive_Single extends Piwik_Archive /** * Fetches all blob fields name_* at once for the current archive for performance reasons. - * + * * @return false if no visits */ public function preFetchBlob( $name ) { $this->setRequestedReport($name); $this->prepareArchive(); - if(!$this->isThereSomeVisits) { return; } + if(!$this->isThereSomeVisits) { return; } $tableBlob = $this->archiveProcessing->getTableArchiveBlobName(); @@ -388,8 +403,8 @@ class Piwik_Archive_Single extends Piwik_Archive $query = $db->query("SELECT value, name FROM $tableBlob WHERE idarchive = ? - AND name LIKE '$name%'", - array( $this->idArchive ) + AND name LIKE '$name%'", + array( $this->idArchive ) ); while($row = $query->fetch()) @@ -434,24 +449,24 @@ class Piwik_Archive_Single extends Piwik_Archive */ public function getBlob( $name ) { - return $this->get($name, 'blob'); + return $this->get($name, 'blob'); } /** - * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Simple + * Given a list of fields defining numeric values, it will return a Piwik_DataTable_Simple * containing one row per field name. - * + * * For example $fields = array( 'max_actions', - * 'nb_uniq_visitors', + * 'nb_uniq_visitors', * 'nb_visits', - * 'nb_actions', + * 'nb_actions', * 'sum_visit_length', * 'bounce_count', * 'nb_visits_converted' - * ); + * ); + * + * @param string|array $fields Name or array of names of Archive fields * - * @param string|array $fields Name or array of names of Archive fields - * * @return Piwik_DataTable_Simple */ public function getDataTableFromNumeric( $fields ) @@ -497,7 +512,7 @@ class Piwik_Archive_Single extends Piwik_Archive { $table->addRowsFromSerializedArray($data); } - if($data === false + if($data === false && $idSubTable !== null) { // This is not expected, but somehow happens in some unknown cases and very rarely. @@ -509,9 +524,14 @@ class Piwik_Archive_Single extends Piwik_Archive return $table; } - public function setRequestedReport($requestedReport ) + public function setRequestedReport($requestedReport, $scope=null) { $this->requestedReport = $requestedReport; + + if ($scope != null) + { + $this->requestedReportScope = $scope; + } } protected function getRequestedReport() @@ -522,9 +542,9 @@ class Piwik_Archive_Single extends Piwik_Archive { return 'VisitsSummary_CoreMetrics'; } - // VisitFrequency metrics don't follow the same naming convention (HACK) + // VisitFrequency metrics don't follow the same naming convention (HACK) if(strpos($this->requestedReport, '_returning') > 0 - // ignore Goal_visitor_returning_1_1_nb_conversions + // ignore Goal_visitor_returning_1_1_nb_conversions && strpos($this->requestedReport, 'Goal_') === false) { return 'VisitFrequency_Metrics'; @@ -537,18 +557,23 @@ class Piwik_Archive_Single extends Piwik_Archive return $this->requestedReport; } + protected function getRequestedReportScope() + { + return $this->requestedReportScope; + } + /** * Returns a DataTable that has the name '$name' from the current Archive. * Also loads in memory all subDataTable for this DataTable. - * + * * For example, if $name = 'Referers_keywordBySearchEngine' it will load all DataTable * named 'Referers_keywordBySearchEngine_*' and they will be set as subDataTable to the - * rows. You can then go through the rows + * rows. You can then go through the rows * $rows = DataTable->getRows(); * and for each row request the subDataTable (in this case the DataTable of the keywords for each search engines) * $idSubTable = $row->getIdSubDataTable(); * $subTable = Piwik_DataTable_Manager::getInstance()->getTable($idSubTable); - * + * * @param string $name * @param int $idSubTable Optional subDataTable to load instead of loading the parent DataTable * @return Piwik_DataTable @@ -560,6 +585,6 @@ class Piwik_Archive_Single extends Piwik_Archive $this->loadSubDataTables($name, $dataTableToLoad, $addMetadataSubtableId = true); $dataTableToLoad->enableRecursiveFilters(); $this->freeBlob($name); - return $dataTableToLoad; + return $dataTableToLoad; } } diff --git a/core/ArchiveProcessing.php b/core/ArchiveProcessing.php index d285cfc53bed16254bd7442abda0bd637dd88d39..72dfbb11e0fadc45441736900bb60547faaf1ff9 100644 --- a/core/ArchiveProcessing.php +++ b/core/ArchiveProcessing.php @@ -1,11 +1,11 @@ <?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 Piwik */ @@ -13,19 +13,19 @@ /** * The ArchiveProcessing module is a module that reads the Piwik logs from the DB and * compute all the reports, which are then stored in the database. - * + * * The ArchiveProcessing class is used by the Archive object to make sure the given Archive is processed and available in the DB. - * + * * A record in the Database for a given report is defined by * - idarchive = unique ID that is associated to all the data of this archive (idsite+period+date) - * - idsite = the ID of the website + * - idsite = the ID of the website * - date1 = starting day of the period * - date2 = ending day of the period * - period = integer that defines the period (day/week/etc.). @see period::getId() * - ts_archived = timestamp when the archive was processed (UTC) * - name = the name of the report (ex: uniq_visitors or search_keywords_by_search_engines) * - value = the actual data - * + * * @package Piwik * @subpackage Piwik_ArchiveProcessing */ @@ -49,7 +49,7 @@ abstract class Piwik_ArchiveProcessing /** * Flag indicates the archive is over a period that is not finished, eg. the current day, current week, etc. * Archives flagged will be regularly purged from the DB. - * + * * @var int */ const DONE_OK_TEMPORARY = 3; @@ -77,34 +77,34 @@ abstract class Piwik_ArchiveProcessing /** * Starting date of the archive - * + * * @var Piwik_Date */ protected $dateStart; /** * Ending date of the archive - * + * * @var Piwik_Date */ protected $dateEnd; /** * Object used to generate (depending on the $dateStart) the name of the DB table to use to store numeric values - * + * * @var Piwik_TablePartitioning */ protected $tableArchiveNumeric; /** * Object used to generate (depending on the $dateStart) the name of the DB table to use to store numeric values - * + * * @var Piwik_TablePartitioning */ protected $tableArchiveBlob; /** - * Minimum timestamp looked at for processed archives + * Minimum timestamp looked at for processed archives * * @var int */ @@ -119,7 +119,7 @@ abstract class Piwik_ArchiveProcessing /** * Is the current archive temporary. ie. - * - today + * - today * - current week / month / year */ protected $temporaryArchive; @@ -127,7 +127,7 @@ abstract class Piwik_ArchiveProcessing /** * Id of the current site * Can be accessed by plugins (that is why it's public) - * + * * @var int */ public $idsite = null; @@ -135,7 +135,7 @@ abstract class Piwik_ArchiveProcessing /** * Period of the current archive * Can be accessed by plugins (that is why it's public) - * + * * @var Piwik_Period */ public $period = null; @@ -143,7 +143,7 @@ abstract class Piwik_ArchiveProcessing /** * Site of the current archive * Can be accessed by plugins (that is why it's public) - * + * * @var Piwik_Site */ public $site = null; @@ -220,7 +220,7 @@ abstract class Piwik_ArchiveProcessing switch($name) { case 'day': - $process = new Piwik_ArchiveProcessing_Day(); + $process = new Piwik_ArchiveProcessing_Day(); $process->debugAlwaysArchive = Zend_Registry::get('config')->Debug->always_archive_data_day; break; @@ -249,9 +249,9 @@ abstract class Piwik_ArchiveProcessing static public function getCoreMetrics() { return array( - 'nb_uniq_visitors', + 'nb_uniq_visitors', 'nb_visits', - 'nb_actions', + 'nb_actions', 'sum_visit_length', 'bounce_count', 'nb_visits_converted', @@ -317,7 +317,7 @@ abstract class Piwik_ArchiveProcessing $this->tableArchiveNumeric->setIdSite($this->idsite); $this->tableArchiveNumeric->setTimestamp($dateStartLocalTimezone->getTimestamp()); $this->tableArchiveBlob = new Piwik_TablePartitioning_Monthly('archive_blob'); - $this->tableArchiveBlob->setIdSite($this->idsite); + $this->tableArchiveBlob->setIdSite($this->idsite); $this->tableArchiveBlob->setTimestamp($dateStartLocalTimezone->getTimestamp()); $dateStartUTC = $dateStartLocalTimezone->setTimezone($this->site->getTimezone()); @@ -349,7 +349,7 @@ abstract class Piwik_ArchiveProcessing /** * Returns the minimum archive processed datetime to look at - * + * * @return string Datetime string, or false if must look at any archive available */ public function getMinTimeArchivedProcessed() @@ -363,15 +363,15 @@ abstract class Piwik_ArchiveProcessing { $this->temporaryArchive = true; $minDatetimeArchiveProcessedUTC = $this->time - self::getTodayArchiveTimeToLive(); - // see #1150; if new archives are not triggered from the browser, + // see #1150; if new archives are not triggered from the browser, // we still want to try and return the latest archive available for today (rather than return nothing) if($this->isArchivingDisabled()) { return false; } } - // - if the period we are looking for is finished, we look for a ts_archived that - // is greater than the last day of the archive + // - if the period we are looking for is finished, we look for a ts_archived that + // is greater than the last day of the archive elseif($this->endTimestampUTC <= $this->time) { $minDatetimeArchiveProcessedUTC = $this->endTimestampUTC+1; @@ -384,7 +384,7 @@ abstract class Piwik_ArchiveProcessing // We choose to only look at archives that are newer than the specified timeout $minDatetimeArchiveProcessedUTC = $this->time - self::getTodayArchiveTimeToLive(); - // However, if archiving is disabled for this request, we shall + // However, if archiving is disabled for this request, we shall // accept any archive that was processed today after 00:00:01 this morning if($this->isArchivingDisabled()) { @@ -397,7 +397,7 @@ abstract class Piwik_ArchiveProcessing /** * This method returns the idArchive ; if necessary, it triggers the archiving process. - * + * * If the archive was not processed yet, it will launch the archiving process. * If the current archive needs sub-archives (eg. a month archive needs all the days archive) * it will recursively launch the archiving (using this loadArchive() on the sub-periods) @@ -443,7 +443,7 @@ abstract class Piwik_ArchiveProcessing } /** - * This methods reads the subperiods if necessary, + * This methods reads the subperiods if necessary, * and computes the archive of the current period. */ abstract protected function compute(); @@ -458,7 +458,7 @@ abstract class Piwik_ArchiveProcessing $pluginProcessed = self::getPluginBeingProcessed($this->getRequestedReport()); // Piwik::log("Plugin processed: $pluginProcessed"); if(!Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginProcessed) - || $flagArchiveAsAllPlugins + || $flagArchiveAsAllPlugins ) { $pluginProcessed = 'all'; @@ -477,7 +477,7 @@ abstract class Piwik_ArchiveProcessing * When a segment is set, we shall only process the requested report (no more). * The requested data set will return a lot faster if we only process these reports rather than all plugins. * Similarly, when a period=range is requested, we shall only process the requested report for the range itself. - * + * * @param string $pluginName * @return bool */ @@ -494,7 +494,7 @@ abstract class Piwik_ArchiveProcessing $pluginBeingProcessed = self::getPluginBeingProcessed($this->getRequestedReport()); return $pluginBeingProcessed == $pluginName || !Piwik_PluginsManager::getInstance()->isPluginLoaded($pluginBeingProcessed) - ; + ; } /** @@ -514,24 +514,24 @@ abstract class Piwik_ArchiveProcessing { $temporary = 'temporary archive'; } - Piwik::log("'" . $this->period->getLabel() . "'" - .", idSite = ". $this->idsite." ($temporary)" + Piwik::log("'" . $this->period->getLabel() . "'" + .", idSite = ". $this->idsite." ($temporary)" .", segment = '". $this->getSegment()->getString()."'" - .", report = '". $this->getRequestedReport()."'" + .", report = '". $this->getRequestedReport()."'" .", UTC datetime [".$this->startDatetimeUTC." -> ".$this->endDatetimeUTC." ]..."); } /** * Post processing called at the end of the main archive processing. * Makes sure the new archive is marked as "successful" in the DB - * + * * We also try to delete some stuff from memory but really there is still a lot... */ protected function postCompute() { - // delete the first done = ERROR + // delete the first done = ERROR $done = $this->getDoneStringFlag(); - Piwik_Query("DELETE FROM ".$this->tableArchiveNumeric->getTableName()." + Piwik_Query("DELETE FROM ".$this->tableArchiveNumeric->getTableName()." WHERE idarchive = ? AND name = '".$done."'", array($this->idArchive) ); @@ -547,7 +547,7 @@ abstract class Piwik_ArchiveProcessing /** * Returns the name of the numeric table where the archive numeric values are stored * - * @return string + * @return string */ public function getTableArchiveNumericName() { @@ -557,7 +557,7 @@ abstract class Piwik_ArchiveProcessing /** * Returns the name of the blob table where the archive blob values are stored * - * @return string + * @return string */ public function getTableArchiveBlobName() { @@ -569,12 +569,12 @@ abstract class Piwik_ArchiveProcessing * * @param Piwik_Period $period */ - public function setPeriod( Piwik_Period $period ) + public function setPeriod( Piwik_Period $period ) { $this->period = $period; } - public function setSegment( Piwik_Segment $segment) + public function setSegment( Piwik_Segment $segment) { $this->segment = $segment; } @@ -593,15 +593,21 @@ abstract class Piwik_ArchiveProcessing $this->site = $site; } - public function setRequestedReport($requestedReport) + public function setRequestedReport($requestedReport, $scope=null) { $this->requestedReport = $requestedReport; + $this->requestedReportScope = $scope; } protected function getRequestedReport() { return $this->requestedReport; } + + protected function getRequestedReportScope() + { + return $this->requestedReportScope; + } static public function getPluginBeingProcessed( $requestedReport ) { @@ -647,7 +653,7 @@ abstract class Piwik_ArchiveProcessing protected function loadNextIdarchive() { $db = Zend_Registry::get('db'); - $id = $db->fetchOne("SELECT max(idarchive) + $id = $db->fetchOne("SELECT max(idarchive) FROM ".$this->tableArchiveNumeric->getTableName()); if(empty($id)) { @@ -670,7 +676,7 @@ abstract class Piwik_ArchiveProcessing /** * @param string $name * @param string|array of string $aValues - * @return true + * @return true */ public function insertBlobRecord($name, $values) { @@ -755,10 +761,10 @@ abstract class Piwik_ArchiveProcessing protected function getBindArray() { return array( $this->idArchive, - $this->idsite, - $this->period->getDateStart()->toString('Y-m-d'), - $this->period->getDateEnd()->toString('Y-m-d'), - $this->periodId, + $this->idsite, + $this->period->getDateStart()->toString('Y-m-d'), + $this->period->getDateEnd()->toString('Y-m-d'), + $this->periodId, date("Y-m-d H:i:s")); } @@ -776,7 +782,7 @@ abstract class Piwik_ArchiveProcessing // table to use to save the data if(is_numeric($value)) { - // We choose not to record records with a value of 0 + // We choose not to record records with a value of 0 if($value == 0) { return; @@ -790,7 +796,7 @@ abstract class Piwik_ArchiveProcessing // duplicate idarchives are Ignored, see http://dev.piwik.org/trac/ticket/987 - $query = "INSERT IGNORE INTO ".$table->getTableName()." + $query = "INSERT IGNORE INTO ".$table->getTableName()." (". implode(", ", $this->getInsertFields()).") VALUES (?,?,?,?,?,?,?,?)"; $bindSql = $this->getBindArray(); @@ -803,7 +809,7 @@ abstract class Piwik_ArchiveProcessing /** * Returns the idArchive if the archive is available in the database. * Returns false if the archive needs to be computed. - * + * * An archive is available if * - for today, the archive was computed less than minDatetimeArchiveProcessedUTC seconds ago * - for any other day, if the archive was computed once this day was finished @@ -813,10 +819,10 @@ abstract class Piwik_ArchiveProcessing */ protected function isArchived() { - $bindSQL = array( $this->idsite, - $this->period->getDateStart()->toString('Y-m-d'), - $this->period->getDateEnd()->toString('Y-m-d'), - $this->periodId, + $bindSQL = array( $this->idsite, + $this->period->getDateStart()->toString('Y-m-d'), + $this->period->getDateEnd()->toString('Y-m-d'), + $this->periodId, ); $timeStampWhere = ''; @@ -887,7 +893,7 @@ abstract class Piwik_ArchiveProcessing // we look for the nb_visits result for this most recent archive foreach($results as $result) { - if($result['name'] == 'nb_visits' + if($result['name'] == 'nb_visits' && $result['idarchive'] == $idarchive) { $this->isThereSomeVisits = ($result['value'] > 0); @@ -900,7 +906,7 @@ abstract class Piwik_ArchiveProcessing /** * Returns true if, for some reasons, triggering the archiving is disabled. - * Note that when a segment is passed to the function, archiving will always occur + * Note that when a segment is passed to the function, archiving will always occur * (since segments are by default not pre-processed) * * @return bool @@ -919,7 +925,7 @@ abstract class Piwik_ArchiveProcessing { return self::isBrowserTriggerArchivingEnabled() || Piwik_Common::isPhpCliMode() - || (Piwik::isUserIsSuperUser() + || (Piwik::isUserIsSuperUser() && Piwik_Common::isArchivePhpTriggered()) ; } diff --git a/core/ArchiveProcessing/Day.php b/core/ArchiveProcessing/Day.php index 978236d94739bb7a893689cad7b64f9305f6b15c..580c364d3060bd9f99b4f8abee7985f9bb332023 100644 --- a/core/ArchiveProcessing/Day.php +++ b/core/ArchiveProcessing/Day.php @@ -1,22 +1,22 @@ <?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 Piwik */ /** - * Handles the archiving process for a day. - * The class provides generic helper methods to manipulate data from the DB, + * Handles the archiving process for a day. + * The class provides generic helper methods to manipulate data from the DB, * easily create Piwik_DataTable objects from running SELECT ... GROUP BY on the log_visit table. - * + * * All the logic of the archiving is done inside the plugins listening to the event 'ArchiveProcessing_Day.compute' - * + * * @package Piwik * @subpackage Piwik_ArchiveProcessing */ @@ -44,102 +44,275 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing /** * Returns true if there are logs for the current archive. - * - * If the current archive is for a specific plugin (for example, Referers), + * + * If the current archive is for a specific plugin (for example, Referers), * (for example when a Segment is defined and the Keywords report is requested) * Then the function will create the Archive for the Core metrics 'VisitsSummary' which will in turn process the number of visits - * - * If there is no specified segment, the SQL query will always run. + * + * If there is no specified segment, the SQL query will always run. */ public function isThereSomeVisits() { - if(!is_null($this->isThereSomeVisits)) + if (!is_null($this->isThereSomeVisits)) { - if($this->isThereSomeVisits && is_null($this->nb_visits)) { debug_print_backtrace(); exit; } + if ($this->isThereSomeVisits && is_null($this->nb_visits)) + { + debug_print_backtrace(); + exit; + } return $this->isThereSomeVisits; } - // Handling Custom Segment - $segmentSql = $this->getSegment()->getSql(); - $sqlSegmentBind = $segmentSql['bind']; - $sqlSegment = $segmentSql['sql']; - if(!empty($sqlSegment)) $sqlSegment = ' AND '.$sqlSegment; - + + // prepare segmentation + $segment = $this->getSegment(); + $segmentation = !$segment->isEmpty(); + $segmentationWhere = ''; + $segmentationBind = array(); + + if ($segmentation) + { + $segmentationSql = $segment->getSql(); + $segmentationWhere = $segmentationSql['sql']; + $segmentationBind = $segmentationSql['bind']; + + $segmentation = !empty($segmentationWhere); + } + + $reportType = self::getPluginBeingProcessed($this->getRequestedReport()); + $reportScope = $this->getRequestedReportScope(); + $isVisitsSummaryReport = ($reportType == 'VisitsSummary'); + // We check if there is visits for the requested date / site / segment - // If no specified Segment + // If no specified Segment // Or if a segment is passed and we specifically process VisitsSummary - // Then we check the logs. This is to ensure that this query is ran only once for this day/site/segment (rather than running it for every plugin) - if(empty($sqlSegment) - || self::getPluginBeingProcessed($this->getRequestedReport()) == 'VisitsSummary') + // Then we check the logs. This is to ensure that this query is ran only once for this day/site/segment (rather than running it for every plugin) + if (!$segmentation || $isVisitsSummaryReport) { - $query = "SELECT count(distinct idvisitor) as nb_uniq_visitors, - count(*) as nb_visits, - sum(visit_total_actions) as nb_actions, - max(visit_total_actions) as max_actions, - sum(visit_total_time) as sum_visit_length, - sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, - sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted - FROM ".Piwik_Common::prefixTable('log_visit')." AS log_visit - WHERE visit_last_action_time >= ? - AND visit_last_action_time <= ? - AND idsite = ? - $sqlSegment - ORDER BY NULL"; - $bind = array_merge(array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite ) - , $sqlSegmentBind); -// echo "Querying logs..."; -// var_dump($query);var_dump($bind); + if (!$segmentation || + ($segmentation && ($reportScope == 'visit' || !$reportScope))) + { + $this->checkSegmentationIsAvailable('visits', $segment, + $this->getSegmentsAvailableForVisits()); + + $data = $this->getBasicMetricsForVisitScope($segmentationWhere, $segmentationBind); + } - $row = $this->db->fetchRow($query, $bind ); - if($row === false || $row === null || $row['nb_visits'] == 0) + else if ($segmentation && $reportScope == 'page') { - $this->isThereSomeVisits = false; - return $this->isThereSomeVisits; + $this->checkSegmentationIsAvailable('pages', $segment, + $this->getSegmentsAvailableForActions()); + + $data = $this->getBasicMetricsForPageScope($segmentationWhere, $segmentationBind); } - - foreach($row as $name => $value) + + else if ($reportScope == 'all') + { + $this->checkSegmentationIsAvailable('all scopes', $segment, array_merge( + $this->getSegmentsAvailableForVisits(), + $this->getSegmentsAvailableForActions())); + + // @TODO: calculations are inaccurate if segment matches both page and visit. + // is this important? + // not if the values returned by the api are taken from the archived tables + // some plugins might rely on the values (goals?) => check + + $data = false; + if ($segment->isSegmentAvailable($this->getSegmentsAvailableForVisits())) + { + $data = $this->getBasicMetricsForVisitScope($segmentationWhere, $segmentationBind); + } + if (!$data && $segment->isSegmentAvailable($this->getSegmentsAvailableForActions())) + { + $data = $this->getBasicMetricsForPageScope($segmentationWhere, $segmentationBind); + } + } + + // no visits found + if (!is_array($data) || $data['nb_visits'] == 0) + { + return $this->isThereSomeVisits = false; + } + + // @TODO: why do we need all the attributes? + // wouldn't $isThereSomeVisits be enough? + // which plugins need the values? + + // visits found: set attribtues + foreach ($data as $name => $value) { $this->insertNumericRecord($name, $value); } - $this->setNumberOfVisits($row['nb_visits']); - $this->setNumberOfVisitsConverted($row['nb_visits_converted']); - $this->isThereSomeVisits = true; - return $this->isThereSomeVisits; + + $this->setNumberOfVisits($data['nb_visits']); + $this->setNumberOfVisitsConverted($data['nb_visits_converted']); + + return $this->isThereSomeVisits = true; } - // If a segment is specified but a plugin other than 'VisitsSummary' is being requested - // Then we create an archive for processing VisitsSummary Core Metrics, which will in turn execute the $query above + return $this->redirectRequestToVisitsSummary($reportType); + } + + /** + * Check whether requested segments are available for the scope. + * Throws an exception, if not. + */ + private function checkSegmentationIsAvailable($scopeName, $segment, $availableSegments) + { + if (!$segment->isSegmentAvailable($availableSegments)) + { + throw new Exception('The requested API uses '.$scopeName.' for segmenation. ' + .'One ore more of the requested segments are not available in this case.'); + } + } + + /** + * If a segment is specified but a plugin other than 'VisitsSummary' is being requested, + * we create an archive for processing VisitsSummary Core Metrics, which will in turn + * execute the query above (in isThereSomeVisits) + */ + private function redirectRequestToVisitsSummary($reportType) + { $archive = new Piwik_Archive_Single(); - $archive->setSite( $this->site ); - $archive->setPeriod( $this->period ); - $archive->setSegment( $this->getSegment() ); - $archive->setRequestedReport( 'VisitsSummary' ); + $archive->setSite($this->site); + $archive->setPeriod($this->period); + $archive->setSegment($this->getSegment()); + + // @TODO: get the scope via the API + + // Remeber the page scope, so that segments can be applied correctly later. + switch($reportType) + { + case 'Actions': + $scope = 'page'; + break; + case 'CustomVariables': + $scope = 'all'; + break; + default: + $scope = 'visit'; + break; + } + + $archive->setRequestedReport('VisitsSummary', $scope); $nbVisits = $archive->getNumeric('nb_visits'); - $isThereSomeVisits = $nbVisits > 0; - if($isThereSomeVisits) + $this->isThereSomeVisits = $nbVisits > 0; + + if ($this->isThereSomeVisits) { $nbVisitsConverted = $archive->getNumeric('nb_visits_converted'); $this->setNumberOfVisits($nbVisits); $this->setNumberOfVisitsConverted($nbVisitsConverted); } - $this->isThereSomeVisits = $isThereSomeVisits; + return $this->isThereSomeVisits; } + /** + * Get basic metrics from database + * Segments (e.g. custom variables) are applied to visits (table log_visit) + */ + private function getBasicMetricsForVisitScope($segmentationWhere, $segmentationBind) + { + if ($segmentationWhere) + { + $segmentationWhere = 'AND '.$segmentationWhere; + } + + $query = " + SELECT + count(distinct idvisitor) as nb_uniq_visitors, + count(*) as nb_visits, + sum(visit_total_actions) as nb_actions, + max(visit_total_actions) as max_actions, + sum(visit_total_time) as sum_visit_length, + sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, + sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted + FROM + ".Piwik_Common::prefixTable('log_visit')." AS log_visit + WHERE + visit_last_action_time >= ? + AND visit_last_action_time <= ? + AND idsite = ? + $segmentationWhere + ORDER BY NULL + "; + + $bind = array_merge( + array($this->getStartDatetimeUTC(), $this->getEndDatetimeUTC(), $this->idsite), + $segmentationBind); + + $row = $this->db->fetchRow($query, $bind); + + if (is_array($row) && $row['nb_visits'] > 0) { + return $row; + } + + return false; + } + + /** + * Get basic metrics from database + * Segments (e.g. custom variables) are applied to pages (table log_link_visit_action) + */ + private function getBasicMetricsForPageScope($segmentationWhere, $segmentationBind) + { + $query = " + SELECT + count(distinct idvisitor) as nb_uniq_visitors, + count(*) as nb_visits, + sum(actions_per_visit.nb_actions) as nb_actions, + max(actions_per_visit.nb_actions) as max_actions, + sum(visit_total_time) as sum_visit_length, + sum(case visit_total_actions when 1 then 1 else 0 end) as bounce_count, + sum(case visit_goal_converted when 1 then 1 else 0 end) as nb_visits_converted + FROM + ( + SELECT + idvisit, + count(log_link_visit_action.idlink_va) as nb_actions + FROM + ".Piwik_Common::prefixTable('log_link_visit_action')." + AS log_link_visit_action + WHERE + log_link_visit_action.idsite = ? + AND log_link_visit_action.server_time >= ? + AND log_link_visit_action.server_time <= ? + AND $segmentationWhere + GROUP BY + idvisit + ) AS actions_per_visit + LEFT JOIN + ".Piwik_Common::prefixTable('log_visit')." AS log_visit USING(idvisit) + "; + + $bind = array_merge( + array($this->idsite, $this->getStartDatetimeUTC(), $this->getEndDatetimeUTC()), + $segmentationBind); + + $row = $this->db->fetchRow($query, $bind); + + if (is_array($row) && $row['nb_visits'] > 0) { + return $row; + } + + return false; + } + /** * Helper function that returns a DataTable containing the $select fields / value pairs. * IMPORTANT: The $select must return only one row!! - * - * Example $select = "count(distinct( config_os )) as countDistinctOs, + * + * Example $select = "count(distinct( config_os )) as countDistinctOs, * sum( config_flash ) / count(distinct(idvisit)) as percentFlash " * $labelCount = "test_column_name" * will return a dataTable that looks like - * label test_column_name - * CountDistinctOs 9 + * label test_column_name + * CountDistinctOs 9 * PercentFlash 0.5676 - * * - * @param string $select + * + * @param string $select * @param string $labelCount * @return Piwik_DataTable */ @@ -151,7 +324,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $sqlSegment = $segmentSql['sql']; if(!empty($sqlSegment)) $sqlSegment = ' AND '.$sqlSegment; - $query = "SELECT $select + $query = "SELECT $select FROM ".Piwik_Common::prefixTable('log_visit')." AS log_visit WHERE visit_last_action_time >= ? AND visit_last_action_time <= ? @@ -186,7 +359,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $groupBy = 'label'; } - if(!empty($where)) + if(!empty($where)) { $where = sprintf($where, "log_link_visit_action", "log_link_visit_action"); $where = ' AND '.$where; @@ -220,8 +393,8 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $sqlSegmentWhere GROUP BY $groupBy"; - $bind = array_merge( array( $this->getStartDatetimeUTC(), - $this->getEndDatetimeUTC(), + $bind = array_merge( array( $this->getStartDatetimeUTC(), + $this->getEndDatetimeUTC(), $this->idsite ), $segmentSql['bind']); $query = $this->db->query($query, $bind ); @@ -232,7 +405,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * Query visits by dimension * * @param string $label mixed Can be a string, eg. "referer_name", will be aliased as 'label' in the returned rows - * Can also be an array of strings, when the dimension spans multiple fields, eg. array("referer_name", "referer_keyword") + * Can also be an array of strings, when the dimension spans multiple fields, eg. array("referer_name", "referer_keyword") * @param string $where Additional condition for WHERE clause */ public function queryVisitsByDimension($label, $where = '') @@ -252,7 +425,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $groupBy = 'label'; } - if(!empty($where)) + if(!empty($where)) { $where = sprintf($where, "log_visit", "log_visit"); $where = ' AND '.$where; @@ -263,13 +436,13 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing if(!empty($segmentSql['sql'])) { $segment = ' AND '.$segmentSql['sql']; - } + } $query = "SELECT $select, - count(distinct idvisitor) as `". Piwik_Archive::INDEX_NB_UNIQ_VISITORS ."`, + count(distinct idvisitor) as `". Piwik_Archive::INDEX_NB_UNIQ_VISITORS ."`, count(*) as `". Piwik_Archive::INDEX_NB_VISITS ."`, - sum(visit_total_actions) as `". Piwik_Archive::INDEX_NB_ACTIONS ."`, - max(visit_total_actions) as `". Piwik_Archive::INDEX_MAX_ACTIONS ."`, + sum(visit_total_actions) as `". Piwik_Archive::INDEX_NB_ACTIONS ."`, + max(visit_total_actions) as `". Piwik_Archive::INDEX_MAX_ACTIONS ."`, sum(visit_total_time) as `". Piwik_Archive::INDEX_SUM_VISIT_LENGTH ."`, sum(case visit_total_actions when 1 then 1 else 0 end) as `". Piwik_Archive::INDEX_BOUNCE_COUNT ."`, sum(case visit_goal_converted when 1 then 1 else 0 end) as `". Piwik_Archive::INDEX_NB_VISITS_CONVERTED ."` @@ -281,8 +454,8 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $segment GROUP BY $groupBy ORDER BY NULL"; - $bind = array_merge( array( $this->getStartDatetimeUTC(), - $this->getEndDatetimeUTC(), + $bind = array_merge( array( $this->getStartDatetimeUTC(), + $this->getEndDatetimeUTC(), $this->idsite ), $segmentSql['bind']); $query = $this->db->query($query, $bind ); @@ -307,6 +480,35 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing return $allowedSegments; } + protected function getSegmentsAvailableForVisits() + { + // @TODO: complete list + $allowedSegments = array( + 'idvisitor', + 'referer_type', + 'referer_name', + 'referer_keyword', + 'visitor_returning', + 'visitor_days_since_first', + 'visitor_days_since_order', + 'visitor_count_visits', + 'visit_goal_buyer', + 'location_country', + 'location_continent', + 'custom_var_k1', + 'custom_var_v1', + 'custom_var_k2', + 'custom_var_v2', + 'custom_var_k3', + 'custom_var_v3', + 'custom_var_k4', + 'custom_var_v4', + 'custom_var_k5', + 'custom_var_v5', + ); + return $allowedSegments; + } + protected function getSegmentsAvailableForConversions() { $allowedSegments = array( @@ -337,9 +539,9 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing } /** - * @see queryVisitsByDimension() Similar to this function, - * but queries metrics for the requested dimensions, - * for each Goal conversion + * @see queryVisitsByDimension() Similar to this function, + * but queries metrics for the requested dimensions, + * for each Goal conversion */ public function queryConversionsByDimension($label, $where = '') { @@ -362,7 +564,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $select = $label . " AS label, "; $groupBy = 'label'; } - if(!empty($where)) + if(!empty($where)) { $where = sprintf($where, "log_conversion", "log_conversion"); $where = ' AND '.$where; @@ -384,7 +586,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing "SUM(items) as `". Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS ."`, "; $groupBy = !empty($groupBy) ? ", $groupBy" : ''; - $query = "SELECT + $query = "SELECT $select idgoal, count(*) as `". Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS ."`, @@ -399,8 +601,8 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $segment GROUP BY idgoal $groupBy ORDER BY NULL"; - $bind = array_merge( array( $this->getStartDatetimeUTC(), - $this->getEndDatetimeUTC(), + $bind = array_merge( array( $this->getStartDatetimeUTC(), + $this->getEndDatetimeUTC(), $this->idsite ), $segmentSql['bind']); $query = $this->db->query($query, $bind); @@ -409,7 +611,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing public function queryEcommerceItems($field) { - $query = "SELECT + $query = "SELECT name as label, ".self::getSqlRevenue('SUM(quantity * price)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_REVENUE ."`, ".self::getSqlRevenue('SUM(quantity)')." as `". Piwik_Archive::INDEX_ECOMMERCE_ITEM_QUANTITY ."`, @@ -418,7 +620,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing count(idvisit) as `". Piwik_Archive::INDEX_NB_VISITS."`, case idorder when '0' then ".Piwik_Tracker_GoalManager::IDGOAL_CART." else ".Piwik_Tracker_GoalManager::IDGOAL_ORDER." end as ecommerceType FROM ".Piwik_Common::prefixTable('log_conversion_item')." - LEFT JOIN ".Piwik_Common::prefixTable('log_action')." + LEFT JOIN ".Piwik_Common::prefixTable('log_action')." ON $field = idaction WHERE server_time >= ? AND server_time <= ? @@ -427,8 +629,8 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing GROUP BY ecommerceType, $field ORDER BY NULL"; - $bind = array( $this->getStartDatetimeUTC(), - $this->getEndDatetimeUTC(), + $bind = array( $this->getStartDatetimeUTC(), + $this->getEndDatetimeUTC(), $this->idsite ); $query = $this->db->query($query, $bind); @@ -451,7 +653,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * Output: * array( * LABEL => array( - * Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0, + * Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0, * Piwik_Archive::INDEX_NB_VISITS => 0 * ), * LABEL2 => array( @@ -460,7 +662,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * ) * * Helper function that returns an array with common statistics for a given database field distinct values. - * + * * The statistics returned are: * - number of unique visitors * - number of visits @@ -468,16 +670,16 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * - maximum number of action for a visit * - sum of the visits' length in sec * - count of bouncing visits (visits with one page view) - * + * * For example if $label = 'config_os' it will return the statistics for every distinct Operating systems - * The returned array will have a row per distinct operating systems, + * The returned array will have a row per distinct operating systems, * and a column per stat (nb of visits, max actions, etc) - * - * 'label' Piwik_Archive::INDEX_NB_UNIQ_VISITORS Piwik_Archive::INDEX_NB_VISITS etc. + * + * 'label' Piwik_Archive::INDEX_NB_UNIQ_VISITORS Piwik_Archive::INDEX_NB_VISITS etc. * Linux 27 66 ... - * Windows XP 12 ... + * Windows XP 12 ... * Mac OS 15 36 ... - * + * * @param string $label Table log_visit field name to be use to compute common stats * @return array */ @@ -496,7 +698,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing /** * Generates a dataTable given a multidimensional PHP array that associates LABELS to Piwik_DataTableRows * This is used for the "Actions" DataTable, where a line is the aggregate of all the subtables - * Example: the category /blog has 3 visits because it has /blog/index (2 visits) + /blog/about (1 visit) + * Example: the category /blog has 3 visits because it has /blog/index (2 visits) + /blog/about (1 visit) * * @param array $table * @return Piwik_DataTable @@ -535,7 +737,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing * LABEL => array(col1 => X, col2 => Y), * LABEL2 => array(col1 => X, col2 => Y), * ) - * + * * @param array $array at the given format * @return array Array with one element: the serialized data table string */ @@ -551,27 +753,27 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing /** * Helper function that returns the multiple serialized DataTable of the given PHP array. * The DataTable here associates a subtable to every row of the level 0 array. - * This is used for example for search engines. + * This is used for example for search engines. * Every search engine (level 0) has a subtable containing the keywords. - * - * The $arrayLevel0 must have the format + * + * The $arrayLevel0 must have the format * Example: array ( * // Yahoo.com => array( kwd1 => stats, kwd2 => stats ) * LABEL => array(col1 => X, col2 => Y), * LABEL2 => array(col1 => X, col2 => Y), * ) - * + * * The $subArrayLevel1ByKey must have the format * Example: array( * // Yahoo.com => array( stats ) * LABEL => #Piwik_DataTable_ForLABEL, * LABEL2 => #Piwik_DataTable_ForLABEL2, * ) - * - * + * + * * @param array $arrayLevel0 * @param array $subArrayLevel1ByKey Array of Piwik_DataTable - * @return array Array with N elements: the strings of the datatable serialized + * @return array Array with N elements: the strings of the datatable serialized */ public function getDataTableWithSubtablesFromArraysIndexedByLabel( $arrayLevel0, $subArrayLevel1ByKey ) { @@ -598,16 +800,16 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing { if($onlyMetricsAvailableInActionsTable) { - return array( Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0, - Piwik_Archive::INDEX_NB_VISITS => 0, + return array( Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0, + Piwik_Archive::INDEX_NB_VISITS => 0, Piwik_Archive::INDEX_NB_ACTIONS => 0 ); } - return array( Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0, - Piwik_Archive::INDEX_NB_VISITS => 0, - Piwik_Archive::INDEX_NB_ACTIONS => 0, - Piwik_Archive::INDEX_MAX_ACTIONS => 0, - Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 0, + return array( Piwik_Archive::INDEX_NB_UNIQ_VISITORS => 0, + Piwik_Archive::INDEX_NB_VISITS => 0, + Piwik_Archive::INDEX_NB_ACTIONS => 0, + Piwik_Archive::INDEX_MAX_ACTIONS => 0, + Piwik_Archive::INDEX_SUM_VISIT_LENGTH => 0, Piwik_Archive::INDEX_BOUNCE_COUNT => 0, Piwik_Archive::INDEX_NB_VISITS_CONVERTED=> 0, ); @@ -615,7 +817,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing /** - * Returns a Piwik_DataTable_Row containing default values for common stat, + * Returns a Piwik_DataTable_Row containing default values for common stat, * plus a column 'label' with the value $label * * @param string $label @@ -624,18 +826,18 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing public function getNewInterestRowLabeled( $label ) { return new Piwik_DataTable_Row( - array( - Piwik_DataTable_Row::COLUMNS => array( 'label' => $label) + array( + Piwik_DataTable_Row::COLUMNS => array( 'label' => $label) + $this->getNewInterestRow() ) - ); + ); } /** * Adds the given row $newRowToAdd to the existing $oldRowToUpdate passed by reference * * The rows are php arrays Name => value - * + * * @param array $newRowToAdd * @param array $oldRowToUpdate */ @@ -669,33 +871,33 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing $oldRowToUpdate[Piwik_Archive::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd[Piwik_Archive::INDEX_SUM_VISIT_LENGTH]; $oldRowToUpdate[Piwik_Archive::INDEX_BOUNCE_COUNT] += $newRowToAdd[Piwik_Archive::INDEX_BOUNCE_COUNT]; $oldRowToUpdate[Piwik_Archive::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd[Piwik_Archive::INDEX_NB_VISITS_CONVERTED]; - } + } /** - * Given an array of stats, it will process the sum of goal conversions + * Given an array of stats, it will process the sum of goal conversions * and sum of revenue and add it in the stats array in two new fields. - * + * * @param array $interestByLabel Passed by reference, it will be modified as follows: - * Input: - * array( - * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X, + * Input: + * array( + * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X, * Piwik_Archive::INDEX_GOALS => array( - * idgoal1 => array( [...] ), + * idgoal1 => array( [...] ), * idgoal2 => array( [...] ), * ), * [...] ), * LABEL2 => array( Piwik_Archive::INDEX_NB_VISITS => Y, [...] ) * ); - * - * + * + * * Output: * array( - * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X, + * LABEL => array( Piwik_Archive::INDEX_NB_VISITS => X, * Piwik_Archive::INDEX_NB_CONVERSIONS => Y, // sum of all conversions * Piwik_Archive::INDEX_REVENUE => Z, // sum of all revenue * Piwik_Archive::INDEX_GOALS => array( - * idgoal1 => array( [...] ), + * idgoal1 => array( [...] ), * idgoal2 => array( [...] ), * ), * [...] ), @@ -726,7 +928,7 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing } $values[Piwik_Archive::INDEX_NB_CONVERSIONS] = $conversions; - // 25.00 recorded as 25 + // 25.00 recorded as 25 if(round($revenue) == $revenue) { $revenue = round($revenue); @@ -773,16 +975,16 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing { if($idGoal > Piwik_Tracker_GoalManager::IDGOAL_ORDER) { - return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0, - Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, - Piwik_Archive::INDEX_GOAL_REVENUE => 0, + return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0, + Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, + Piwik_Archive::INDEX_GOAL_REVENUE => 0, ); } if($idGoal == Piwik_Tracker_GoalManager::IDGOAL_ORDER) { - return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0, - Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, - Piwik_Archive::INDEX_GOAL_REVENUE => 0, + return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0, + Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, + Piwik_Archive::INDEX_GOAL_REVENUE => 0, Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SUBTOTAL => 0, Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_TAX => 0, Piwik_Archive::INDEX_GOAL_ECOMMERCE_REVENUE_SHIPPING => 0, @@ -791,9 +993,9 @@ class Piwik_ArchiveProcessing_Day extends Piwik_ArchiveProcessing ); } // $row['idgoal'] == Piwik_Tracker_GoalManager::IDGOAL_CART - return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0, - Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, - Piwik_Archive::INDEX_GOAL_REVENUE => 0, + return array( Piwik_Archive::INDEX_GOAL_NB_CONVERSIONS => 0, + Piwik_Archive::INDEX_GOAL_NB_VISITS_CONVERTED => 0, + Piwik_Archive::INDEX_GOAL_REVENUE => 0, Piwik_Archive::INDEX_GOAL_ECOMMERCE_ITEMS => 0, ); } diff --git a/core/Segment.php b/core/Segment.php index 2cd8894d9d557addff30bd7a24b5da4e75fd092e..c79d2e7316ebbde62e6f15f1e57dbbf30f52016a 100644 --- a/core/Segment.php +++ b/core/Segment.php @@ -1,11 +1,11 @@ <?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 Piwik */ @@ -22,7 +22,7 @@ class Piwik_Segment protected $segment = null; /** - * Truncate the Segments to 4k + * Truncate the Segments to 4k */ const SEGMENT_TRUNCATE_LIMIT = 4096; @@ -30,10 +30,10 @@ class Piwik_Segment { $string = Piwik_Common::unsanitizeInputValue($string); $string = trim($string); - if( !Piwik_Archive::isSegmentationEnabled() + if( !Piwik_Archive::isSegmentationEnabled() && !empty($string)) { - throw new Exception("The Super User has disabled the use of 'segments' for the anonymous user. + throw new Exception("The Super User has disabled the use of 'segments' for the anonymous user. Please log in to use Segmentation in the API."); } // As a preventive measure, we restrict the filter size to a safe limit @@ -77,7 +77,7 @@ class Piwik_Segment { $expressions = $this->segment->parsedSubExpressions; $uniqueFields = array(); - foreach($expressions as $expression) + foreach($expressions as $expression) { $uniqueFields[] = $expression[Piwik_SegmentExpression::INDEX_OPERAND][0]; } @@ -112,8 +112,8 @@ class Piwik_Segment throw new Exception("You do not have enough permission to access the segment ".$name); } -// $this->segmentsHumanReadable[] = $segment['name'] . " " . -// $this->getNameForMatchType($matchType) . +// $this->segmentsHumanReadable[] = $segment['name'] . " " . +// $this->getNameForMatchType($matchType) . // $value; // apply presentation filter @@ -166,7 +166,7 @@ class Piwik_Segment return $return; } - protected function isSegmentAvailable($allowedSegments) + public function isSegmentAvailable($allowedSegments) { $segments = $this->getUniqueSqlFields(); foreach($segments as $segment)