add icons for Character groups

This commit is contained in:
coderkun 2014-04-29 14:18:04 +02:00
commit 2d9a41a5fe
3461 changed files with 594457 additions and 0 deletions

View file

@ -0,0 +1,71 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations;
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/JqplotDataGenerator.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/Cloud.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/HtmlTable.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph.php';
/**
* This plugin contains all core visualizations, such as the normal HTML table and
* jqPlot graphs.
*/
class CoreVisualizations extends \Piwik\Plugin
{
/**
* @see Piwik\Plugin::getListHooksRegistered
*/
public function getListHooksRegistered()
{
return array(
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'ViewDataTable.addViewDataTable' => 'getAvailableDataTableVisualizations',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
);
}
public function getAvailableDataTableVisualizations(&$visualizations)
{
$visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\Sparkline';
$visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\HtmlTable';
$visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\HtmlTable\\AllColumns';
$visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\Cloud';
$visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Pie';
$visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Bar';
$visualizations[] = 'Piwik\\Plugins\\CoreVisualizations\\Visualizations\\JqplotGraph\\Evolution';
}
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less";
$stylesheets[] = "plugins/CoreVisualizations/stylesheets/jqplot.css";
}
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/CoreVisualizations/javascripts/seriesPicker.js";
$jsFiles[] = "plugins/CoreVisualizations/javascripts/jqplot.js";
$jsFiles[] = "plugins/CoreVisualizations/javascripts/jqplotBarGraph.js";
$jsFiles[] = "plugins/CoreVisualizations/javascripts/jqplotPieGraph.js";
$jsFiles[] = "plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js";
}
public function getClientSideTranslationKeys(&$translationKeys)
{
$translationKeys[] = 'General_MetricsToPlot';
$translationKeys[] = 'General_MetricToPlot';
$translationKeys[] = 'General_RecordsToPlot';
$translationKeys[] = 'General_SaveImageOnYourComputer';
$translationKeys[] = 'General_ExportAsImage';
$translationKeys[] = 'General_NoDataForGraph';
}
}

View file

@ -0,0 +1,160 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations;
use Exception;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator\Chart;
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/JqplotDataGenerator/Evolution.php';
/**
* Generates JSON data used to configure and populate JQPlot graphs.
*
* Supports pie graphs, bar graphs and time serieses (aka, evolution graphs).
*/
class JqplotDataGenerator
{
/**
* View properties. @see Piwik\ViewDataTable for more info.
*
* @var array
*/
protected $properties;
protected $graphType;
/**
* Creates a new JqplotDataGenerator instance for a graph type and view properties.
*
* @param string $type 'pie', 'bar', or 'evolution'
* @param array $properties The view properties.
* @throws \Exception
* @return JqplotDataGenerator
*/
public static function factory($type, $properties)
{
switch ($type) {
case 'evolution':
return new JqplotDataGenerator\Evolution($properties, $type);
case 'pie':
case 'bar':
return new JqplotDataGenerator($properties, $type);
default:
throw new Exception("Unknown JqplotDataGenerator type '$type'.");
}
}
/**
* Constructor.
*
* @param array $properties
* @param string $graphType
*
* @internal param \Piwik\Plugin\ViewDataTable $visualization
*/
public function __construct($properties, $graphType)
{
$this->properties = $properties;
$this->graphType = $graphType;
}
/**
* Generates JSON graph data and returns it.
*
* @param DataTable|DataTable\Map $dataTable
* @return string
*/
public function generate($dataTable)
{
$visualization = new Chart();
if ($dataTable->getRowsCount() > 0) {
// if addTotalRow was called in GenerateGraphHTML, add a row containing totals of
// different metrics
if ($this->properties['add_total_row']) {
$dataTable->queueFilter('AddSummaryRow', Piwik::translate('General_Total'));
}
$dataTable->applyQueuedFilters();
$this->initChartObjectData($dataTable, $visualization);
}
return $visualization->render();
}
/**
* @param DataTable|DataTable\Map $dataTable
* @param $visualization
*/
protected function initChartObjectData($dataTable, $visualization)
{
// We apply a filter to the DataTable, decoding the label column (useful for keywords for example)
$dataTable->filter('ColumnCallbackReplace', array('label', 'urldecode'));
$xLabels = $dataTable->getColumn('label');
$columnNames = $this->properties['columns_to_display'];
if (($labelColumnIndex = array_search('label', $columnNames)) !== false) {
unset($columnNames[$labelColumnIndex]);
}
$columnNameToTranslation = $columnNameToValue = array();
foreach ($columnNames as $columnName) {
$columnNameToTranslation[$columnName] = @$this->properties['translations'][$columnName];
$columnNameToValue[$columnName] = $dataTable->getColumn($columnName);
}
$visualization->dataTable = $dataTable;
$visualization->properties = $this->properties;
$visualization->setAxisXLabels($xLabels);
$visualization->setAxisYValues($columnNameToValue);
$visualization->setAxisYLabels($columnNameToTranslation);
$units = $this->getUnitsForColumnsToDisplay();
$visualization->setAxisYUnits($units);
}
protected function getUnitsForColumnsToDisplay()
{
// derive units from column names
$units = $this->deriveUnitsFromRequestedColumnNames();
if (!empty($this->properties['y_axis_unit'])) {
$units = array_fill(0, count($units), $this->properties['y_axis_unit']);
}
// the bar charts contain the labels a first series
// this series has to be removed from the units
if ($this->graphType == 'bar') {
array_shift($units);
}
return $units;
}
private function deriveUnitsFromRequestedColumnNames()
{
$idSite = Common::getRequestVar('idSite', null, 'int');
$units = array();
foreach ($this->properties['columns_to_display'] as $columnName) {
$derivedUnit = Metrics::getUnit($columnName, $idSite);
$units[$columnName] = empty($derivedUnit) ? false : $derivedUnit;
}
return $units;
}
}

View file

@ -0,0 +1,119 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Common;
use Piwik\ProxyHttp;
/**
*
*/
class Chart
{
// the data kept here conforms to the jqplot data layout
// @see http://www.jqplot.com/docs/files/jqPlotOptions-txt.html
protected $series = array();
protected $data = array();
protected $axes = array();
// temporary
public $properties;
public function setAxisXLabels($xLabels)
{
$xSteps = $this->properties['x_axis_step_size'];
$showAllTicks = $this->properties['show_all_ticks'];
$this->axes['xaxis']['labels'] = array_values($xLabels);
$ticks = array_values($xLabels);
if (!$showAllTicks) {
// unset labels so there are $xSteps number of blank ticks between labels
foreach ($ticks as $i => &$label) {
if ($i % $xSteps != 0) {
$label = ' ';
}
}
}
$this->axes['xaxis']['ticks'] = $ticks;
}
public function setAxisXOnClick(&$onClick)
{
$this->axes['xaxis']['onclick'] = & $onClick;
}
public function setAxisYValues(&$values)
{
foreach ($values as $label => &$data) {
$this->series[] = array(
'label' => $label,
'internalLabel' => $label
);
array_walk($data, function (&$v) {
$v = (float)$v;
});
$this->data[] = & $data;
}
}
public function setAxisYUnits($yUnits)
{
$yUnits = array_values(array_map('strval', $yUnits));
// generate axis IDs for each unique y unit
$axesIds = array();
foreach ($yUnits as $idx => $unit) {
if (!isset($axesIds[$unit])) {
// handle axes ids: first y[]axis, then y[2]axis, y[3]axis...
$nextAxisId = empty($axesIds) ? '' : count($axesIds) + 1;
$axesIds[$unit] = 'y' . $nextAxisId . 'axis';
}
}
// generate jqplot axes config
foreach ($axesIds as $unit => $axisId) {
$this->axes[$axisId]['tickOptions']['formatString'] = '%s' . $unit;
}
// map each series to appropriate yaxis
foreach ($yUnits as $idx => $unit) {
$this->series[$idx]['yaxis'] = $axesIds[$unit];
}
}
public function setAxisYLabels($labels)
{
foreach ($this->series as &$series) {
$label = $series['internalLabel'];
if (isset($labels[$label])) {
$series['label'] = $labels[$label];
}
}
}
public function render()
{
ProxyHttp::overrideCacheControlHeaders();
// See http://www.jqplot.com/docs/files/jqPlotOptions-txt.html
$data = array(
'params' => array(
'axes' => &$this->axes,
'series' => &$this->series
),
'data' => &$this->data
);
return $data;
}
}

View file

