Skip to content
Extraits de code Groupes Projets
API.php 27,8 ko
Newer Older
  • Learn to ignore specific revisions
  • <?php
    
    /**
     * Piwik - Open source web analytics
    
     * @link http://piwik.org
    
    robocoder's avatar
    robocoder a validé
     * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
    
    robocoder's avatar
    robocoder a validé
     * @category Piwik_Plugins
     * @package Piwik_API
    
    robocoder's avatar
    robocoder a validé
    /**
     * @package Piwik_API
     */
    
    		return array(
    
    			'description' => Piwik_Translate('API_PluginDescription'),
    
    			'author' => 'Piwik',
    
    			'author_homepage' => 'http://piwik.org/',
    
    			'version' => Piwik_Version::VERSION,
    
    			'AssetManager.getCssFiles' => 'getCssFiles',
    			'TopMenu.add' => 'addTopMenu',
    
    		Piwik_AddTopMenu('General_API', array('module' => 'API', 'action' => 'listAllAPI'), true, 7);
    
    		
    		if(empty($_SERVER['HTTP_USER_AGENT']))
    		{
    			return;
    		}
    		require_once PIWIK_INCLUDE_PATH . '/libs/UserAgentParser/UserAgentParser.php';
    		$os = UserAgentParser::getOperatingSystem($_SERVER['HTTP_USER_AGENT']);
    		if($os && in_array($os['id'], array('AND', 'IPD', 'IPA', 'IPH')))
    		{
    			Piwik_AddTopMenu('Piwik Mobile App', array('module' => 'Proxy', 'action' => 'redirect', 'url' => 'http://piwik.org/mobile/'), true, 4);
    		}
    
    mattpiwik's avatar
    mattpiwik a validé
    	public function getCssFiles($notification) {
    
    		$cssFiles = &$notification->getNotificationObject();
    		
    
    robocoder's avatar
    robocoder a validé
    		$cssFiles[] = "plugins/API/css/styles.css";
    
    mattpiwik's avatar
    mattpiwik a validé
    
    
    
    robocoder's avatar
    robocoder a validé
    /**
    
    mattpiwik's avatar
    mattpiwik a validé
     * This API is the <a href='http://piwik.org/docs/analytics-api/metadata/' target='_blank'>Metadata API</a>: it gives information about all other available APIs methods, as well as providing
    
     * human readable and more complete outputs than normal API methods.
     *
     * Some of the information that is returned by the Metadata API:
    
    mattpiwik's avatar
    mattpiwik a validé
     * <ul>
    
     * <li>the dynamically generated list of all API methods via "getReportMetadata"</li>
    
    mattpiwik's avatar
    mattpiwik a validé
     * <li>the list of metrics that will be returned by each method, along with their human readable name, via "getDefaultMetrics" and "getDefaultProcessedMetrics"</li>
     * <li>the list of segments metadata supported by all functions that have a 'segment' parameter</li>
    
     * <li>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.
    
    mattpiwik's avatar
    mattpiwik a validé
     * </ul>
     * 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 <a href='http://piwik.org/docs/analytics-api/metadata/' target='_blank'>Metadata API documentation page</a>
    
    robocoder's avatar
    robocoder a validé
     * @package Piwik_API
     */
    
    class Piwik_API_API
    
    mattpiwik's avatar
    mattpiwik a validé
    {
    
    mattpiwik's avatar
    mattpiwik a validé
    	static private $instance = null;
    
    	/**
    	 * @return Piwik_API_API
    	 */
    	static public function getInstance()
    	{
    		if (self::$instance == null)
    		{
    
    mattpiwik's avatar
    mattpiwik a validé
    		}
    		return self::$instance;
    	}
    
    
    	public function getDefaultMetrics()
    
    mattpiwik's avatar
    mattpiwik a validé
    	{
    		$translations = array(
    			// Standard metrics
        		'nb_visits' => 'General_ColumnNbVisits',
    
        		'nb_uniq_visitors' => 'General_ColumnNbUniqVisitors',
    
    mattpiwik's avatar
    mattpiwik a validé
        		'nb_actions' => 'General_ColumnNbActions',
    // Do not display these in reports, as they are not so relevant
    
    mattpiwik's avatar
    mattpiwik a validé
    // They are used to process metrics below
    //			'nb_visits_converted' => 'General_ColumnVisitsWithConversions',
    
    mattpiwik's avatar
    mattpiwik a validé
    //    		'max_actions' => 'General_ColumnMaxActions',
    //    		'sum_visit_length' => 'General_ColumnSumVisitLength',
    //			'bounce_count'
    		);
    		$translations = array_map('Piwik_Translate', $translations);
    		return $translations;
    	}
    
    	public function getDefaultProcessedMetrics()
    	{
    		$translations = array(
    
    mattpiwik's avatar
    mattpiwik a validé
    			// Processed in AddColumnsProcessedMetrics
    
    mattpiwik's avatar
    mattpiwik a validé
    			'nb_actions_per_visit' => 'General_ColumnActionsPerVisit',
        		'avg_time_on_site' => 'General_ColumnAvgTimeOnSite',
        		'bounce_rate' => 'General_ColumnBounceRate',
    
    mattpiwik's avatar
    mattpiwik a validé
        		'conversion_rate' => 'General_ColumnConversionRate',
    
    mattpiwik's avatar
    mattpiwik a validé
    		);
    		return array_map('Piwik_Translate', $translations);
    	}
    	
    
    	public function getDefaultMetricsDocumentation()
    	{
    		$documentation = array(
    			'nb_visits' => 'General_ColumnNbVisitsDocumentation',
        		'nb_uniq_visitors' => 'General_ColumnNbUniqVisitorsDocumentation',
        		'nb_actions' => 'General_ColumnNbActionsDocumentation',
    			'nb_actions_per_visit' => 'General_ColumnActionsPerVisitDocumentation',
        		'avg_time_on_site' => 'General_ColumnAvgTimeOnSiteDocumentation',
        		'bounce_rate' => 'General_ColumnBounceRateDocumentation',
        		'conversion_rate' => 'General_ColumnConversionRateDocumentation',
    			'avg_time_on_page' => 'General_ColumnAverageTimeOnPageDocumentation',
    			'nb_hits' => 'General_ColumnPageviewsDocumentation',
    			'exit_rate' => 'General_ColumnExitRateDocumentation'
    		);
    		return array_map('Piwik_Translate', $documentation);
    	}
    	
    
    mattpiwik's avatar
    mattpiwik a validé
    	public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true)
    	{
    		$segments = array();
    		Piwik_PostEvent('API.getSegmentsMetadata', $segments, $idSites);
    		
    		$segments[] = array(
    		        'type' => 'dimension',
    		        'category' => 'Visit',
    		        'name' => 'General_VisitorIP',
    		        'segment' => 'visitIp',
    
    		        'sqlSegment' => 'log_visit.location_ip',
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'permission' => Piwik::isUserHasAdminAccess($idSites),
    
    	    );
    		$segments[] = array(
    		        'type' => 'dimension',
    		        'category' => 'Visit',
    		        'name' => 'General_VisitorID',
    		        'segment' => 'visitorId',
    				'acceptedValues' => '34c31e04394bdc63 - any 16 chars ID requested via the Tracking API function getVisitorId()',
    
    		        'sqlSegment' => 'log_visit.idvisitor',
    
    		        'sqlFilter' => array('Piwik_Common', 'convertVisitorIdToBin'),
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    		$segments[] = array(
    		        'type' => 'metric',
    		        'category' => 'Visit',
    
    		        'name' => 'General_NbActions',
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'segment' => 'actions',
    
    		        'sqlSegment' => 'log_visit.visit_total_actions',
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    		$segments[] = array(
    		        'type' => 'metric',
    		        'category' => 'Visit',
    		        'name' => 'General_ColumnVisitDuration',
    		        'segment' => 'visitDuration',
    
    		        'sqlSegment' => 'log_visit.visit_total_time',
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    		$segments[] = array(
    		        'type' => 'dimension',
    		        'category' => 'Visit',
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'name' => Piwik_Translate('General_VisitType') . ". ".Piwik_Translate('General_VisitTypeExample', '"&segment=visitorType==returning,visitorType==returningCustomer"'),
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'segment' => 'visitorType',
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'acceptedValues' => 'new, returning, returningCustomer',
    
    		        'sqlSegment' => 'log_visit.visitor_returning',
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'sqlFilter' => create_function('$type', 'return $type == "new" ? 0 : ($type == "returning" ? 1 : 2);'),
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    
    mattpiwik's avatar
    mattpiwik a validé
    		$segments[] = array(
    		        'type' => 'metric',
    		        'category' => 'Visit',
    		        'name' => 'General_DaysSinceLastVisit',
    		        'segment' => 'daysSinceLastVisit',
    
    		        'sqlSegment' => 'log_visit.visitor_days_since_last',
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    		$segments[] = array(
    		        'type' => 'metric',
    		        'category' => 'Visit',
    		        'name' => 'General_DaysSinceFirstVisit',
    		        'segment' => 'daysSinceFirstVisit',
    
    		        'sqlSegment' => 'log_visit.visitor_days_since_first',
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    		$segments[] = array(
    		        'type' => 'metric',
    		        'category' => 'Visit',
    		        'name' => 'General_NumberOfVisits',
    		        'segment' => 'visitCount',
    
    		        'sqlSegment' => 'log_visit.visitor_count_visits',
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    	    
    		$segments[] = array(
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'type' => 'dimension',
    
    mattpiwik's avatar
    mattpiwik a validé
    		        'category' => 'Visit',
    		        'name' => 'General_VisitConvertedGoal',
    		        'segment' => 'visitConverted',
    				'acceptedValues' => '0, 1',
    
    		        'sqlSegment' => 'log_visit.visit_goal_converted',
    
    mattpiwik's avatar
    mattpiwik a validé
    	    );
    
    		        'category' => 'Visit',
    		        'name' => Piwik_Translate('General_EcommerceVisitStatus', '"&segment=visitEcommerceStatus==ordered,visitEcommerceStatus==orderedThenAbandonedCart"'),
    		        'segment' => 'visitEcommerceStatus',
    				'acceptedValues' => implode(", ", self::$visitEcommerceStatus),
    
    		        'sqlSegment' => 'log_visit.visit_goal_buyer',
    
    		        'sqlFilter' => array('Piwik_API_API', 'getVisitEcommerceStatus'),
    	    );
    	    
    	    $segments[] = array(
    		        'type' => 'metric',
    		        'category' => 'Visit',
    		        'name' => 'General_DaysSinceLastEcommerceOrder',
    		        'segment' => 'daysSinceLastEcommerceOrder',
    
    		        'sqlSegment' => 'log_visit.visitor_days_since_order',
    
    		foreach ($segments as &$segment)
    
    mattpiwik's avatar
    mattpiwik a validé
    		{
    		    $segment['name'] = Piwik_Translate($segment['name']);
    		    $segment['category'] = Piwik_Translate($segment['category']);
    		    
    		    if($_hideImplementationData)
    		    {
    		    	unset($segment['sqlFilter']);
    		    	unset($segment['sqlSegment']);
    		    }
    		}
    
    		
    		usort($segments, array($this, 'sortSegments'));
    
    mattpiwik's avatar
    mattpiwik a validé
    		return $segments;
    	}
    	
    
    	static protected $visitEcommerceStatus = array(
    		Piwik_Tracker_GoalManager::TYPE_BUYER_NONE => 'none',
    		Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED => 'ordered',
    		Piwik_Tracker_GoalManager::TYPE_BUYER_OPEN_CART => 'abandonedCart',
    		Piwik_Tracker_GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART => 'orderedThenAbandonedCart',
    	);
    	
    	static public function getVisitEcommerceStatusFromId($id)
    	{
    		if(!isset(self::$visitEcommerceStatus[$id]))
    		{
    			throw new Exception("Unexpected ECommerce status value ");
    		}
    		return self::$visitEcommerceStatus[$id];
    	}
    	
    	static public function getVisitEcommerceStatus($status)
    	{
    		$id = array_search($status, self::$visitEcommerceStatus);
    		if($id === false)
    		{
    			throw new Exception("Invalid 'visitEcommerceStatus' segment value");
    		}
    		return $id;
    	}
    	
    
    	private function sortSegments($row1, $row2)
    	{
    		$columns = array('type', 'category', 'name', 'segment');
    		foreach($columns as $column)
    		{
    
    			// Keep segments ordered alphabetically inside categories..
    			$type = -1;
    			if($column == 'name') $type = 1;
    			$compare = $type * strcmp($row1[$column], $row2[$column]);
    
    mattpiwik's avatar
    mattpiwik a validé
    			
    			// hack so that custom variables "page" are grouped together in the doc
    			if($row1['category'] == Piwik_Translate('CustomVariables_CustomVariables')
    				&& $row1['category'] == $row2['category']) {
    				$compare = strcmp($row1['segment'], $row2['segment']);
    				return $compare;
    			}
    
    			if($compare != 0){
    				return $compare;
    			}
    		}
    		return $compare;
    	}
    
    	
    	/**
    	 * Returns the url to application logo (~280x110px)
    
    robocoder's avatar
    robocoder a validé
    	 *
    	 * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
    	 * @return string
    
    	 */
    	public function getLogoUrl($pathOnly=false)
    	{
    
    mattpiwik's avatar
    mattpiwik a validé
            $logo = 'themes/default/images/logo.png';
    	    if(Zend_Registry::get('config')->branding->use_custom_logo == 1 
    	    	&& file_exists(Piwik_Common::getPathToPiwikRoot() .'/themes/logo.png')) 
    	    {
    
    	        $logo = 'themes/logo.png';
    
    mattpiwik's avatar
    mattpiwik a validé
    	    } 
    
    	    if(!$pathOnly) {
    	        return Piwik::getPiwikUrl() . $logo;
    
    mattpiwik's avatar
    mattpiwik a validé
    	    } 
    	    return Piwik_Common::getPathToPiwikRoot() .'/'. $logo;
    
    	}
    	
    	/**
    	 * Returns the url to header logo (~127x50px)
    
    robocoder's avatar
    robocoder a validé
    	 *
    	 * @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
    	 * @return string
    
    	 */
    	public function getHeaderLogoUrl($pathOnly=false)
    	{
    
    mattpiwik's avatar
    mattpiwik a validé
            $logo = 'themes/default/images/logo-header.png';
    	    if(Zend_Registry::get('config')->branding->use_custom_logo == 1 
    	    	&& file_exists(Piwik_Common::getPathToPiwikRoot() .'/themes/logo-header.png')) 
    	    {
    
    	        $logo = 'themes/logo-header.png';
    
    mattpiwik's avatar
    mattpiwik a validé
    	    } 
    
    	    if(!$pathOnly) {
    	        return Piwik::getPiwikUrl() . $logo;
    	    }
    
    mattpiwik's avatar
    mattpiwik a validé
    	    return Piwik_Common::getPathToPiwikRoot() .'/'. $logo;
    
    mattpiwik's avatar
    mattpiwik a validé
        /**
    
         * Loads reports metadata, then return the requested one,
    
    mattpiwik's avatar
    mattpiwik a validé
         * matching optional API parameters.
    
    mattpiwik's avatar
    mattpiwik a validé
    	public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false, $period = false, $date = false)
    
    mattpiwik's avatar
    mattpiwik a validé
        	Piwik_Translate::getInstance()->reloadLanguage($language);
    
    mattpiwik's avatar
    mattpiwik a validé
        	$reportsMetadata = $this->getReportMetadata($idSite, $period, $date);
    
    mattpiwik's avatar
    mattpiwik a validé
        	foreach($reportsMetadata as $report)
    
    mattpiwik's avatar
    mattpiwik a validé
        		// See ArchiveProcessing/Period.php - unique visitors are not processed for period != day
    
    	    	if(($period && $period != 'day')
    
    	    		&& !($apiModule == 'VisitsSummary'
    
    mattpiwik's avatar
    mattpiwik a validé
    	    			&& $apiAction == 'get'))
    	    	{
    	    		unset($report['metrics']['nb_uniq_visitors']);
    	    	}
    
        		if($report['module'] == $apiModule
        			&& $report['action'] == $apiAction)
    			{
    
    mattpiwik's avatar
    mattpiwik a validé
    				// No custom parameters 
    				if(empty($apiParameters)
    					&& empty($report['parameters']))
    
    				{
            			return array($report);
    				}
    				if(empty($report['parameters']))
    				{
    					continue;
    				}
    				$diff = array_diff($report['parameters'], $apiParameters);
    				if(empty($diff))
    				{
    					return array($report);
    				}
    			}
        	}
        	return false;
        }
        
    
    mattpiwik's avatar
    mattpiwik a validé
    	/**
    	 * Triggers a hook to ask plugins for available Reports.
    
    	 * Returns metadata information about each report (category, name, dimension, metrics, etc.)
    
    mattpiwik's avatar
    mattpiwik a validé
    	 *
    
    mattpiwik's avatar
    mattpiwik a validé
    	 * @param string $idSites Comma separated list of website Ids
    
    mattpiwik's avatar
    mattpiwik a validé
    	 * @return array
    	 */
    
    mattpiwik's avatar
    mattpiwik a validé
    	public function getReportMetadata($idSites = '', $period = false, $date = false)
    
    mattpiwik's avatar
    mattpiwik a validé
    	{
    		$idSites = Piwik_Site::getIdSitesFromIdSitesString($idSites);
    
    mattpiwik's avatar
    mattpiwik a validé
    		if(!empty($idSites))
    		{
    			Piwik::checkUserHasViewAccess($idSites);
    		}
    		
    		$parameters = array( 'idSites' => $idSites, 'period' => $period, 'date' => $date);
    
    mattpiwik's avatar
    mattpiwik a validé
    		
    
    mattpiwik's avatar
    mattpiwik a validé
    		$availableReports = array();
    
    mattpiwik's avatar
    mattpiwik a validé
    		Piwik_PostEvent('API.getReportMetadata', $availableReports, $parameters);
    
    mattpiwik's avatar
    mattpiwik a validé
    		foreach ($availableReports as &$availableReport) {
    			if (!isset($availableReport['metrics'])) {
    				$availableReport['metrics'] = $this->getDefaultMetrics();
    			}
    			if (!isset($availableReport['processedMetrics'])) {
    				$availableReport['processedMetrics'] = $this->getDefaultProcessedMetrics();
    			}
    
    			if (!isset($availableReport['metricsDocumentation'])) {
    				$availableReport['metricsDocumentation'] = $this->getDefaultMetricsDocumentation();
    			}
    
    mattpiwik's avatar
    mattpiwik a validé
    		}
    		
    		// Some plugins need to add custom metrics after all plugins hooked in
    
    mattpiwik's avatar
    mattpiwik a validé
    		Piwik_PostEvent('API.getReportMetadata.end', $availableReports, $parameters);
    
    mattpiwik's avatar
    mattpiwik a validé
    		
    
    mattpiwik's avatar
    mattpiwik a validé
    		// Sort results to ensure consistent order
    		usort($availableReports, array($this, 'sort'));
    
    
    mattpiwik's avatar
    mattpiwik a validé
    		$knownMetrics = array_merge( $this->getDefaultMetrics(), $this->getDefaultProcessedMetrics() );
    		foreach($availableReports as &$availableReport)
    		{
    
    mattpiwik's avatar
    mattpiwik a validé
    			// Ensure all metrics have a translation
    
    mattpiwik's avatar
    mattpiwik a validé
    			$metrics = $availableReport['metrics'];
    			$cleanedMetrics = array();
    			foreach($metrics as $metricId => $metricTranslation)
    			{
    
    mattpiwik's avatar
    mattpiwik a validé
    				// When simply the column name was given, ie 'metric' => array( 'nb_visits' )
    				// $metricTranslation is in this case nb_visits. We look for a known translation.
    
    mattpiwik's avatar
    mattpiwik a validé
    				if(is_numeric($metricId)
    					&& isset($knownMetrics[$metricTranslation]))
    				{
    					$metricId = $metricTranslation;
    					$metricTranslation = $knownMetrics[$metricTranslation];
    				}
    				$cleanedMetrics[$metricId] = $metricTranslation;
    			}
    			$availableReport['metrics'] = $cleanedMetrics;
    
    mattpiwik's avatar
    mattpiwik a validé
    			
    			// Remove array elements that are false (to clean up API output)
    			foreach($availableReport as $attributeName => $attributeValue)
    			{
    				if(empty($attributeValue))
    				{
    					unset($availableReport[$attributeName]);
    				}
    			}
    
    mattpiwik's avatar
    mattpiwik a validé
            	// when there are per goal metrics, don't display conversion_rate since it can differ from per goal sum
    	        if(isset($availableReport['metricsGoal']))
    	        {
    	        	unset($availableReport['processedMetrics']['conversion_rate']);
    	        	unset($availableReport['metricsGoal']['conversion_rate']);
    	        }
    
    mattpiwik's avatar
    mattpiwik a validé
    			
    
    			// Processing a uniqueId for each report,
    
    mattpiwik's avatar
    mattpiwik a validé
    			// can be used by UIs as a key to match a given report
    			$uniqueId = $availableReport['module'] . '_' . $availableReport['action'];
    			if(!empty($availableReport['parameters']))
    			{
    				foreach($availableReport['parameters'] as $key => $value)
    				{
    					$uniqueId .= '_' . $key . '--' . $value;
    				}
    			}
    			$availableReport['uniqueId'] = $uniqueId;
    
    mattpiwik's avatar
    mattpiwik a validé
    			
    			// Order is used to order reports internally, but not meant to be used outside
    			unset($availableReport['order']);
    
    mattpiwik's avatar
    mattpiwik a validé
    		}
    
    mattpiwik's avatar
    mattpiwik a validé
    		
    
    mattpiwik's avatar
    mattpiwik a validé
    		return $availableReports;
    	}
    
    mattpiwik's avatar
    mattpiwik a validé
    
    
    mattpiwik's avatar
    mattpiwik a validé
    	public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false, $apiParameters = false, $idGoal = false, $language = false, $showTimer = true)
    
    mattpiwik's avatar
    mattpiwik a validé
        {
    
    mattpiwik's avatar
    mattpiwik a validé
        	$timer = new Piwik_Timer();
    
    mattpiwik's avatar
    mattpiwik a validé
        	if($apiParameters === false)
        	{
        		$apiParameters = array();
        	}
    
    mattpiwik's avatar
    mattpiwik a validé
    		if(!empty($idGoal)
    			&& empty($apiParameters['idGoal']))
    		{
    			$apiParameters['idGoal'] = $idGoal;
    		}
    
    mattpiwik's avatar
    mattpiwik a validé
            // Is this report found in the Metadata available reports?
    
    mattpiwik's avatar
    mattpiwik a validé
            $reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language, $period, $date);
    
            if(empty($reportMetadata))
    
    mattpiwik's avatar
    mattpiwik a validé
            {
    
    mattpiwik's avatar
    mattpiwik a validé
            	throw new Exception("Requested report $apiModule.$apiAction for Website id=$idSite not found in the list of available reports. \n");
    
    mattpiwik's avatar
    mattpiwik a validé
            }
    
            $reportMetadata = reset($reportMetadata);
    
    mattpiwik's avatar
    mattpiwik a validé
            
    		// Generate Api call URL passing custom parameters
    		$parameters = array_merge( $apiParameters, array(
    			'method' => $apiModule.'.'.$apiAction,
    			'idSite' => $idSite,
    			'period' => $period,
    			'date' => $date,
    			'format' => 'original',
    
    mattpiwik's avatar
    mattpiwik a validé
    			'serialize' => '0',
    			'language' => $language,
    
    mattpiwik's avatar
    mattpiwik a validé
    		));
    
    mattpiwik's avatar
    mattpiwik a validé
    		if(!empty($segment)) $parameters['segment'] = $segment;
    		
    
    mattpiwik's avatar
    mattpiwik a validé
    		$url = Piwik_Url::getQueryStringFromParameters($parameters);
            $request = new Piwik_API_Request($url);
            try {
            	/** @var Piwik_DataTable */
            	$dataTable = $request->process();
            } catch(Exception $e) {
            	throw new Exception("API returned an error: ".$e->getMessage()."\n");
            }
    
    
        	list($newReport, $columns, $rowsMetadata) = $this->handleTableReport($idSite, $dataTable, $reportMetadata, isset($reportMetadata['dimension']));
    
    mattpiwik's avatar
    mattpiwik a validé
        	foreach($columns as $columnId => &$name)
        	{
        		$name = ucfirst($name);
        	}
        	$website = new Piwik_Site($idSite);
    
    mattpiwik's avatar
    mattpiwik a validé
    //    	$segment = new Piwik_Segment($segment, $idSite);
    
    
    		if(Piwik_Archive::isMultiplePeriod($date, $period))
    		{
    			$period =  new Piwik_Period_Range($period, $date);
    		}
    		else
    		{
    			if($period == 'range')
    			{
    				$period = new Piwik_Period_Range($period, $date);
    			}
    			else
    			{
    				$period = Piwik_Period::factory($period, Piwik_Date::factory($date));
    			}
    		}
    
    		$period = $period->getLocalizedLongString();
    
    mattpiwik's avatar
    mattpiwik a validé
        	
    
    mattpiwik's avatar
    mattpiwik a validé
        	$return = array(
    
    mattpiwik's avatar
    mattpiwik a validé
    				'website' => $website->getName(),
    
    				'prettyDate' => $period,
    
    mattpiwik's avatar
    mattpiwik a validé
    //    			'prettySegment' => $segment->getPrettyString(),
    
    				'metadata' => $reportMetadata,
    				'columns' => $columns,
    				'reportData' =>	$newReport,
    
    mattpiwik's avatar
    mattpiwik a validé
    				'reportMetadata' => $rowsMetadata,
    		);
    
    mattpiwik's avatar
    mattpiwik a validé
    		if($showTimer)
    		{
    			$return['timerMillis'] = $timer->getTimeMs(0);
    		}
    		return $return;
    
    mattpiwik's avatar
    mattpiwik a validé
        }
    
    
    	/**
    	 * Enhance a $dataTable using metadata :
    	 *
    	 * - remove metrics based on $reportMetadata['metrics']
    	 * - add 0 valued metrics if $dataTable doesn't provide all $reportMetadata['metrics']
    	 * - format metric values to a 'human readable' format
    	 * - extract row metadata to a separate Piwik_DataTable_Simple|Piwik_DataTable_Array : $rowsMetadata
    	 * - translate metric names to a separate array : $columns
    	 *
    	 * @param int $idSite enables monetary value formatting based on site currency
    	 * @param Piwik_DataTable|Piwik_DataTable_Array $dataTable
    	 * @param array $reportMetadata
    	 * @param boolean $hasDimension
    	 * @return array Piwik_DataTable_Simple|Piwik_DataTable_Array $newReport with human readable format & array $columns list of translated column names & Piwik_DataTable_Simple|Piwik_DataTable_Array $rowsMetadata
    	**/
        private function handleTableReport($idSite, $dataTable, &$reportMetadata, $hasDimension)
    
    mattpiwik's avatar
    mattpiwik a validé
        {
    
        	$columns = $reportMetadata['metrics'];
    
    		if($hasDimension)
    		{
    			$columns = array_merge(
    				array('label' => $reportMetadata['dimension'] ),
    				$columns
    			);
    
    			if(isset($reportMetadata['processedMetrics']))
    			{
    				$processedMetricsAdded = $this->getDefaultProcessedMetrics();
    				foreach($processedMetricsAdded as $processedMetricId => $processedMetricTranslation)
    				{
    					// this processed metric can be displayed for this report
    					if(isset($reportMetadata['processedMetrics'][$processedMetricId]))
    					{
    						$columns[$processedMetricId] = $processedMetricTranslation;
    					}
    				}
    			}
    
    			// Display the global Goal metrics
    			if(isset($reportMetadata['metricsGoal']))
    			{
    				$metricsGoalDisplay = array('revenue');
    				// Add processed metrics to be displayed for this report
    				foreach($metricsGoalDisplay as $goalMetricId)
    				{
    					if(isset($reportMetadata['metricsGoal'][$goalMetricId]))
    					{
    						$columns[$goalMetricId] = $reportMetadata['metricsGoal'][$goalMetricId];
    					}
    				}
    			}
    
    			if(isset($reportMetadata['processedMetrics']))
    			{
    				// Add processed metrics
    				$dataTable->filter('AddColumnsProcessedMetrics', array($deleteRowsWithNoVisit = false));
    			}
    		}
    
    		// $dataTable is an instance of Piwik_DataTable_Array when multiple periods requested
    		if ($dataTable instanceof Piwik_DataTable_Array)
    		{
    			// Need a new Piwik_DataTable_Array to store the 'human readable' values
    			$newReport = new Piwik_DataTable_Array();
    			$newReport->setKeyName("prettyDate");
    			$dataTableMetadata = $dataTable->metadata;
    			$newReport->metadata = $dataTableMetadata;
    
    			// Need a new Piwik_DataTable_Array to store report metadata
    			$rowsMetadata = new Piwik_DataTable_Array();
    			$rowsMetadata->setKeyName("prettyDate");
    
    			// Process each Piwik_DataTable_Simple entry
    			foreach($dataTable->getArray() as $label => $simpleDataTable)
    			{
    				list($enhancedSimpleDataTable, $rowMetadata) = $this->handleSimpleDataTable($idSite, $simpleDataTable, $columns, $hasDimension);
    
    				$period = $dataTableMetadata[$label]['period']->getLocalizedLongString();
    				$newReport->addTable($enhancedSimpleDataTable, $period);
    				$rowsMetadata->addTable($rowMetadata, $period);
    			}
    		}
    		else
    		{
    			list($newReport, $rowsMetadata) = $this->handleSimpleDataTable($idSite, $dataTable, $columns, $hasDimension);
    		}
    
    mattpiwik's avatar
    mattpiwik a validé
    
        	return array(
    
    mattpiwik's avatar
    mattpiwik a validé
        		$columns,
    
        		$rowsMetadata
    
    mattpiwik's avatar
    mattpiwik a validé
        	);
        }
    
    
    	/**
    	 * Enhance $simpleDataTable using metadata :
    	 *
    	 * - remove metrics based on $reportMetadata['metrics']
    	 * - add 0 valued metrics if $simpleDataTable doesn't provide all $reportMetadata['metrics']
    	 * - format metric values to a 'human readable' format
    	 * - extract row metadata to a separate Piwik_DataTable_Simple $rowsMetadata
    	 *
    	 * @param int $idSite enables monetary value formatting based on site currency
    	 * @param Piwik_DataTable_Simple $simpleDataTable
    	 * @param array $metadataColumns
    	 * @param boolean $hasDimension
    	 * @return array Piwik_DataTable $enhancedDataTable filtered metrics with human readable format & Piwik_DataTable_Simple $rowsMetadata
    	 */
    	private function handleSimpleDataTable($idSite, $simpleDataTable, $metadataColumns, $hasDimension)
    	{
    		// new DataTable to store metadata
    		$rowsMetadata = new Piwik_DataTable();
    		
    		// new DataTable to store 'human readable' values
    		if($hasDimension)
    		{
    			$enhancedDataTable = new Piwik_DataTable();
    		}
    		else
    		{
    			$enhancedDataTable = new Piwik_DataTable_Simple();
    		}
    
    		// add missing metrics
    		foreach($simpleDataTable->getRows() as $row)
    		{
    			$rowMetrics = $row->getColumns();
        		foreach($metadataColumns as $id => $name)
    
    mattpiwik's avatar
    mattpiwik a validé
        		{
    
        			if(!isset($rowMetrics[$id]))
    
    mattpiwik's avatar
    mattpiwik a validé
        			{
    
    					$row->addColumn($id, 0);
    
    mattpiwik's avatar
    mattpiwik a validé
        			}
        		}
    
    		}
    
    		foreach($simpleDataTable->getRows() as $row)
    		{
    			$enhancedRow = new Piwik_DataTable_Row();
    			$enhancedDataTable->addRow($enhancedRow);
    
    			$rowMetrics = $row->getColumns();
    			foreach($rowMetrics as $columnName => $columnValue)
    			{
    				// filter metrics according to metadata definition
    				if(isset($metadataColumns[$columnName]))
    				{
    					// generate 'human readable' metric values
    					$prettyValue = Piwik::getPrettyValue($idSite, $columnName, $columnValue, false, false);
    					$enhancedRow->addColumn($columnName, $prettyValue);
    				}
    			}
    
    			// If report has a dimension, extract metadata into a distinct DataTable
    			if($hasDimension)
    			{
    				$rowMetadata = $row->getMetadata();
    				$idSubDataTable = $row->getIdSubDataTable();
    
    				// Create a row metadata only if there are metadata to insert
    				if(count($rowMetadata) > 0 || !is_null($idSubDataTable))
    				{
    					$metadataRow = new Piwik_DataTable_Row();
    					$rowsMetadata->addRow($metadataRow);
    
    					foreach($rowMetadata as $metadataKey => $metadataValue)
    					{
    						$metadataRow->addColumn($metadataKey, $metadataValue);
    					}
    
    					if(!is_null($idSubDataTable))
    					{
    						$metadataRow->addColumn('idsubdatatable', $idSubDataTable);
    					}
    				}
    			}
    		}
    
    		return array(
        		$enhancedDataTable,
    
    mattpiwik's avatar
    mattpiwik a validé
        		$rowsMetadata
        	);
    
    mattpiwik's avatar
    mattpiwik a validé
    
    
    mattpiwik's avatar
    mattpiwik a validé
    	/**
    
    	 * API metadata are sorted by category/name,
    
    mattpiwik's avatar
    mattpiwik a validé
    	 * with a little tweak to replicate the standard Piwik category ordering
    
    	 * @param string $a
    	 * @param string $b
    
    mattpiwik's avatar
    mattpiwik a validé
    	 * @return int
    	 */
    
    	private function sort($a, $b)
    	{
    
    mattpiwik's avatar
    mattpiwik a validé
    		static $order = null;
    		if(is_null($order))
    		{
    			$order = array(
    				Piwik_Translate('VisitsSummary_VisitsSummary'),
    
    JulienMoumne's avatar
    JulienMoumne a validé
    				Piwik_Translate('Goals_Ecommerce'),
    
    mattpiwik's avatar
    mattpiwik a validé
    				Piwik_Translate('Actions_Actions'),
    				Piwik_Translate('Referers_Referers'),
    				Piwik_Translate('Goals_Goals'),
    				Piwik_Translate('General_Visitors'),
    				Piwik_Translate('UserSettings_VisitorSettings'),
    			);
    		}
    
    		return ($category = strcmp(array_search($a['category'], $order), array_search($b['category'], $order))) == 0
    
    mattpiwik's avatar
    mattpiwik a validé
    				?  (@$a['order'] < @$b['order'] ? -1 : 1)
    
    mattpiwik's avatar
    mattpiwik a validé
    				: $category;
    
    mattpiwik's avatar
    mattpiwik a validé
    	/** 
    	 * Get a combined report of the *.get API methods. 
    	 */
    
    	public function get( $idSite, $period, $date, $segment = false, $columns = false)
    	{
    		$columns = Piwik::getArrayFromApiParameter($columns);
    
    		
    		// build columns map for faster checks later on
    		$columnsMap = array();
    
    			$columnsMap[$column] = true;
    		}
    		
    		// find out which columns belong to which plugin
    		$columnsByPlugin = array();
    		$meta = Piwik_API_API::getInstance()->getReportMetadata($idSite, $period, $date);
    		foreach ($meta as $reportMeta)
    		{
    			// scan all *.get reports
    			if ($reportMeta['action'] == 'get' && !isset($reportMeta['parameters']))
    			{
    				$plugin = $reportMeta['module'];
    				foreach ($reportMeta['metrics'] as $column => $columnTranslation)
    				{
    					// a metric from this report has been requested
    
    mattpiwik's avatar
    mattpiwik a validé
    					if (isset($columnsMap[$column])
    					// or by default, return all metrics
    						|| empty($columnsMap))
    
    					{
    						$columnsByPlugin[$plugin][] = $column;
    					}
    				}
    			}
    
    mattpiwik's avatar
    mattpiwik a validé
    		$params = compact('idSite', 'period', 'date', 'segment', 'idGoal');
    
    		foreach ($columnsByPlugin as $plugin => $columns)
    		{
    			// load the data		
    			$className = 'Piwik_'.$plugin.'_API';
    			$params['columns'] = implode(',', $columns);
    			$dataTable = Piwik_API_Proxy::getInstance()->call($className, 'get', $params);
    			// make sure the table has all columns
    			$array = ($dataTable instanceof Piwik_DataTable_Array ? $dataTable->getArray() : array($dataTable));
    			foreach ($array as $table) 
    			{
    				$firstRow = $table->getFirstRow();
    				if (!$firstRow)
    				{
    					$firstRow = new Piwik_DataTable_Row;
    					$table->addRow($firstRow);
    				}
    				foreach ($columns as $column)
    				{
    					if ($firstRow->getColumn($column) === false)
    					{
    						$firstRow->setColumn($column, 0);
    					}
    				
    				}
    			}
    			
    			// merge reports
    			if ($mergedDataTable === false)
    			{
    				$mergedDataTable = $dataTable;
    			}
    			else
    			{
    				$this->mergeDataTables($mergedDataTable, $dataTable);
    			}
    		}
    		
    		return $mergedDataTable;
    	}
    	
    	
    	/**
    	 * Merge the columns of two data tables.
    	 * Manipulates the first table.
    	 */ 
    	private function mergeDataTables($table1, $table2)
    	{
    		// handle table arrays
    		if ($table1 instanceof Piwik_DataTable_Array && $table2 instanceof Piwik_DataTable_Array)
    		{
    			$subTables2 = $table2->getArray();
    			foreach ($table1->getArray() as $index => $subTable1)
    			{
    				$subTable2 = $subTables2[$index];
    				$this->mergeDataTables($subTable1, $subTable2);
    			}
    			return;
    		}
    		
    		$firstRow1 = $table1->getFirstRow();
    		$firstRow2 = $table2->getFirstRow();
    		foreach ($firstRow2->getColumns() as $metric => $value)
    		{
    			$firstRow1->setColumn($metric, $value);
    		}
    	}