Newer
Older
mattpiwik
a validé
<?php
/**
* Piwik - Open source web analytics
mattpiwik
a validé
* @link http://piwik.org
mattpiwik
a validé
*/
namespace Piwik;
mattab
a validé
use Piwik\Metrics;
mattab
a validé
use Piwik\API\Request;
use Piwik\Period\Range;
use Piwik\Date;
use Piwik\DataTable;
use Piwik\Url;
use Piwik\ViewDataTable\VisualizationPropertiesProxy;
mattab
a validé
use Piwik\Plugins\API\API;
mattpiwik
a validé
/**
* This class is used to load (from the API) and customize the output of a given DataTable.
* The main() method will create an object implementing ViewInterface
mattpiwik
a validé
* You can customize the dataTable using the disable* methods.
benakamoorthi
a validé
* You can also customize the dataTable rendering using row metadata:
* - 'html_label_prefix': If this metadata is present on a row, it's contents will be prepended
* the label in the HTML output.
* - 'html_label_suffix': If this metadata is present on a row, it's contents will be appended
* after the label in the HTML output.
mattpiwik
a validé
* Example:
* In the Controller of the plugin VisitorInterest
* <pre>
* function getNumberOfVisitsPerVisitDuration( $fetch = false)
mattpiwik
a validé
* {
* $view = ViewDataTable::factory( 'cloud' );
* $view->init( $this->pluginName, __FUNCTION__, 'VisitorInterest.getNumberOfVisitsPerVisitDuration' );
* $view->setColumnsToDisplay( array('label','nb_visits') );
* $view->disableSort();
* $view->disableExcludeLowPopulation();
* $view->disableOffsetInformation();
* return $this->renderView($view, $fetch);
* }
mattpiwik
a validé
* </pre>
* @see \Piwik\ViewDataTable\Properties - for core DataTable display properties.
mattpiwik
a validé
* @see factory() for all the available output (cloud tags, html table, pie chart, vertical bar chart)
* @subpackage ViewDataTable
mattpiwik
a validé
*/
class ViewDataTable
mattpiwik
a validé
{
Benaka Moorthi
a validé
/**
Benaka Moorthi
a validé
* This event is called before a visualization is created. Plugins can use this event to
Benaka Moorthi
a validé
* override view properties for individual reports or visualizations.
*
* Themes can use this event to make sure reports look nice with their themes. Plugins
* that provide new visualizations can use this event to make sure certain reports
* are configured differently when viewed with the new visualization.
*
* Callback Signature: function (ViewDataTable $view) {}
*/
const CONFIGURE_VIEW_EVENT = 'ViewDataTable.configureReportView';
/**
* The class name of the visualization to use.
*
* @var string|null
*/
private $visualizationClass;
Benaka Moorthi
a validé
* Cache for getAllReportDisplayProperties result.
*
* @var array
Benaka Moorthi
a validé
public static $reportPropertiesCache = null;
* Array of properties that are available in the view
* Used to store UI properties, eg. "show_footer", "show_search", etc.
*
* @var array
*/
protected $viewProperties = array();
/**
* If the current dataTable refers to a subDataTable (eg. keywordsBySearchEngineId for id=X) this variable is set to the Id
*
* @var bool|int
*/
protected $idSubtable = false;
/**
* DataTable loaded from the API for this ViewDataTable.
*
* @var DataTable
*/
protected $dataTable = null;
/**
* @see init()
* @var string
*/
protected $currentControllerAction;
/**
* @see init()
* @var string
*/
protected $currentControllerName;
/**
* This view should be an implementation of the Interface ViewInterface
* The $view object should be created in the main() method.
*
*/
protected $view = null;
/**
Benaka Moorthi
a validé
* Default constructor.
Benaka Moorthi
a validé
public function __construct($currentControllerAction,
$apiMethodToRequestDataTable,
$viewProperties = array(),
$visualizationId = null)
Benaka Moorthi
a validé
{
Benaka Moorthi
a validé
$visualizationClass = $visualizationId ? DataTableVisualization::getClassFromId($visualizationId) : null;
Benaka Moorthi
a validé
$this->visualizationClass = $visualizationClass;
list($currentControllerName, $currentControllerAction) = explode('.', $currentControllerAction);
$this->currentControllerName = $currentControllerName;
$this->currentControllerAction = $currentControllerAction;
$this->viewProperties['visualization_properties'] = new VisualizationPropertiesProxy($visualizationClass);
Benaka Moorthi
a validé
$this->viewProperties['metadata'] = array();
Benaka Moorthi
a validé
$this->viewProperties['translations'] = array();
Benaka Moorthi
a validé
$this->viewProperties['filters'] = array();
Benaka Moorthi
a validé
$this->viewProperties['after_data_loaded_functions'] = array();
Benaka Moorthi
a validé
$this->viewProperties['related_reports'] = array();
$this->viewProperties['subtable_controller_action'] = $currentControllerAction;
Benaka Moorthi
a validé
$this->setDefaultProperties();
$this->setViewProperties($viewProperties);
$this->overrideViewPropertiesWithQueryParams();
Benaka Moorthi
a validé
$this->idSubtable = Common::getRequestVar('idSubtable', false, 'int');
$this->viewProperties['show_footer_icons'] = ($this->idSubtable == false);
$this->viewProperties['apiMethodToRequestDataTable'] = $apiMethodToRequestDataTable;
$this->viewProperties['report_id'] = $currentControllerName . '.' . $currentControllerAction;
$this->viewProperties['self_url'] = $this->getBaseReportUrl($currentControllerName, $currentControllerAction);
Benaka Moorthi
a validé
Benaka Moorthi
a validé
// the exclude low population threshold value is sometimes obtained by requesting data.
// to avoid issuing unecessary requests when display properties are determined by metadata,
// we allow it to be a closure.
if (isset($this->viewProperties['filter_excludelowpop_value'])
&& $this->viewProperties['filter_excludelowpop_value'] instanceof \Closure
) {
$function = $this->viewProperties['filter_excludelowpop_value'];
$this->viewProperties['filter_excludelowpop_value'] = $function();
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
$this->loadDocumentation();
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
/**
* Returns the API method that will be called to obatin the report data.
*
* @return string e.g. 'Actions.getPageUrls'
*/
public function getReportApiMethod()
{
return $this->viewProperties['apiMethodToRequestDataTable'];
}
/**
* Returns the view's associated visualization class name.
*
* @return string
*/
public function getVisualizationClass()
{
return $this->visualizationClass;
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
/**
* Gets a view property by reference.
*
* @param string $name A valid view property name. @see Properties for all
Benaka Moorthi
a validé
* valid view properties.
* @return mixed
Benaka Moorthi
a validé
*/
public function &__get($name)
{
Benaka Moorthi
a validé
return $this->viewProperties[$name];
}
/**
* Sets a view property.
*
* @param string $name A valid view property name. @see Properties for all
Benaka Moorthi
a validé
* valid view properties.
* @param mixed $value
* @return mixed Returns $value.
Benaka Moorthi
a validé
*/
public function __set($name, $value)
{
Benaka Moorthi
a validé
return $this->viewProperties[$name] = $value;
}
* Hack to allow property access in Twig (w/ property name checking).
public function __call($name, $arguments)
{
return $this->$name;
}
/**
* Unique string ID that defines the format of the dataTable, eg. "pieChart", "table", etc.
*
* @return string
*/
public function getViewDataTableId()
{
Benaka Moorthi
a validé
$klass = $this->visualizationClass;
return $klass::getViewDataTableId($this);
}
/**
* Returns a Piwik_ViewDataTable_* object.
* By default it will return a ViewDataTable_Html
* If there is a viewDataTable parameter in the URL, a ViewDataTable of this 'viewDataTable' type will be returned.
* If defaultType is specified and if there is no 'viewDataTable' in the URL, a ViewDataTable of this $defaultType will be returned.
* If force is set to true, a ViewDataTable of the $defaultType will be returned in all cases.
*
* @param string $defaultType Any of these: table, cloud, graphPie, graphVerticalBar, graphEvolution, sparkline, generateDataChart*
Benaka Moorthi
a validé
* @param string|bool $apiAction
* @param string|bool $controllerAction
Benaka Moorthi
a validé
static public function factory($defaultType = null, $apiAction = false, $controllerAction = false, $forceDefault = false)
Benaka Moorthi
a validé
if ($controllerAction === false) {
$controllerAction = $apiAction;
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
$defaultProperties = self::getDefaultPropertiesForReport($apiAction);
if (!empty($defaultProperties['default_view_type'])
&& !$forceDefault
) {
$defaultType = $defaultProperties['default_view_type'];
Benaka Moorthi
a validé
$type = Common::getRequestVar('viewDataTable', $defaultType ?: 'table', 'string');
Benaka Moorthi
a validé
if ($type == 'sparkline') {
Benaka Moorthi
a validé
$result = new ViewDataTable\Sparkline($controllerAction, $apiAction, $defaultProperties);
Benaka Moorthi
a validé
} else {
Benaka Moorthi
a validé
$result = new ViewDataTable($controllerAction, $apiAction, $defaultProperties, $type);
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
return $result;
}
Benaka Moorthi
a validé
/**
Benaka Moorthi
a validé
* Returns the list of view properties that should be sent with the HTML response
* as JSON. These properties are visible to the UI JavaScript, but are not passed
* with every request.
*
Benaka Moorthi
a validé
* @return array
*/
public function getClientSideProperties()
Benaka Moorthi
a validé
{
$result = array();
Benaka Moorthi
a validé
if ($this->visualizationClass) {
$klass = $this->visualizationClass;
$result = array_merge($result, $klass::getClientSideProperties());
Benaka Moorthi
a validé
}
return $result;
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
/**
* Returns the list of view properties that should be sent with the HTML response
Benaka Moorthi
a validé
* and resent by the UI JavaScript in every subsequent AJAX request.
*
Benaka Moorthi
a validé
* @return array
*/
public function getClientSideParameters()
Benaka Moorthi
a validé
{
$result = array(
Benaka Moorthi
a validé
'filter_excludelowpop',
'filter_excludelowpop_value',
'filter_pattern',
'filter_column',
);
Benaka Moorthi
a validé
if ($this->visualizationClass) {
$klass = $this->visualizationClass;
$result = array_merge($result, $klass::getClientSideParameters());
}
return $result;
}
public function getCurrentControllerAction()
{
return $this->currentControllerAction;
}
public function getCurrentControllerName()
{
return $this->currentControllerName;
}
/**
* Returns the DataTable loaded from the API
*
* @return DataTable
*/
public function getDataTable()
{
if (is_null($this->dataTable)) {
throw new \Exception("The DataTable object has not yet been created");
}
return $this->dataTable;
}
/**
* To prevent calling an API multiple times, the DataTable can be set directly.
* It won't be loaded again from the API in this case
*
* @param $dataTable
* @return void $dataTable DataTable
*/
public function setDataTable($dataTable)
{
$this->dataTable = $dataTable;
}
Benaka Moorthi
a validé
/**
* Returns the defaut view properties for a report, if any.
Benaka Moorthi
a validé
* Plugins can associate callbacks with the ViewDataTable.getReportDisplayProperties
* event to set the default properties of reports.
Benaka Moorthi
a validé
* @return array
*/
private static function getDefaultPropertiesForReport($apiAction)
{
Benaka Moorthi
a validé
$reportDisplayProperties = self::getAllReportDisplayProperties();
return isset($reportDisplayProperties[$apiAction]) ? $reportDisplayProperties[$apiAction] : array();
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
/**
* Returns the list of display properties for all available reports.
*
* @return array
*/
private static function getAllReportDisplayProperties()
{
if (self::$reportPropertiesCache === null) {
self::$reportPropertiesCache = array();
Piwik_PostEvent('ViewDataTable.getReportDisplayProperties', array(&self::$reportPropertiesCache));
Benaka Moorthi
a validé
}
return self::$reportPropertiesCache;
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
/**
* Sets a view property by name. This function handles special view properties
Benaka Moorthi
a validé
* like 'translations' & 'related_reports' that store arrays.
*
Benaka Moorthi
a validé
* @param string $name
* @param mixed $value For array properties, $value can be a comma separated string.
Benaka Moorthi
a validé
*/
private function setViewProperty($name, $value)
{
if (isset($this->viewProperties[$name])
&& is_array($this->viewProperties[$name])
&& is_string($value)
) {
$value = Piwik::getArrayFromApiParameter($value);
}
Benaka Moorthi
a validé
if ($name == 'translations'
|| $name == 'filters'
Benaka Moorthi
a validé
|| $name == 'after_data_loaded_functions'
Benaka Moorthi
a validé
) {
Benaka Moorthi
a validé
$this->viewProperties[$name] = array_merge($this->viewProperties[$name], $value);
Benaka Moorthi
a validé
} else if ($name == 'related_reports') { // TODO: should process after (in overrideViewProperties)
$this->addRelatedReports($value);
} else if ($name == 'visualization_properties') {
$this->setVisualizationPropertiesFromMetadata($value);
} else if (Properties::isCoreViewProperty($name)) {
Benaka Moorthi
a validé
$this->viewProperties[$name] = $value;
} else {
$report = $this->currentControllerName . '.' . $this->currentControllerAction;
throw new \Exception("Invalid view property '$name' specified in view property metadata for '$report'.");
}
}
/**
* TODO
*/
private function setVisualizationPropertiesFromMetadata($properties)
{
if ($this->visualizationClass === null) {
return null;
}
$visualizationIds = DataTableVisualization::getVisualizationIdsWithInheritance($this->visualizationClass);
foreach ($visualizationIds as $visualizationId) {
if (empty($properties[$visualizationId])) {
continue;
}
foreach ($properties[$visualizationId] as $key => $value) {
if (Properties::isCoreViewProperty($key)) {
$this->viewProperties[$key] = $value;
Benaka Moorthi
a validé
} else {
$this->viewProperties['visualization_properties']->$key = $value;
Benaka Moorthi
a validé
}
}
}
}
/**
* Function called by the ViewDataTable objects in order to fetch data from the API.
* The function init() must have been called before, so that the object knows which API module and action to call.
* It builds the API request string and uses Request to call the API.
* The requested DataTable object is stored in $this->dataTable.
*/
protected function loadDataTableFromAPI()
{
if (!is_null($this->dataTable)) {
// data table is already there
// this happens when setDataTable has been used
return;
}
Benaka Moorthi
a validé
// we build the request (URL) to call the API
$requestArray = $this->getRequestArray();
// we make the request to the API
mattab
a validé
$request = new Request($requestArray);
// and get the DataTable structure
$dataTable = $request->process();
$this->dataTable = $dataTable;
}
/**
* Checks that the API returned a normal DataTable (as opposed to DataTable_Array)
* @return void
*/
protected function checkStandardDataTable()
{
Piwik::checkObjectTypeIs($this->dataTable, array('\Piwik\DataTable'));
Benaka Moorthi
a validé
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
private function getFiltersToRun()
{
$priorityFilters = array();
$presentationFilters = array();
foreach ($this->viewProperties['filters'] as $filterInfo) {
if ($filterInfo instanceof \Closure) {
$nameOrClosure = $filterInfo;
$parameters = array();
$priority = false;
} else {
@list($nameOrClosure, $parameters, $priority) = $filterInfo;
}
if ($nameOrClosure instanceof \Closure) {
$parameters[] = $this;
}
if ($priority) {
$priorityFilters[] = array($nameOrClosure, $parameters);
} else {
$presentationFilters[] = array($nameOrClosure, $parameters);
}
}
return array($priorityFilters, $presentationFilters);
}
/**
* Hook called after the dataTable has been loaded from the API
* Can be used to add, delete or modify the data freshly loaded
*
* @return bool
*/
protected function postDataTableLoadedFromAPI()
{
Benaka Moorthi
a validé
$columns = $this->dataTable->getColumns();
$haveNbVisits = in_array('nb_visits', $columns);
$haveNbUniqVisitors = in_array('nb_uniq_visitors', $columns);
Benaka Moorthi
a validé
// default columns_to_display to label, nb_uniq_visitors/nb_visits if those columns exist in the
// dataset. otherwise, default to all columns in dataset.
Benaka Moorthi
a validé
if (empty($this->viewProperties['columns_to_display'])) {
Benaka Moorthi
a validé
if ($haveNbVisits
|| $haveNbUniqVisitors
) {
Benaka Moorthi
a validé
$columnsToDisplay = array('label');
// if unique visitors data is available, show it, otherwise just visits
Benaka Moorthi
a validé
if ($haveNbUniqVisitors) {
Benaka Moorthi
a validé
$columnsToDisplay[] = 'nb_uniq_visitors';
Benaka Moorthi
a validé
} else {
Benaka Moorthi
a validé
$columnsToDisplay[] = 'nb_visits';
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
} else {
$columnsToDisplay = $columns;
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
$this->viewProperties['columns_to_display'] = array_filter($columnsToDisplay);
}
Benaka Moorthi
a validé
Benaka Moorthi
a validé
$this->removeEmptyColumnsFromDisplay();
// default sort order to visits/visitors data
if (empty($this->viewProperties['filter_sort_column'])) {
Benaka Moorthi
a validé
if ($haveNbUniqVisitors) {
Benaka Moorthi
a validé
$this->viewProperties['filter_sort_column'] = 'nb_uniq_visitors';
} else {
$this->viewProperties['filter_sort_column'] = 'nb_visits';
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
$this->viewProperties['filter_sort_order'] = 'desc';
Benaka Moorthi
a validé
}
// deal w/ table metadata
if ($this->dataTable instanceof DataTable) {
$this->viewProperties['metadata'] = $this->dataTable->getAllTableMetadata();
if (isset($this->viewProperties['metadata'][DataTable::ARCHIVED_DATE_METADATA_NAME])) {
$this->viewProperties['metadata'][DataTable::ARCHIVED_DATE_METADATA_NAME] =
$this->makePrettyArchivedOnText();
}
}
Benaka Moorthi
a validé
list($priorityFilters, $otherFilters) = $this->getFiltersToRun();
Benaka Moorthi
a validé
Benaka Moorthi
a validé
// First, filters that delete rows
foreach ($priorityFilters as $filter) {
$this->dataTable->filter($filter[0], $filter[1]);
if (!$this->areGenericFiltersDisabled()) {
// Second, generic filters (Sort, Limit, Replace Column Names, etc.)
Benaka Moorthi
a validé
$requestArray = $this->getRequestArray();
mattab
a validé
$request = Request::getRequestArrayFromString($requestArray);
if ($this->viewProperties['enable_sort'] === false) {
$request['filter_sort_column'] = $request['filter_sort_order'] = '';
}
mattab
a validé
$genericFilter = new \Piwik\API\DataTableGenericFilter($request);
$genericFilter->filter($this->dataTable);
}
if (!$this->areQueuedFiltersDisabled()) {
Benaka Moorthi
a validé
$this->dataTable->applyQueuedFilters();
// Finally, apply datatable filters that were queued (should be 'presentation' filters that
// do not affect the number of rows)
Benaka Moorthi
a validé
foreach ($otherFilters as $filter) {
$this->dataTable->filter($filter[0], $filter[1]);
}
}
return true;
}
/**
* Returns true if generic filters have been disabled, false if otherwise.
*
* @return bool
*/
private function areGenericFiltersDisabled()
{
// if disable_generic_filters query param is set to '1', generic filters are disabled
if (Common::getRequestVar('disable_generic_filters', '0', 'string') == 1) {
return true;
}
// if $this->disableGenericFilters() was called, generic filters are disabled
Benaka Moorthi
a validé
if (isset($this->viewProperties['disable_generic_filters'])
&& $this->viewProperties['disable_generic_filters'] === true
) {
return true;
}
return false;
}
/**
* Returns true if queued filters have been disabled, false if otherwise.
*
* @return bool
*/
private function areQueuedFiltersDisabled()
{
Benaka Moorthi
a validé
return isset($this->viewProperties['disable_queued_filters'])
&& $this->viewProperties['disable_queued_filters'];
}
/**
* Returns prettified and translated text that describes when a report was last updated.
*
* @return string
*/
private function makePrettyArchivedOnText()
{
$dateText = $this->viewProperties['metadata'][DataTable::ARCHIVED_DATE_METADATA_NAME];
$date = Date::factory($dateText);
$today = mktime(0, 0, 0);
if ($date->getTimestamp() > $today) {
$elapsedSeconds = time() - $date->getTimestamp();
$timeAgo = Piwik::getPrettyTimeFromSeconds($elapsedSeconds);
return Piwik_Translate('CoreHome_ReportGeneratedXAgo', $timeAgo);
}
$prettyDate = $date->getLocalized("%longYear%, %longMonth% %day%") . $date->toString('S');
return Piwik_Translate('CoreHome_ReportGeneratedOn', $prettyDate);
}
/**
* @return string URL to call the API, eg. "method=Referers.getKeywords&period=day&date=yesterday"...
*/
Benaka Moorthi
a validé
protected function getRequestArray()
Benaka Moorthi
a validé
// we prepare the array to give to the API Request
// we setup the method and format variable
// - we request the method to call to get this specific DataTable
// - the format = original specifies that we want to get the original DataTable structure itself, not rendered
Benaka Moorthi
a validé
$requestArray = array(
'method' => $this->viewProperties['apiMethodToRequestDataTable'],
'format' => 'original',
Benaka Moorthi
a validé
'disable_generic_filters' => Common::getRequestVar('disable_generic_filters', 1, 'int'),
'disable_queued_filters' => Common::getRequestVar('disable_queued_filters', 1, 'int')
Benaka Moorthi
a validé
);
$toSetEventually = array(
'filter_limit',
'keep_summary_row',
'filter_sort_column',
'filter_sort_order',
'filter_excludelowpop',
'filter_excludelowpop_value',
'filter_column',
'filter_pattern',
);
foreach ($toSetEventually as $varToSet) {
$value = $this->getDefaultOrCurrent($varToSet);
if (false !== $value) {
Benaka Moorthi
a validé
$requestArray[$varToSet] = $value;
}
}
Benaka Moorthi
a validé
mattab
a validé
$segment = Request::getRawSegmentFromRequest();
Benaka Moorthi
a validé
$requestArray['segment'] = $segment;
if (self::shouldLoadExpanded()) {
$requestArray['expanded'] = 1;
}
$requestArray = array_merge($requestArray, $this->request_parameters_to_modify);
if (!empty($requestArray['filter_limit'])
&& $requestArray['filter_limit'] === 0
) {
unset($requestArray['filter_limit']);
}
Benaka Moorthi
a validé
return $requestArray;
}
/**
* This functions reads the customization values for the DataTable and returns an array (name,value) to be printed in Javascript.
* This array defines things such as:
* - name of the module & action to call to request data for this table
* - optional filters information, eg. filter_limit and filter_offset
* - etc.
*
* The values are loaded:
* - from the generic filters that are applied by default @see Piwik_API_DataTableGenericFilter.php::getGenericFiltersInformation()
* - from the values already available in the GET array
* - from the values set using methods from this class (eg. setSearchPattern(), setLimit(), etc.)
*
* @return array eg. array('show_offset_information' => 0, 'show_...
*/
protected function getJavascriptVariablesToSet()
{
// build javascript variables to set
$javascriptVariablesToSet = array();
Benaka Moorthi
a validé
foreach ($this->viewProperties['custom_parameters'] as $name => $value) {
$javascriptVariablesToSet[$name] = $value;
}
foreach ($_GET as $name => $value) {
try {
$requestValue = Common::getRequestVar($name);
$requestValue = '';
}
$javascriptVariablesToSet[$name] = $requestValue;
}
foreach ($this->getClientSideParameters() as $name) {
if (!isset($javascriptVariablesToSet[$name])) {
if (isset($this->viewProperties[$name])
&& $this->viewProperties[$name] !== false
) {
$javascriptVariablesToSet[$name] = $this->convertForJson($this->viewProperties[$name]);
} else if (Properties::isValidVisualizationProperty($this->visualizationClass, $name)) {
$javascriptVariablesToSet[$name] =
$this->convertForJson($this->viewProperties['visualization_properties']->$name);
}
Benaka Moorthi
a validé
}
}
if ($this->dataTable instanceof DataTable) {
// we override the filter_sort_column with the column used for sorting,
// which can be different from the one specified (eg. if the column doesn't exist)
$javascriptVariablesToSet['filter_sort_column'] = $this->dataTable->getSortedByColumnName();
// datatable can return "2" but we want to write "nb_visits" in the js
mattab
a validé
if (isset(Metrics::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']])) {
$javascriptVariablesToSet['filter_sort_column'] = Metrics::$mappingFromIdToName[$javascriptVariablesToSet['filter_sort_column']];
}
}
$javascriptVariablesToSet['module'] = $this->currentControllerName;
$javascriptVariablesToSet['action'] = $this->currentControllerAction;
if (!isset($javascriptVariablesToSet['viewDataTable'])) {
$javascriptVariablesToSet['viewDataTable'] = $this->getViewDataTableId();
}
if ($this->dataTable &&
// Set doesn't have the method
!($this->dataTable instanceof DataTable\Map)
&& empty($javascriptVariablesToSet['totalRows'])
) {
$javascriptVariablesToSet['totalRows'] = $this->dataTable->getRowsCountBeforeLimitFilter();
}
// we escape the values that will be displayed in the javascript footer of each datatable
// to make sure there is no malicious code injected (the value are already htmlspecialchar'ed as they
// are loaded with Common::getRequestVar()
foreach ($javascriptVariablesToSet as &$value) {
if (is_array($value)) {
$value = array_map('addslashes', $value);
} else {
$value = addslashes($value);
}
}
$deleteFromJavascriptVariables = array(
'filter_excludelowpop',
'filter_excludelowpop_value',
);
foreach ($deleteFromJavascriptVariables as $name) {
if (isset($javascriptVariablesToSet[$name])) {
unset($javascriptVariablesToSet[$name]);
}
}
mattab
a validé
$rawSegment = Request::getRawSegmentFromRequest();
if(!empty($rawSegment)) {
$javascriptVariablesToSet['segment'] = $rawSegment;
}
return $javascriptVariablesToSet;
}
/**
* Returns array of properties that should be visible to client side JavaScript. The data
* will be available in the data-props HTML attribute of the .dataTable div.
*
* @return array Maps property names w/ property values.
*/
private function getClientSidePropertiesToSet()
{
$result = array();
foreach ($this->getClientSideProperties() as $name) {
if (isset($this->viewProperties[$name])) {
$result[$name] = $this->convertForJson($this->viewProperties[$name]);
} else if (Properties::isValidVisualizationProperty($this->visualizationClass, $name)) {
$result[$name] = $this->convertForJson($this->viewProperties['visualization_properties']->$name);
}
}
return $result;
}
/**
* Returns, for a given parameter, the value of this parameter in the REQUEST array.
* If not set, returns the default value for this parameter @see getDefault()
*
* @param string $nameVar
* @return string|mixed Value of this parameter
*/
protected function getDefaultOrCurrent($nameVar)
{
if (isset($_GET[$nameVar])) {
return Common::sanitizeInputValue($_GET[$nameVar]);
}
$default = $this->getDefault($nameVar);
return $default;
}
/**
* Returns the default value for a given parameter.
* For example, these default values can be set using the disable* methods.
*
* @param string $nameVar
* @return mixed
*/
protected function getDefault($nameVar)
{
Benaka Moorthi
a validé
if (!isset($this->viewProperties[$nameVar])) {
return false;
}
Benaka Moorthi
a validé
return $this->viewProperties[$nameVar];
/** Load documentation from the API */
private function loadDocumentation()
{
Benaka Moorthi
a validé
$this->viewProperties['metrics_documentation'] = array();
mattab
a validé
$report = API::getInstance()->getMetadata(0, $this->currentControllerName, $this->currentControllerAction);
$report = $report[0];
if (isset($report['metricsDocumentation'])) {
Benaka Moorthi
a validé
$this->viewProperties['metrics_documentation'] = $report['metricsDocumentation'];
}
if (isset($report['documentation'])) {
Benaka Moorthi
a validé
$this->viewProperties['documentation'] = $report['documentation'];
}
}
private function removeEmptyColumnsFromDisplay()
{
if (empty($this->dataTable)) {
if ($this->dataTable instanceof DataTable\Map) {
$emptyColumns = $this->dataTable->getMetadataIntersectArray(DataTable::EMPTY_COLUMNS_METADATA_NAME);
} else {
$emptyColumns = $this->dataTable->getMetadata(DataTable::EMPTY_COLUMNS_METADATA_NAME);
}
if (is_array($emptyColumns)) {
foreach ($emptyColumns as $emptyColumn) {
Benaka Moorthi
a validé
$key = array_search($emptyColumn, $this->viewProperties['columns_to_display']);
if ($key !== false) {
Benaka Moorthi
a validé
unset($this->viewProperties['columns_to_display'][$key]);
}
}
Benaka Moorthi
a validé
$this->viewProperties['columns_to_display'] = array_values($this->viewProperties['columns_to_display']);
}
}
private function addRelatedReport($module, $action, $title, $queryParams = array())
{
// don't add the related report if it references this report
if ($this->currentControllerName == $module && $this->currentControllerAction == $action) {
return;
}
$url = $this->getBaseReportUrl($module, $action, $queryParams);
Benaka Moorthi
a validé
$this->viewProperties['related_reports'][$url] = $title;
private function addRelatedReports($relatedReports)
{
foreach ($relatedReports as $report => $title) {
list($module, $action) = explode('.', $report);
$this->addRelatedReport($module, $action, $title);
}
}
/**
* Returns true if it is likely that the data for this report has been purged and if the
* user should be told about that.
*
* In order for this function to return true, the following must also be true:
* - The data table for this report must either be empty or not have been fetched.
* - The period of this report is not a multiple period.
* - The date of this report must be older than the delete_reports_older_than config option.
* @return bool
*/
public function hasReportBeenPurged()
{
$strPeriod = Common::getRequestVar('period', false);
$strDate = Common::getRequestVar('date', false);
if ($strPeriod !== false
&& $strDate !== false
&& (is_null($this->dataTable)
|| (!empty($this->dataTable) && $this->dataTable->getRowsCount() == 0))) {
// if range, only look at the first date
if ($strPeriod == 'range') {
$idSite = Common::getRequestVar('idSite', '');
if (intval($idSite) != 0) {
$timezone = $site->getTimezone();
} else {
$timezone = 'UTC';
}
$period = new Range('range', $strDate, $timezone);
$reportDate = $period->getDateStart();
} // if a multiple period, this function is irrelevant
else if (Period::isMultiplePeriod($strDate, $strPeriod)) {
return false;
} // otherwise, use the date as given
else {
$reportDate = Date::factory($strDate);
}
$reportYear = $reportDate->toString('Y');
$reportMonth = $reportDate->toString('m');
if (PluginsManager::getInstance()->isPluginActivated('PrivacyManager')
mattab
a validé
&& Plugins\PrivacyManager\PrivacyManager::shouldReportBePurged($reportYear, $reportMonth)
) {
return true;
}
}
return false;
}
/**
* Returns URL for this report w/o any filter parameters.
*
* @param string $module
* @param string $action
* @param array $queryParams
*/
private function getBaseReportUrl($module, $action, $queryParams = array())
{
$params = array_merge($queryParams, array('module' => $module, 'action' => $action));
mattab
a validé
return Request::getCurrentUrlWithoutGenericFilters($params);
Benaka Moorthi
a validé
}
Benaka Moorthi
a validé
/**
* Convenience method that creates and renders a ViewDataTable for a API method.
*
Benaka Moorthi
a validé
* @param string $pluginName The name of the plugin (eg, UserSettings).
* @param string $apiAction The name of the API action (eg, getResolution).
* @param bool $fetch If true, the result is returned, if false it is echo'd.
Benaka Moorthi
a validé
* @return string|null See $fetch.
*/
Benaka Moorthi
a validé
static public function renderReport($pluginName, $apiAction, $fetch = true)
Benaka Moorthi
a validé
{
mattab
a validé
$namespacedApiClassName = "\\Piwik\\Plugins\\$pluginName\\API";
if (!method_exists($namespacedApiClassName::getInstance(), $apiAction)) {
throw new \Exception("$namespacedApiClassName Invalid action name '$apiAction' for '$pluginName' plugin.");
Benaka Moorthi
a validé
}
$view = self::factory(null, $pluginName.'.'.$apiAction);
Benaka Moorthi
a validé
$rendered = $view->render();