@ -0,0 +1,188 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Archive\DataTableFactory;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Menu\MenuMain;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Url;
/**
* Generates JQPlot JSON data/config for evolution graphs.
*/
class Evolution extends JqplotDataGenerator
{
/**
* @param DataTable|DataTable\Map $dataTable
* @param $visualization
*/
protected function initChartObjectData($dataTable, $visualization)
{
// if the loaded datatable is a simple DataTable, it is most likely a plugin plotting some custom data
// we don't expect plugin developers to return a well defined Set
if ($dataTable instanceof DataTable) {
parent::initChartObjectData($dataTable, $visualization);
return;
}
// the X label is extracted from the 'period' object in the table's metadata
$xLabels = array();
foreach ($dataTable->getDataTables() as $metadataDataTable) {
$xLabels[] = $metadataDataTable->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLocalizedShortString(); // eg. "Aug 2009"
}
$units = $this->getUnitsForColumnsToDisplay();
// if rows to display are not specified, default to all rows (TODO: perhaps this should be done elsewhere?)
$rowsToDisplay = $this->properties['rows_to_display']
? : array_unique($dataTable->getColumn('label'))
? : array(false) // make sure that a series is plotted even if there is no data
;
// collect series data to show. each row-to-display/column-to-display permutation creates a series.
$allSeriesData = array();
$seriesUnits = array();
foreach ($rowsToDisplay as $rowLabel) {
foreach ($this->properties['columns_to_display'] as $columnName) {
$seriesLabel = $this->getSeriesLabel($rowLabel, $columnName);
$seriesData = $this->getSeriesData($rowLabel, $columnName, $dataTable);
$allSeriesData[$seriesLabel] = $seriesData;
$seriesUnits[$seriesLabel] = $units[$columnName];
}
}
$visualization->dataTable = $dataTable;
$visualization->properties = $this->properties;
$visualization->setAxisXLabels($xLabels);
$visualization->setAxisYValues($allSeriesData);
$visualization->setAxisYUnits($seriesUnits);
$dataTables = $dataTable->getDataTables();
if ($this->isLinkEnabled()) {
$idSite = Common::getRequestVar('idSite', null, 'int');
$periodLabel = reset($dataTables)->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getLabel();
$axisXOnClick = array();
$queryStringAsHash = $this->getQueryStringAsHash();
foreach ($dataTable->getDataTables() as $idDataTable => $metadataDataTable) {
$dateInUrl = $metadataDataTable->getMetadata(DataTableFactory::TABLE_METADATA_PERIOD_INDEX)->getDateStart();
$parameters = array(
'idSite' => $idSite,
'period' => $periodLabel,
'date' => $dateInUrl->toString(),
'segment' => \Piwik\API\Request::getRawSegmentFromRequest()
);
$hash = '';
if (!empty($queryStringAsHash)) {
$hash = '#' . Url::getQueryStringFromParameters($queryStringAsHash + $parameters);
}
$link = 'index.php?' .
Url::getQueryStringFromParameters(array(
'module' => 'CoreHome',
'action' => 'index',
) + $parameters)
. $hash;
$axisXOnClick[] = $link;
}
$visualization->setAxisXOnClick($axisXOnClick);
}
}
private function getSeriesData($rowLabel, $columnName, DataTable\Map $dataTable)
{
$seriesData = array();
foreach ($dataTable->getDataTables() as $childTable) {
// get the row for this label (use the first if $rowLabel is false)
if ($rowLabel === false) {
$row = $childTable->getFirstRow();
} else {
$row = $childTable->getRowFromLabel($rowLabel);
}
// get series data point. defaults to 0 if no row or no column value.
if ($row === false) {
$seriesData[] = 0;
} else {
$seriesData[] = $row->getColumn($columnName) ? : 0;
}
}
return $seriesData;
}
/**
* Derive the series label from the row label and the column name.
* If the row label is set, both the label and the column name are displayed.
* @param string $rowLabel
* @param string $columnName
* @return string
*/
private function getSeriesLabel($rowLabel, $columnName)
{
$metricLabel = @$this->properties['translations'][$columnName];
if ($rowLabel !== false) {
// eg. "Yahoo! (Visits)"
$label = "$rowLabel ($metricLabel)";
} else {
// eg. "Visits"
$label = $metricLabel;
}
return $label;
}
/**
* We link the graph dots to the same report as currently being displayed (only the date would change).
*
* In some cases the widget is loaded within a report that doesn't exist as such.
* For example, the dashboards loads the 'Last visits graph' widget which can't be directly linked to.
* Instead, the graph must link back to the dashboard.
*
* In other cases, like Visitors>Overview or the Goals graphs, we can link the graph clicks to the same report.
*
* To detect whether or not we can link to a report, we simply check if the current URL from which it was loaded
* belongs to the menu or not. If it doesn't belong to the menu, we do not append the hash to the URL,
* which results in loading the dashboard.
*
* @return array Query string array to append to the URL hash or false if the dashboard should be displayed
*/
private function getQueryStringAsHash()
{
$queryString = Url::getArrayFromCurrentQueryString();
$piwikParameters = array('idSite', 'date', 'period', 'XDEBUG_SESSION_START', 'KEY');
foreach ($piwikParameters as $parameter) {
unset($queryString[$parameter]);
}
if (MenuMain::getInstance()->isUrlFound($queryString)) {
return $queryString;
}
return false;
}
private function isLinkEnabled()
{
static $linkEnabled;
if (!isset($linkEnabled)) {
// 1) Custom Date Range always have link disabled, otherwise
// the graph data set is way too big and fails to display
// 2) disableLink parameter is set in the Widgetize "embed" code
$linkEnabled = !Common::getRequestVar('disableLink', 0, 'int')
&& Common::getRequestVar('period', 'day') != 'range';
}
return $linkEnabled;
}
}

View file

@ -0,0 +1,195 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Plugin\Visualization;
use Piwik\View;
/**
* Generates a tag cloud from a given data array.
* The generated tag cloud can be in PHP format, or in HTML.
*
* Inspired from Derek Harvey (www.derekharvey.co.uk)
*
* @property Cloud\Config $config
*
*/
class Cloud extends Visualization
{
const ID = 'cloud';
const TEMPLATE_FILE = "@CoreVisualizations/_dataTableViz_tagCloud.twig";
const FOOTER_ICON = 'plugins/Zeitgeist/images/tagcloud.png';
const FOOTER_ICON_TITLE = 'General_TagCloud';
/** Used by integration tests to make sure output is consistent. */
public static $debugDisableShuffle = false;
public $truncatingLimit = 50;
protected $wordsArray = array();
public static function getDefaultConfig()
{
return new Cloud\Config();
}
public function beforeRender()
{
$this->config->show_exclude_low_population = false;
$this->config->show_offset_information = false;
$this->config->show_limit_control = false;
}
public function afterAllFiltersAreApplied()
{
if ($this->dataTable->getRowsCount() == 0) {
return;
}
$columnToDisplay = isset($this->config->columns_to_display[1]) ? $this->config->columns_to_display[1] : 'nb_visits';
$labelMetadata = array();
foreach ($this->dataTable->getRows() as $row) {
$logo = false;
if ($this->config->display_logo_instead_of_label) {
$logo = $row->getMetadata('logo');
}
$label = $row->getColumn('label');
$labelMetadata[$label] = array(
'logo' => $logo,
'url' => $row->getMetadata('url'),
);
$this->addWord($label, $row->getColumn($columnToDisplay));
}
$cloudValues = $this->getCloudValues();
foreach ($cloudValues as &$value) {
$value['logoWidth'] = round(max(16, $value['percent']));
}
$this->assignTemplateVar('labelMetadata', $labelMetadata);
$this->assignTemplateVar('cloudValues', $cloudValues);
}
/**
* Assign word to array
* @param string $word
* @param int $value
* @return string
*/
public function addWord($word, $value = 1)
{
if (isset($this->wordsArray[$word])) {
$this->wordsArray[$word] += $value;
} else {
$this->wordsArray[$word] = $value;
}
}
private function getCloudValues()
{
$this->shuffleCloud();
if (empty($this->wordsArray)) {
return array();
}
$return = array();
$maxValue = max($this->wordsArray);
foreach ($this->wordsArray as $word => $popularity) {
$wordTruncated = $this->truncateWordIfNeeded($word);
$percent = $this->getPercentage($popularity, $maxValue);
$sizeRange = $this->getClassFromPercent($percent);
$return[$word] = array(
'word' => $word,
'wordTruncated' => $wordTruncated,
'value' => $popularity,
'size' => $sizeRange,
'percent' => $percent,
);
}
return $return;
}
/**
* Shuffle associated names in array
*/
protected function shuffleCloud()
{
if (self::$debugDisableShuffle) {
return;
}
$keys = array_keys($this->wordsArray);
shuffle($keys);
if (count($keys) && is_array($keys)) {
$tmpArray = $this->wordsArray;
$this->wordsArray = array();
foreach ($keys as $key => $value) {
$this->wordsArray[$value] = $tmpArray[$value];
}
}
}
/**
* Get the class range using a percentage
*
* @param $percent
*
* @return int class
*/
protected function getClassFromPercent($percent)
{
$mapping = array(95, 70, 50, 30, 15, 5, 0);
foreach ($mapping as $key => $value) {
if ($percent >= $value) {
return $key;
}
}
return 0;
}
/**
* @param $word
* @return string
*/
private function truncateWordIfNeeded($word)
{
if (Common::mb_strlen($word) > $this->truncatingLimit) {
return Common::mb_substr($word, 0, $this->truncatingLimit - 3) . '...';
}
return $word;
}
private function getPercentage($popularity, $maxValue)
{
// case hideFutureHoursWhenToday=1 shows hours with no visits
if ($maxValue == 0) {
return 0;
}
$percent = ($popularity / $maxValue) * 100;
return $percent;
}
}

View file

@ -0,0 +1,35 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\Cloud;
use Piwik\ViewDataTable\Config as VisualizationConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends VisualizationConfig
{
/**
* Whether to display the logo assocatied with a DataTable row (stored as 'logo' row metadata)
* instead of the label in Tag Clouds.
*
* Default value: false
*/
public $display_logo_instead_of_label = false;
public function __construct()
{
parent::__construct();
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array('display_logo_instead_of_label'));
}
}

View file

@ -0,0 +1,157 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\Piwik;
use Piwik\Plugin\Visualization;
/**
* This is an abstract visualization that should be the base of any 'graph' visualization.
* This class defines certain visualization properties that are specific to all graph types.
* Derived visualizations can decide for themselves whether they should support individual
* properties.
*
* @property Graph\Config $config
*/
abstract class Graph extends Visualization
{
const ID = 'graph';
public $selectableRows = array();
public static function getDefaultConfig()
{
return new Graph\Config();
}
public static function getDefaultRequestConfig()
{
$config = parent::getDefaultRequestConfig();
$config->addPropertiesThatShouldBeAvailableClientSide(array('columns'));
return $config;
}
public function beforeRender()
{
if ($this->config->show_goals) {
$this->config->translations['nb_conversions'] = Piwik::translate('Goals_ColumnConversions');
$this->config->translations['revenue'] = Piwik::translate('General_TotalRevenue');
}
}
public function beforeLoadDataTable()
{
// TODO: this should not be required here. filter_limit should not be a view property, instead HtmlTable should use 'limit' or something,
// and manually set request_parameters_to_modify['filter_limit'] based on that. (same for filter_offset).
$this->requestConfig->request_parameters_to_modify['filter_limit'] = false;
if ($this->config->max_graph_elements) {
$this->requestConfig->request_parameters_to_modify['filter_truncate'] = $this->config->max_graph_elements - 1;
}
$this->requestConfig->request_parameters_to_modify['disable_queued_filters'] = 1;
}
/**
* Determines what rows are selectable and stores them in the selectable_rows property in
* a format the SeriesPicker JavaScript class can use.
*/
public function determineWhichRowsAreSelectable()
{
if ($this->config->row_picker_match_rows_by === false) {
return;
}
// collect all selectable rows
$self = $this;
$this->dataTable->filter(function ($dataTable) use ($self) {
/** @var DataTable $dataTable */
foreach ($dataTable->getRows() as $row) {
$rowLabel = $row->getColumn('label');
if (false === $rowLabel) {
continue;
}
// build config
if (!isset($self->selectableRows[$rowLabel])) {
$self->selectableRows[$rowLabel] = array(
'label' => $rowLabel,
'matcher' => $rowLabel,
'displayed' => $self->isRowVisible($rowLabel)
);
}
}
});
}
public function isRowVisible($rowLabel)
{
$isVisible = true;
if ('label' == $this->config->row_picker_match_rows_by) {
$isVisible = in_array($rowLabel, $this->config->rows_to_display);
}
return $isVisible;
}
/**
* Defaults the selectable_columns property if it has not been set and then transforms
* it into something the SeriesPicker JavaScript class can use.
*/
public function afterAllFiltersAreApplied()
{
$this->determineWhichRowsAreSelectable();
$this->config->selectable_rows = array_values($this->selectableRows);
if ($this->config->add_total_row) {
$totalTranslation = Piwik::translate('General_Total');
$this->config->selectable_rows[] = array(
'label' => $totalTranslation,
'matcher' => $totalTranslation,
'displayed' => $this->isRowVisible($totalTranslation)
);
}
if ($this->config->show_goals) {
$this->config->addTranslations(array(
'nb_conversions' => Piwik::translate('Goals_ColumnConversions'),
'revenue' => Piwik::translate('General_TotalRevenue')
));
}
// set default selectable columns, if none specified
$selectableColumns = $this->config->selectable_columns;
if (false === $selectableColumns) {
$selectableColumns = array('nb_visits', 'nb_actions', 'nb_uniq_visitors');
if ($this->config->show_goals) {
$goalMetrics = array('nb_conversions', 'revenue');
$selectableColumns = array_merge($selectableColumns, $goalMetrics);
}
}
$transformed = array();
foreach ($selectableColumns as $column) {
$transformed[] = array(
'column' => $column,
'translation' => @$this->config->translations[$column],
'displayed' => in_array($column, $this->config->columns_to_display)
);
}
$this->config->selectable_columns = $transformed;
}
}

View file

@ -0,0 +1,122 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\Graph;
use Piwik\ViewDataTable\Config as VisualizationConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends VisualizationConfig
{
/**
* Whether the series picker should allow picking more than one series or not.
*
* Default value: true
*/
public $allow_multi_select_series_picker = true;
/**
* The maximum number of rows to render. All other rows will be aggregated in an 'Others' row.
*
* Default value: false (no limit)
*/
public $max_graph_elements = false;
/**
* Array property that contains the names of columns that can be selected in the Series Picker.
*
* Default value: false
*/
public $selectable_columns = false;
/**
* Contains the column (if any) of the values used in the Row Picker.
*
* @see self::ROWS_TO_DISPLAY
*
* Default value: false
*/
public $row_picker_match_rows_by = false;
/**
* Contains the list of values identifying rows that should be displayed as separate series.
* The values are of a specific column determined by the row_picker_match_rows_by column.
*
* @see self::ROW_PICKER_VALUE_COLUMN
*
* Default value: false
*/
public $rows_to_display = false;
/**
* Contains the list of values available for the Row Picker. Currently set to be all visible
* rows, if the row_picker_match_rows_by property is set.
*
* @see self::ROW_PICKER_VALUE_COLUMN
*/
public $selectable_rows = 'selectable_rows';
/**
* Controls whether all ticks & labels are shown on a graph's x-axis or just some.
*
* Default value: false
*/
public $show_all_ticks = false;
/**
* If true, a row with totals of each DataTable column is added.
*
* Default value: false
*/
public $add_total_row = false;
/**
* Controls whether the Series Picker is shown or not. The Series Picker allows users to
* choose between displaying data of different columns.
*
* Default value: true
*/
public $show_series_picker = true;
/**
* Controls whether the percentage of the total is displayed as a tooltip when hovering over
* data points.
*
* NOTE: Sometimes this percentage is meaningless (when the total of the column values is
* not the total number of elements in the set). In this case the tooltip should not be
* displayed.
*
* Default value: true
*/
public $display_percentage_in_tooltip = true;
public function __construct()
{
parent::__construct();
$this->show_limit_control = false;
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'show_series_picker',
'allow_multi_select_series_picker',
'selectable_columns',
'selectable_rows',
'display_percentage_in_tooltip'
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array(
'show_all_ticks',
'show_series_picker'
));
}
}

View file

@ -0,0 +1,77 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\Plugin\Visualization;
use Piwik\View;
use Piwik\Common;
use Piwik\Period;
use Piwik\API\Request as ApiRequest;
/**
* DataTable visualization that shows DataTable data in an HTML table.
*
* @property HtmlTable\Config $config
*/
class HtmlTable extends Visualization
{
const ID = 'table';
const TEMPLATE_FILE = "@CoreVisualizations/_dataTableViz_htmlTable.twig";
const FOOTER_ICON = 'plugins/Zeitgeist/images/table.png';
const FOOTER_ICON_TITLE = 'General_DisplaySimpleTable';
public static function getDefaultConfig()
{
return new HtmlTable\Config();
}
public static function getDefaultRequestConfig()
{
return new HtmlTable\RequestConfig();
}
public function beforeRender()
{
if ($this->requestConfig->idSubtable
&& $this->config->show_embedded_subtable) {
$this->config->show_visualization_only = true;
}
// we do not want to get a datatable\map
$period = Common::getRequestVar('period', 'day', 'string');
if (Period\Range::parseDateRange($period)) {
$period = 'range';
}
if ($this->dataTable->getRowsCount()) {
$request = new ApiRequest(array(
'method' => 'API.get',
'module' => 'API',
'action' => 'get',
'format' => 'original',
'filter_limit' => '-1',
'disable_generic_filters' => 1,
'expanded' => 0,
'flat' => 0,
'filter_offset' => 0,
'period' => $period,
'showColumns' => implode(',', $this->config->columns_to_display),
'columns' => implode(',', $this->config->columns_to_display)
));
$dataTable = $request->process();
$this->assignTemplateVar('siteSummary', $dataTable);
}
}
}

View file

@ -0,0 +1,66 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\View;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class AllColumns extends HtmlTable
{
const ID = 'tableAllColumns';
const FOOTER_ICON = 'plugins/Zeitgeist/images/table_more.png';
const FOOTER_ICON_TITLE = 'General_DisplayTableWithMoreMetrics';
public function beforeRender()
{
$this->config->show_extra_columns = true;
$this->config->datatable_css_class = 'dataTableVizAllColumns';
$this->config->show_exclude_low_population = true;
parent::beforeRender();
}
public function beforeGenericFiltersAreAppliedToLoadedDataTable()
{
$this->dataTable->filter('AddColumnsProcessedMetrics');
$properties = $this->config;
$this->dataTable->filter(function ($dataTable) use ($properties) {
$columnsToDisplay = array('label', 'nb_visits');
if (in_array('nb_uniq_visitors', $dataTable->getColumns())) {
$columnsToDisplay[] = 'nb_uniq_visitors';
}
$columnsToDisplay = array_merge(
$columnsToDisplay, array('nb_actions', 'nb_actions_per_visit', 'avg_time_on_site', 'bounce_rate')
);
// only display conversion rate for the plugins that do not provide "per goal" metrics
// otherwise, conversion rate is meaningless as a whole (since we don't process 'cross goals' conversions)
if (!$properties->show_goals) {
$columnsToDisplay[] = 'conversion_rate';
}
$properties->columns_to_display = $columnsToDisplay;
});
}
public function afterGenericFiltersAreAppliedToLoadedDataTable()
{
$prettifyTime = array('\Piwik\MetricsFormatter', 'getPrettyTimeFromSeconds');
$this->dataTable->filter('ColumnCallbackReplace', array('avg_time_on_site', $prettifyTime));
}
}

View file

@ -0,0 +1,119 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\ViewDataTable\Config as VisualizationConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends VisualizationConfig
{
/**
* If this property is set to true, subtables will be shown as embedded in the original table.
* If false, subtables will be shown as whole tables between rows.
*
* Default value: false
*/
public $show_embedded_subtable = false;
/**
* Controls whether the entire DataTable should be rendered (including subtables) or just one
* specific table in the tree.
*
* Default value: false
*/
public $show_expanded = false;
/**
* When showing an expanded datatable, this property controls whether rows with subtables are
* replaced with their subtables, or if they are shown alongside their subtables.
*
* Default value: false
*/
public $replace_row_with_subtable = false;
/**
* Controls whether any DataTable Row Action icons are shown. If true, no icons are shown.
*
* Default value: false
*/
public $disable_row_actions = false;
/**
* Controls whether the row evolution DataTable Row Action icon is shown or not.
*
* Default value: false
*/
public $disable_row_evolution = false;
/**
* If true, the 'label', 'nb_visits', 'nb_uniq_visitors' (if present), 'nb_actions',
* 'nb_actions_per_visit', 'avg_time_on_site', 'bounce_rate' and 'conversion_rate' (if
* goals view is not allowed) are displayed.
*
* Default value: false
*/
public $show_extra_columns = false;
/**
* If true, conversions for each existing goal will be displayed for the visits in
* each row.
*
* Default value: false
*/
public $show_goals_columns = false;
/**
* If true, subtables will not be loaded when rows are clicked, but only if the
* 'show_goals_columns' property is also true.
*
* Default value: false
*/
public $disable_subtable_when_show_goals = false;
/**
* If true, the summary row will be colored differently than all other DataTable rows.
*
* Default value: false
*/
public $highlight_summary_row = false;
public function __construct()
{
parent::__construct();
$this->enable_sort = true;
$this->datatable_js_type = 'DataTable';
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'show_extra_columns',
'show_goals_columns',
'disable_row_evolution',
'disable_row_actions',
'enable_sort',
'keep_summary_row',
'subtable_controller_action',
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array(
'show_expanded',
'disable_row_actions',
'disable_row_evolution',
'show_extra_columns',
'show_goals_columns',
'disable_subtable_when_show_goals',
'keep_summary_row',
'highlight_summary_row',
));
}
}

View file

@ -0,0 +1,54 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\Common;
use Piwik\Config as PiwikConfig;
use Piwik\ViewDataTable\RequestConfig as VisualizationRequestConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class RequestConfig extends VisualizationRequestConfig
{
/**
* Controls whether the summary row is displayed on every page of the datatable view or not.
* If false, the summary row will be treated as the last row of the dataset and will only visible
* when viewing the last rows.
*
* Default value: false
*/
public $keep_summary_row = false;
public function __construct()
{
$this->filter_limit = PiwikConfig::getInstance()->General['datatable_default_limit'];
if (Common::getRequestVar('enable_filter_excludelowpop', false) == '1') {
$this->filter_excludelowpop = 'nb_visits';
$this->filter_excludelowpop_value = false;
}
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'search_recursive',
'filter_limit',
'filter_offset',
'filter_sort_column',
'filter_sort_order',
'keep_summary_row'
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array(
'keep_summary_row',
));
}
}

View file

@ -0,0 +1,48 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Piwik\DataTable;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\View;
/**
* DataTable visualization that displays DataTable data in a JQPlot graph.
* TODO: should merge all this logic w/ jqplotdatagenerator & 'Chart' visualizations.
*
* @property JqplotGraph\Config $config
*/
abstract class JqplotGraph extends Graph
{
const ID = 'jqplot_graph';
const TEMPLATE_FILE = '@CoreVisualizations/_dataTableViz_jqplotGraph.twig';
public static function getDefaultConfig()
{
return new JqplotGraph\Config();
}
public function getGraphData($dataTable, $properties)
{
$dataGenerator = $this->makeDataGenerator($properties);
return $dataGenerator->generate($dataTable);
}
/**
* @param $properties
* @return JqplotDataGenerator
*/
abstract protected function makeDataGenerator($properties);
}
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Pie.php';
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/Visualizations/JqplotGraph/Evolution.php';

View file

@ -0,0 +1,43 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
/**
* Visualization that renders HTML for a Bar graph using jqPlot.
*/
class Bar extends JqplotGraph
{
const ID = 'graphVerticalBar';
const FOOTER_ICON = 'plugins/Zeitgeist/images/chart_bar.png';
const FOOTER_ICON_TITLE = 'General_VBarGraph';
public function beforeRender()
{
parent::beforeRender();
$this->config->datatable_js_type = 'JqplotBarGraphDataTable';
}
public static function getDefaultConfig()
{
$config = new Config();
$config->max_graph_elements = 6;
return $config;
}
protected function makeDataGenerator($properties)
{
return JqplotDataGenerator::factory('bar', $properties);
}
}

View file

@ -0,0 +1,65 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Plugins\CoreVisualizations\Visualizations\Graph\Config as GraphConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends GraphConfig
{
/**
* The name of the JavaScript class to use as this graph's external series toggle. The class
* must be a subclass of JQPlotExternalSeriesToggle.
*
* @see self::EXTERNAL_SERIES_TOGGLE_SHOW_ALL
*
* Default value: false
*/
public $external_series_toggle = false;
/**
* Whether the graph should show all loaded series upon initial display.
*
* @see self::EXTERNAL_SERIES_TOGGLE
*
* Default value: false
*/
public $external_series_toggle_show_all = false;
/**
* The number of x-axis ticks for each x-axis label.
*
* Default: 2
*/
public $x_axis_step_size = 2;
public function __construct()
{
parent::__construct();
$this->show_exclude_low_population = false;
$this->show_offset_information = false;
$this->show_pagination_control = false;
$this->show_exclude_low_population = false;
$this->show_search = false;
$this->show_export_as_image_icon = true;
$this->y_axis_unit = '';
$this->addPropertiesThatShouldBeAvailableClientSide(array(
'external_series_toggle',
'external_series_toggle_show_all'
));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array('x_axis_step_size'));
}
}

View file

@ -0,0 +1,200 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Period\Range;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Site;
/**
* Visualization that renders HTML for a line graph using jqPlot.
*
* @property Evolution\Config $config
*/
class Evolution extends JqplotGraph
{
const ID = 'graphEvolution';
const SERIES_COLOR_COUNT = 8;
public static function getDefaultConfig()
{
return new Evolution\Config();
}
public function beforeRender()
{
parent::beforeRender();
$this->config->datatable_js_type = 'JqplotEvolutionGraphDataTable';
}
public function beforeLoadDataTable()
{
$this->calculateEvolutionDateRange();
parent::beforeLoadDataTable();
// period will be overridden when 'range' is requested in the UI
// but the graph will display for each day of the range.
// Default 'range' behavior is to return the 'sum' for the range
if (Common::getRequestVar('period', false) == 'range') {
$this->requestConfig->request_parameters_to_modify['period'] = 'day';
}
$this->config->custom_parameters['columns'] = $this->config->columns_to_display;
}
public function afterAllFiltersAreApplied()
{
parent::afterAllFiltersAreApplied();
if (false === $this->config->x_axis_step_size) {
$rowCount = $this->dataTable->getRowsCount();
$this->config->x_axis_step_size = $this->getDefaultXAxisStepSize($rowCount);
}
}
protected function makeDataGenerator($properties)
{
return JqplotDataGenerator::factory('evolution', $properties);
}
/**
* Based on the period, date and evolution_{$period}_last_n query parameters,
* calculates the date range this evolution chart will display data for.
*/
private function calculateEvolutionDateRange()
{
$period = Common::getRequestVar('period');
$defaultLastN = self::getDefaultLastN($period);
$originalDate = Common::getRequestVar('date', 'last' . $defaultLastN, 'string');
if ('range' != $period) { // show evolution limit if the period is not a range
$this->config->show_limit_control = true;
// set the evolution_{$period}_last_n query param
if (Range::parseDateRange($originalDate)) {
// if a multiple period
// overwrite last_n param using the date range
$oPeriod = new Range($period, $originalDate);
$lastN = count($oPeriod->getSubperiods());
} else {
// if not a multiple period
list($newDate, $lastN) = self::getDateRangeAndLastN($period, $originalDate, $defaultLastN);
$this->requestConfig->request_parameters_to_modify['date'] = $newDate;
$this->config->custom_parameters['dateUsedInGraph'] = $newDate;
}
$lastNParamName = self::getLastNParamName($period);
$this->config->custom_parameters[$lastNParamName] = $lastN;
}
}
/**
* Returns the entire date range and lastN value for the current request, based on
* a period type and end date.
*
* @param string $period The period type, 'day', 'week', 'month' or 'year'
* @param string $endDate The end date.
* @param int|null $defaultLastN The default lastN to use. If null, the result of
* getDefaultLastN is used.
* @return array An array w/ two elements. The first is a whole date range and the second
* is the lastN number used, ie, array('2010-01-01,2012-01-02', 2).
*/
public static function getDateRangeAndLastN($period, $endDate, $defaultLastN = null)
{
if ($defaultLastN === null) {
$defaultLastN = self::getDefaultLastN($period);
}
$lastNParamName = self::getLastNParamName($period);
$lastN = Common::getRequestVar($lastNParamName, $defaultLastN, 'int');
$site = new Site(Common::getRequestVar('idSite'));
$dateRange = Range::getRelativeToEndDate($period, 'last' . $lastN, $endDate, $site);
return array($dateRange, $lastN);
}
/**
* Returns the default last N number of dates to display for a given period.
*
* @param string $period 'day', 'week', 'month' or 'year'
* @return int
*/
public static function getDefaultLastN($period)
{
switch ($period) {
case 'week':
return 26;
case 'month':
return 24;
case 'year':
return 5;
case 'day':
default:
return 30;
}
}
/**
* Returns the query parameter that stores the lastN number of periods to get for
* the evolution graph.
*
* @param string $period The period type, 'day', 'week', 'month' or 'year'.
* @return string
*/
public static function getLastNParamName($period)
{
return "evolution_{$period}_last_n";
}
public function getDefaultXAxisStepSize($countGraphElements)
{
// when the number of elements plotted can be small, make sure the X legend is useful
if ($countGraphElements <= 7) {
return 1;
}
$periodLabel = Common::getRequestVar('period');
switch ($periodLabel) {
case 'day':
case 'range':
$steps = 5;
break;
case 'week':
$steps = 4;
break;
case 'month':
$steps = 5;
break;
case 'year':
$steps = 5;
break;
default:
$steps = 5;
break;
}
$paddedCount = $countGraphElements + 2; // pad count so last label won't be cut off
return ceil($paddedCount / $steps);
}
}

View file

@ -0,0 +1,41 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Config as JqplotGraphConfig;
/**
* DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
*/
class Config extends JqplotGraphConfig
{
/**
* Whether to show a line graph or a bar graph.
*
* Default value: true
*/
public $show_line_graph = true;
public function __construct()
{
parent::__construct();
$this->show_all_views_icons = false;
$this->show_table = false;
$this->show_table_all_columns = false;
$this->hide_annotations_view = false;
$this->x_axis_step_size = false;
$this->show_line_graph = true;
$this->addPropertiesThatShouldBeAvailableClientSide(array('show_line_graph'));
$this->addPropertiesThatCanBeOverwrittenByQueryParams(array('show_line_graph'));
}
}

View file

@ -0,0 +1,58 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
use Piwik\Plugins\CoreVisualizations\JqplotDataGenerator;
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph;
/**
* Visualization that renders HTML for a Pie graph using jqPlot.
*/
class Pie extends JqplotGraph
{
const ID = 'graphPie';
const FOOTER_ICON = 'plugins/Zeitgeist/images/chart_pie.png';
const FOOTER_ICON_TITLE = 'General_Piechart';
public static function getDefaultConfig()
{
$config = new Config();
$config->max_graph_elements = 6;
$config->allow_multi_select_series_picker = false;
return $config;
}
public function beforeRender()
{
parent::beforeRender();
$this->config->show_all_ticks = true;
$this->config->datatable_js_type = 'JqplotPieGraphDataTable';
}
public function afterAllFiltersAreApplied()
{
parent::afterAllFiltersAreApplied();
$metricColumn = reset($this->config->columns_to_display);
if ($metricColumn == 'label') {
$metricColumn = next($this->config->columns_to_display);
}
$this->config->columns_to_display = array($metricColumn ? : 'nb_visits');
}
protected function makeDataGenerator($properties)
{
return JqplotDataGenerator::factory('pie', $properties);
}
}

View file

@ -0,0 +1,128 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\CoreVisualizations\Visualizations;
use Exception;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Plugin\ViewDataTable;
/**
* Reads the requested DataTable from the API and prepare data for the Sparkline view.
*
*/
class Sparkline extends ViewDataTable
{
const ID = 'sparkline';
/**
* @see ViewDataTable::main()
* @return mixed
*/
protected function buildView()
{
// If period=range, we force the sparkline to draw daily data points
$period = Common::getRequestVar('period');
if ($period == 'range') {
$_GET['period'] = 'day';
}
$this->loadDataTableFromAPI();
// then revert the hack for potentially subsequent getRequestVar
$_GET['period'] = $period;
$values = $this->getValuesFromDataTable($this->dataTable);
if (empty($values)) {
$values = array_fill(0, 30, 0);
}
$graph = new \Piwik\Visualization\Sparkline();
$graph->setValues($values);
$height = Common::getRequestVar('height', 0, 'int');
if (!empty($height)) {
$graph->setHeight($height);
}
$width = Common::getRequestVar('width', 0, 'int');
if (!empty($width)) {
$graph->setWidth($width);
}
$graph->main();
return $graph;
}
/**
* @param DataTable\Map $dataTableMap
* @param string $columnToPlot
*
* @return array
* @throws \Exception
*/
protected function getValuesFromDataTableMap($dataTableMap, $columnToPlot)
{
$dataTableMap->applyQueuedFilters();
$values = array();
foreach ($dataTableMap->getDataTables() as $table) {
if ($table->getRowsCount() > 1) {
throw new Exception("Expecting only one row per DataTable");
}
$value = 0;
$onlyRow = $table->getFirstRow();
if (false !== $onlyRow) {
if (!empty($columnToPlot)) {
$value = $onlyRow->getColumn($columnToPlot);
} // if not specified, we load by default the first column found
// eg. case of getLastDistinctCountriesGraph
else {
$columns = $onlyRow->getColumns();
$value = current($columns);
}
}
$values[] = $value;
}
return $values;
}
protected function getValuesFromDataTable($dataTable)
{
$columns = $this->config->columns_to_display;
$columnToPlot = false;
if (!empty($columns)) {
$columnToPlot = reset($columns);
if ($columnToPlot == 'label') {
$columnToPlot = next($columns);
}
}
// a Set is returned when using the normal code path to request data from Archives, in all core plugins
// however plugins can also return simple datatable, hence why the sparkline can accept both data types
if ($this->dataTable instanceof DataTable\Map) {
$values = $this->getValuesFromDataTableMap($dataTable, $columnToPlot);
} elseif ($this->dataTable instanceof DataTable) {
$values = $this->dataTable->getColumn($columnToPlot);
} else {
$values = false;
}
return $values;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,80 @@
/**
* Piwik - Web Analytics
*
* DataTable UI class for JqplotGraph/Bar.
*
* @link http://www.jqplot.com
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function ($, require) {
var exports = require('piwik/UI'),
JqplotGraphDataTable = exports.JqplotGraphDataTable;
exports.JqplotBarGraphDataTable = function (element) {
JqplotGraphDataTable.call(this, element);
};
$.extend(exports.JqplotBarGraphDataTable.prototype, JqplotGraphDataTable.prototype, {
_setJqplotParameters: function (params) {
JqplotGraphDataTable.prototype._setJqplotParameters.call(this, params);
this.jqplotParams.seriesDefaults = {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
shadowOffset: 1,
shadowDepth: 2,
shadowAlpha: .2,
fillToZero: true,
barMargin: this.data[0].length > 10 ? 2 : 10
}
};
this.jqplotParams.piwikTicks = {
showTicks: true,
showGrid: false,
showHighlight: false,
tickColor: this.tickColor
};
this.jqplotParams.axes.xaxis.renderer = $.jqplot.CategoryAxisRenderer;
this.jqplotParams.axes.xaxis.tickOptions = {
showGridline: false
};
this.jqplotParams.canvasLegend = {
show: true
};
},
_bindEvents: function () {
this.setYTicks();
JqplotGraphDataTable.prototype._bindEvents.call(this);
},
_showDataPointTooltip: function (element, seriesIndex, valueIndex) {
var value = this.formatY(this.data[seriesIndex][valueIndex], seriesIndex);
var series = this.jqplotParams.series[seriesIndex].label;
var percentage = '';
if (typeof this.tooltip.percentages != 'undefined') {
percentage = this.tooltip.percentages[seriesIndex][valueIndex];
percentage = ' (' + percentage + '%)';
}
var label = this.jqplotParams.axes.xaxis.labels[valueIndex];
var text = '<strong>' + value + '</strong> ' + series + percentage;
$(element).tooltip({
track: true,
items: '*',
content: '<h3>' + label + '</h3>' + text,
show: false,
hide: false
}).trigger('mouseover');
}
});
})(jQuery, require);

View file

@ -0,0 +1,135 @@
/**
* Piwik - Web Analytics
*
* DataTable UI class for JqplotGraph/Evolution.
*
* @link http://www.jqplot.com
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function ($, require) {
var exports = require('piwik/UI'),
JqplotGraphDataTable = exports.JqplotGraphDataTable,
JqplotGraphDataTablePrototype = JqplotGraphDataTable.prototype;
exports.JqplotEvolutionGraphDataTable = function (element) {
JqplotGraphDataTable.call(this, element);
};
$.extend(exports.JqplotEvolutionGraphDataTable.prototype, JqplotGraphDataTablePrototype, {
_setJqplotParameters: function (params) {
JqplotGraphDataTablePrototype._setJqplotParameters.call(this, params);
var defaultParams = {
axes: {
xaxis: {
pad: 1.0,
renderer: $.jqplot.CategoryAxisRenderer,
tickOptions: {
showGridline: false
}
}
},
piwikTicks: {
showTicks: true,
showGrid: true,
showHighlight: true,
tickColor: this.tickColor
}
};
if (this.props.show_line_graph) {
defaultParams.seriesDefaults = {
lineWidth: 1,
markerOptions: {
style: "filledCircle",
size: 6,
shadow: false
}
};
} else {
defaultParams.seriesDefaults = {
renderer: $.jqplot.BarRenderer,
rendererOptions: {
shadowOffset: 1,
shadowDepth: 2,
shadowAlpha: .2,
fillToZero: true,
barMargin: this.data[0].length > 10 ? 2 : 10
}
};
}
var overrideParams = {
legend: {
show: false
},
canvasLegend: {
show: true
}
};
this.jqplotParams = $.extend(true, {}, defaultParams, this.jqplotParams, overrideParams);
},
_bindEvents: function () {
JqplotGraphDataTablePrototype._bindEvents.call(this);
var self = this;
var lastTick = false;
$('#' + this.targetDivId)
.on('jqplotMouseLeave', function (e, s, i, d) {
$(this).css('cursor', 'default');
JqplotGraphDataTablePrototype._destroyDataPointTooltip.call(this, $(this));
})
.on('jqplotClick', function (e, s, i, d) {
if (lastTick !== false && typeof self.jqplotParams.axes.xaxis.onclick != 'undefined'
&& typeof self.jqplotParams.axes.xaxis.onclick[lastTick] == 'string') {
var url = self.jqplotParams.axes.xaxis.onclick[lastTick];
piwikHelper.redirectToUrl(url);
}
})
.on('jqplotPiwikTickOver', function (e, tick) {
lastTick = tick;
var label;
if (typeof self.jqplotParams.axes.xaxis.labels != 'undefined') {
label = self.jqplotParams.axes.xaxis.labels[tick];
} else {
label = self.jqplotParams.axes.xaxis.ticks[tick];
}
var text = [];
for (var d = 0; d < self.data.length; d++) {
var value = self.formatY(self.data[d][tick], d);
var series = self.jqplotParams.series[d].label;
text.push('<strong>' + value + '</strong> ' + series);
}
$(this).tooltip({
track: true,
items: 'div',
content: '<h3>'+label+'</h3>'+text.join('<br />'),
show: false,
hide: false
}).trigger('mouseover');
if (typeof self.jqplotParams.axes.xaxis.onclick != 'undefined'
&& typeof self.jqplotParams.axes.xaxis.onclick[lastTick] == 'string') {
$(this).css('cursor', 'pointer');
}
});
this.setYTicks();
},
_destroyDataPointTooltip: function () {
// do nothing, tooltips are destroyed in the jqplotMouseLeave event
},
render: function () {
JqplotGraphDataTablePrototype.render.call(this);
}
});
})(jQuery, require);

View file

@ -0,0 +1,81 @@
/**
* Piwik - Web Analytics
*
* DataTable UI class for JqplotGraph/Pie.
*
* @link http://www.jqplot.com
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function ($, require) {
var exports = require('piwik/UI'),
JqplotGraphDataTable = exports.JqplotGraphDataTable;
exports.JqplotPieGraphDataTable = function (element) {
JqplotGraphDataTable.call(this, element);
};
$.extend(exports.JqplotPieGraphDataTable.prototype, JqplotGraphDataTable.prototype, {
_setJqplotParameters: function (params) {
JqplotGraphDataTable.prototype._setJqplotParameters.call(this, params);
this.jqplotParams.seriesDefaults = {
renderer: $.jqplot.PieRenderer,
rendererOptions: {
shadow: false,
showDataLabels: false,
sliceMargin: 1,
startAngle: 35
}
};
this.jqplotParams.piwikTicks = {
showTicks: false,
showGrid: false,
showHighlight: false,
tickColor: this.tickColor
};
this.jqplotParams.legend = {
show: false
};
this.jqplotParams.pieLegend = {
show: true,
labelColor: this.singleMetricColor
};
this.jqplotParams.canvasLegend = {
show: true,
singleMetric: true,
singleMetricColor: this.singleMetricColor
};
// pie charts have a different data format
if (!(this.data[0][0] instanceof Array)) { // check if already in different format
for (var i = 0; i < this.data[0].length; i++) {
this.data[0][i] = [this.jqplotParams.axes.xaxis.ticks[i], this.data[0][i]];
}
}
},
_showDataPointTooltip: function (element, seriesIndex, valueIndex) {
var value = this.formatY(this.data[0][valueIndex][1], 0);
var series = this.jqplotParams.series[0].label;
var percentage = this.tooltip.percentages[0][valueIndex];
var label = this.data[0][valueIndex][0];
var text = '<strong>' + percentage + '%</strong> (' + value + ' ' + series + ')';
$(element).tooltip({
track: true,
items: '*',
content: '<h3>' + label + '</h3>' + text,
show: false,
hide: false
}).trigger('mouseover');
}
});
})(jQuery, require);

View file

@ -0,0 +1,366 @@
/**
* Piwik - Web Analytics
*
* Series Picker control addition for DataTable visualizations.
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function ($, doc, require) {
/**
* This class creates and manages the Series Picker for certain DataTable visualizations.
*
* To add the series picker to your DataTable visualization, create a SeriesPicker instance
* and after your visualization has been rendered, call the 'init' method.
*
* To customize SeriesPicker placement and behavior, you can bind callbacks to the following
* events before calling 'init':
*
* 'placeSeriesPicker': Triggered after the DOM element for the series picker link is created.
* You must use this event to add the link to the dataTable. YOu can also
* use this event to position the link however you want.
*
* Callback Signature: function () {}
*
* 'seriesPicked': Triggered when the user selects one or more columns/rows.
*
* Callback Signature: function (eventInfo, columns, rows) {}
*
* Events are triggered via jQuery, so you bind callbacks to them like this:
*
* var picker = new SeriesPicker(dataTable);
* $(picker).bind('placeSeriesPicker', function () {
* $(this.domElem).doSomething(...);
* });
*
* @param {dataTable} dataTable The dataTable instance to add a series picker to.
* @constructor
*/
var SeriesPicker = function (dataTable) {
this.domElem = null;
this.dataTableId = dataTable.workingDivId;
// the columns that can be selected
this.selectableColumns = dataTable.props.selectable_columns;
// the rows that can be selected
this.selectableRows = dataTable.props.selectable_rows;
// render the picker?
this.show = !! dataTable.props.show_series_picker
&& (this.selectableColumns || this.selectableRows);
// can multiple rows we selected?
this.multiSelect = !! dataTable.props.allow_multi_select_series_picker;
// language strings
this.lang =
{
metricsToPlot: _pk_translate('General_MetricsToPlot'),
metricToPlot: _pk_translate('General_MetricToPlot'),
recordsToPlot: _pk_translate('General_RecordsToPlot')
};
this._pickerState = null;
this._pickerPopover = null;
};
SeriesPicker.prototype = {
/**
* Initializes the series picker by creating the element. Must be called when
* the datatable the picker is being attached to is ready for it to be drawn.
*/
init: function () {
if (!this.show) {
return;
}
var self = this;
// initialize dom element
this.domElem = $(doc.createElement('a'))
.addClass('jqplot-seriespicker')
.attr('href', '#')
.html('+')
// set opacity on 'hide'
.on('hide', function () {
$(this).css('opacity', .55);
})
.trigger('hide')
// show picker on hover
.hover(
function () {
var $this = $(this);
$this.css('opacity', 1);
if (!$this.hasClass('open')) {
$this.addClass('open');
self._showPicker();
}
},
function () {
// do nothing on mouseout because using this event doesn't work properly.
// instead, the timeout check beneath is used (_bindCheckPickerLeave()).
}
)
.click(function (e) {
e.preventDefault();
return false;
});
$(this).trigger('placeSeriesPicker');
},
/**
* Returns the translation of a metric that can be selected.
*
* @param {String} metric The name of the metric, ie, 'nb_visits' or 'nb_actions'.
* @return {String} The metric translation. If one cannot be found, the metric itself
* is returned.
*/
getMetricTranslation: function (metric) {
for (var i = 0; i != this.selectableColumns.length; ++i) {
if (this.selectableColumns[i].column == metric) {
return this.selectableColumns[i].translation;
}
}
return metric;
},
/**
* Creates the popover DOM element, binds event handlers to it, and then displays it.
*/
_showPicker: function () {
this._pickerState = {manipulated: false};
this._pickerPopover = this._createPopover();
this._positionPopover();
// hide and replot on mouse leave
var self = this;
this._bindCheckPickerLeave(function () {
var replot = self._pickerState.manipulated;
self._hidePicker(replot);
});
},
/**
* Creates a checkbox and related elements for a selectable column or selectable row.
*/
_createPickerPopupItem: function (config, type) {
var self = this;
if (type == 'column') {
var columnName = config.column,
columnLabel = config.translation,
cssClass = 'pickColumn';
} else {
var columnName = config.matcher,
columnLabel = config.label,
cssClass = 'pickRow';
}
var checkbox = $(document.createElement('input')).addClass('select')
.attr('type', this.multiSelect ? 'checkbox' : 'radio');
if (config.displayed && !(!this.multiSelect && this._pickerState.oneChecked)) {
checkbox.prop('checked', true);
this._pickerState.oneChecked = true;
}
// if we are rendering a column, remember the column name
// if it's a row, remember the string that can be used to match the row
checkbox.data('name', columnName);
var el = $(document.createElement('p'))
.append(checkbox)
.append($('<label/>').text(columnLabel))
.addClass(cssClass);
var replot = function () {
self._unbindPickerLeaveCheck();
self._hidePicker(true);
};
var checkBox = function (box) {
if (!self.multiSelect) {
self._pickerPopover.find('input.select:not(.current)').prop('checked', false);
}
box.prop('checked', true);
replot();
};
el.click(function (e) {
self._pickerState.manipulated = true;
var box = $(this).find('input.select');
if (!$(e.target).is('input.select')) {
if (box.is(':checked')) {
box.prop('checked', false);
} else {
checkBox(box);
}
} else {
if (box.is(':checked')) {
checkBox(box);
}
}
});
return el;
},
/**
* Binds an event to document that checks if the user has left the series picker.
*/
_bindCheckPickerLeave: function (onLeaveCallback) {
var offset = this._pickerPopover.offset();
var minX = offset.left;
var minY = offset.top;
var maxX = minX + this._pickerPopover.outerWidth();
var maxY = minY + this._pickerPopover.outerHeight();
var self = this;
this._onMouseMove = function (e) {
var currentX = e.pageX, currentY = e.pageY;
if (currentX < minX || currentX > maxX
|| currentY < minY || currentY > maxY
) {
self._unbindPickerLeaveCheck();
onLeaveCallback();
}
};
$(doc).mousemove(this._onMouseMove);
},
/**
* Unbinds the callback that was bound in _bindCheckPickerLeave.
*/
_unbindPickerLeaveCheck: function () {
$(doc).unbind('mousemove', this._onMouseMove);
},
/**
* Removes and destroys the popover dom element. If any columns/rows were selected, the
* 'seriesPicked' event is triggered.
*/
_hidePicker: function (replot) {
// hide picker
this._pickerPopover.hide();
this.domElem.trigger('hide').removeClass('open');
// replot
if (replot) {
var columns = [];
var rows = [];
this._pickerPopover.find('input:checked').each(function () {
if ($(this).closest('p').hasClass('pickRow')) {
rows.push($(this).data('name'));
} else {
columns.push($(this).data('name'));
}
});
var noRowSelected = this._pickerPopover.find('.pickRow').size() > 0
&& this._pickerPopover.find('.pickRow input:checked').size() == 0;
if (columns.length > 0 && !noRowSelected) {
$(this).trigger('seriesPicked', [columns, rows]);
// inform dashboard widget about changed parameters (to be restored on reload)
$('#' + this.dataTableId).closest('[widgetId]').trigger('setParameters', {columns: columns, rows: rows});
}
}
this._pickerPopover.remove();
},
/**
* Creates and returns the popover element. This element shows a list of checkboxes, one
* for each selectable column/row.
*/
_createPopover: function () {
var hasColumns = $.isArray(this.selectableColumns) && this.selectableColumns.length;
var hasRows = $.isArray(this.selectableRows) && this.selectableRows.length;
var popover = $('<div/>')
.addClass('jqplot-seriespicker-popover');
// create headline element
var title = this.multiSelect ? this.lang.metricsToPlot : this.lang.metricToPlot;
popover.append($('<p/>').addClass('headline').html(title));
// create selectable columns list
if (hasColumns) {
for (var i = 0; i < this.selectableColumns.length; i++) {
var column = this.selectableColumns[i];
popover.append(this._createPickerPopupItem(column, 'column'));
}
}
// create selectable rows list
if (hasRows) {
// "records to plot" subheadline
var header = $('<p/>').addClass('headline').addClass('recordsToPlot').html(this.lang.recordsToPlot);
popover.append(header);
// render the selectable rows
for (var i = 0; i < this.selectableRows.length; i++) {
var row = this.selectableRows[i];
popover.append(this._createPickerPopupItem(row, 'row'));
}
}
popover.hide();
return popover;
},
/**
* Positions the popover element.
*/
_positionPopover: function () {
var $body = $('body'),
popover = this._pickerPopover,
pickerLink = this.domElem,
pickerLinkLeft = pickerLink.offset().left,
bodyRight = $body.offset().left + $body.width()
;
$body.prepend(popover);
var neededSpace = popover.outerWidth() + 10;
var linkOffset = pickerLink.offset();
if (navigator.appVersion.indexOf("MSIE 7.") != -1) {
linkOffset.left -= 10;
}
// try to display popover to the right
var margin = parseInt(pickerLink.css('margin-left')) - 4;
var popoverRight = pickerLinkLeft + margin + neededSpace;
if (popoverRight < bodyRight
// make sure it's not too far to the left
|| popoverRight < 0
) {
popover.css('margin-left', (linkOffset.left - 4) + 'px').show();
} else {
// display to the left
popover.addClass('alignright')
.css('margin-left', (linkOffset.left - neededSpace + 38) + 'px')
.css('background-position', (popover.outerWidth() - 25) + 'px 4px')
.show();
}
popover.css('margin-top', (linkOffset.top - 5) + 'px').show();
}
};
var exports = require('piwik/DataTableVisualizations/Widgets');
exports.SeriesPicker = SeriesPicker;
})(jQuery, document, require);

View file

@ -0,0 +1,28 @@
/* container of each table */
.dataTableVizHtmlTable > .dataTableWrapper {
width: 450px;
/* not more than 450px to make sure 2 tables can fit horizontally on a 1024 screen */
}
.dataTableVizAllColumns > .dataTableWrapper {
width: 535px;
}
.dataTableVizPie > .dataTableWrapper, .dataTableVizBar > .dataTableWrapper {
width: 500px;
min-height: 1px;
}
.piwik-graph {
height: 250px;
}
.dataTableVizEvolution {
> .dataTableWrapper {
width: 100%;
}
.piwik-graph {
height: 170px;
}
}

View file

@ -0,0 +1,259 @@
.jqplot-loading {
background: url(../../Zeitgeist/images/loading-blue.gif) no-repeat center center white;
position: absolute;
z-index: 10;
}
.jqplot-target {
position: relative;
color: #333;
font-size: 1em;
}
.jqplot-axis {
font-size: 0.75em;
}
.jqplot-xaxis {
margin-top: 10px;
}
.jqplot-yaxis {
margin-right: 10px;
}
.jqplot-y2axis,
.jqplot-y3axis {
margin: 0 3px 0 7px;
}
.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick {
position: absolute;
}
.jqplot-xaxis-tick {
top: 0;
left: 15px;
vertical-align: top;
}
.jqplot-yaxis-tick {
right: 0;
top: 15px;
text-align: right;
}
.jqplot-yaxis-tick.jqplot-breakTick {
right: -20px;
margin-right: 0;
padding: 1px 5px 1px 5px;
z-index: 2;
font-size: 1.5em;
}
.jqplot-xaxis-label {
margin-top: 10px;
font-size: 11pt;
position: absolute;
}
.jqplot-yaxis-label {
margin-right: 10px;
font-size: 11pt;
position: absolute;
}
.jqplot-title {
top: 0;
left: 0;
padding-bottom: 0.5em;
font-size: 1.2em;
}
/**
* ROW EVOLUTION POPUP
*/
.rowevolution {
position: relative;
overflow: hidden;
text-align: left;
}
.rowevolution h2 {
font-size: 16px;
margin: 0;
padding: 0;
}
.rowevolution .metrics-container {
padding: 11px 0 5px 0;
}
.rowevolution table.metrics {
border-spacing: 0;
}
.multirowevolution table.metrics {
margin-bottom: 12px;
}
.rowevolution table.metrics,
.multirowevolution table.metrics {
/* prevent select for shift-click on metric toggles */
user-select: none; /* CSS3 */
-moz-user-select: none; /* Gecko (Firefox) */
-khtml-user-select: none; /* Webkit (Safari, Chrome) */
}
.rowevolution table.metrics tr {
margin: 0;
padding: 0;
cursor: pointer;
}
.rowevolution table.metrics td {
vertical-align: middle;
text-align: left;
margin: 0;
padding: 4px 0;
cursor: pointer;
}
.rowevolution table.metrics td.sparkline {
width: 120px;
}
.multirowevolution table.metrics td.sparkline {
padding-top: 15px;
}
/** IE7 does not support inline image data, which is needed for spark lines */
*+html .multirowevolution table.metrics td.sparkline,
*+html .rowevolution table.metrics td.sparkline {
display: none;
}
.rowevolution table.metrics td.text {
font-size: 13px;
line-height: 18px;
color: #7E7363;
font-weight: bold;
}
.multirowevolution table.metrics td.text {
padding-top: 8px;
}
.rowevolution table.metrics td.text span.details {
font-weight: normal;
color: #444;
}
.rowevolution table.metrics td.text span.change {
display: block;
float: left;
padding-left: 15px;
}
.rowevolution table.metrics td.text span.good {
color: #008000;
}
.rowevolution table.metrics td.text span.bad {
color: #f00;
}
.rowevolution-documentation {
font-size: 12px;
margin: 2px 0 5px 0;
padding: 5px 0 5px 23px;
color: #888;
background: url(../../Zeitgeist/images/help.png) no-repeat left center;
}
.rowevolution .metric-selectbox,
.rowevolution .compare-container {
padding: 15px 0 5px 0;
}
.rowevolution .metric-selectbox select {
font-size: 13px;
color: #444;
margin: 8px 0 0 0;
padding: 0;
}
a.rowevolution-startmulti {
font-size: 12px;
color: #7E7363;
font-weight: bold;
text-decoration: none;
}
a.rowevolution-startmulti:hover {
color: #444;
}
/**
* SERIES PICKER FOR CHARTS
*/
.jqplot-seriespicker {
display: block;
position: absolute;
z-index: 9;
width: 24px;
height: 16px;
margin-top: 3px;
background: url(../../Zeitgeist/images/chart_line_edit.png) no-repeat center center;
overflow: hidden;
text-indent: -999px;
}
.jqplot-seriespicker-popover {
display: block;
position: absolute;
z-index: 1010; /* must be above ui dialog */
margin-top: -2px;
background: url(../../Zeitgeist/images/chart_line_edit.png) no-repeat 7px 4px #f7f7f7;
font-size: 11px;
font-weight: normal;
border: 1px solid #e4e5e4;
padding: 6px 9px;
border-radius: 4px;
-moz-box-shadow: 1px 1px 2px #666;
-webkit-box-shadow: 1px 1px 2px #666;
box-shadow: 1px 1px 2px #666;
}
.jqplot-seriespicker-popover p {
margin: 0;
padding: 0 4px 0 0;
line-height: 15px;
vertical-align: middle;
}
.jqplot-seriespicker-popover p.headline {
font-weight: bold;
font-size: 12px;
padding: 0 0 6px 22px;
color: #7E7363;
}
.jqplot-seriespicker-popover p.headline.recordsToPlot {
padding: 8px 0 3px 0;
}
.jqplot-seriespicker-popover.alignright p.headline {
padding: 0 22px 6px 0;
}
.jqplot-seriespicker-popover input.select {
margin-right: 8px;
}
.jqplot-seriespicker-popover p.pickColumn,
.jqplot-seriespicker-popover p.pickRow {
cursor: pointer;
}

View file

@ -0,0 +1,50 @@
{%- set subtablesAreDisabled = properties.show_goals_columns|default(false)
and properties.disable_subtable_when_show_goals|default(false) -%}
{%- set showingEmbeddedSubtable = properties.show_embedded_subtable is not empty
and idSubtable|default(false) -%}
{% if error is defined %}
{{ error.message }}
{% else %}
{%- if not showingEmbeddedSubtable -%}
<table cellspacing="0" class="dataTable">
{% include "@CoreHome/_dataTableHead.twig" %}
<tbody>
{%- endif -%}
{% if showingEmbeddedSubtable and dataTable.getRowsCount() == 0 %}
<tr>
<td colspan="{{ properties.columns_to_display|length }}">{{ 'CoreHome_CategoryNoData'|translate }}</td>
</tr>
{% else %}
{%- for rowId, row in dataTable.getRows() -%}
{%- set rowHasSubtable = not subtablesAreDisabled and row.getIdSubDataTable() and properties.subtable_controller_action is not null -%}
{%- set shouldHighlightRow = rowId == constant('Piwik\\DataTable::ID_SUMMARY_ROW') and properties.highlight_summary_row -%}
{# display this row if it doesn't have a subtable or if we don't replace the row with the subtable #}
{%- set showRow = subtablesAreDisabled
or not rowHasSubtable
or not properties.show_expanded|default(false)
or not properties.replace_row_with_subtable|default(false) -%}
{% if showRow %}
<tr {% if rowHasSubtable %}id="{{ row.getIdSubDataTable() }}"{% endif %}
class="{{ row.getMetadata('css_class') }} {% if rowHasSubtable %}subDataTable{% endif %}{% if shouldHighlightRow %} highlight{% endif %}">
{% for column in properties.columns_to_display %}
<td>
{% include "@CoreHome/_dataTableCell.twig" with properties %}
</td>
{% endfor %}
</tr>
{% endif %}
{# display subtable if present and showing expanded datatable #}
{% if properties.show_expanded|default(false) and rowHasSubtable %}
{% include "@CoreVisualizations/_dataTableViz_htmlTable.twig" with {'dataTable': row.getSubtable(), 'idSubtable': row.getIdSubDataTable()} %}
{% endif %}
{%- endfor -%}
{% endif %}
{%- if not showingEmbeddedSubtable -%}
</tbody>
</table>
{%- endif -%}
{% endif %}

View file

@ -0,0 +1,3 @@
<div class="jqplot-graph">
<div class="piwik-graph" data-data="{{ visualization.getGraphData(dataTable, properties)|json_encode }}"></div>
</div>

View file

@ -0,0 +1,18 @@
{% set cloudColumn = properties.columns_to_display[1] %}
<div class="tagCloud">
{% for word,value in cloudValues %}
<span title="{{ value.word }} ({{ value.value }} {{ properties.translations[cloudColumn]|default(cloudColumn) }})" class="word size{{ value.size }}
{# we strike tags with 0 hits #}
{% if value.value == 0 %}valueIsZero{% endif %}">
{% if labelMetadata[value.word].url is not sameas(false) %}
<a href="{{ labelMetadata[value.word].url }}" target="_blank">
{% endif %}
{% if labelMetadata[value.word].logo is not sameas(false) %}
<img src="{{ labelMetadata[value.word].logo }}" width="{{ value.logoWidth }}" />
{% else %}
{{ value.wordTruncated }}
{% endif %}
{% if labelMetadata[value.word].url is not sameas(false) %}</a>{% endif %}
</span>
{% endfor %}
</div>