diff --git a/core/DataTable/Filter/CalculateEvolutionFilter.php b/core/DataTable/Filter/CalculateEvolutionFilter.php
index 7328245ab64..24c86121853 100755
--- a/core/DataTable/Filter/CalculateEvolutionFilter.php
+++ b/core/DataTable/Filter/CalculateEvolutionFilter.php
@@ -34,12 +34,12 @@ class CalculateEvolutionFilter extends ColumnCallbackAddColumnPercentage
*
* @var DataTable
*/
- private $pastDataTable;
+ protected $pastDataTable;
/**
* Tells if column being added is the revenue evolution column.
*/
- private $isRevenueEvolution = null;
+ protected $isRevenueEvolution = null;
/**
* Constructor.
@@ -130,7 +130,7 @@ protected function formatValue($value, $divisor)
* @param Row $row The row in the 'current' DataTable.
* @return bool|Row
*/
- private function getPastRowFromCurrent($row)
+ protected function getPastRowFromCurrent($row)
{
return $this->pastDataTable->getRowFromLabel($row->getColumn('label'));
}
diff --git a/core/Period/Range.php b/core/Period/Range.php
index a814db98f5d..ab542fe8386 100644
--- a/core/Period/Range.php
+++ b/core/Period/Range.php
@@ -365,6 +365,24 @@ protected function fillArraySubPeriods($startDate, $endDate, $period)
* @api
*/
public static function getLastDate($date = false, $period = false)
+ {
+ return self::getDateXPeriodsAgo(1, $date, $period);
+ }
+
+ /**
+ * Returns the date that is X periods before the supplied date.
+ *
+ * @param bool|string $date The date to get the last date of.
+ * @param bool|string $period The period to use (either 'day', 'week', 'month', 'year');
+ * @param int $subXPeriods How many periods in the past the date should be, for instance 1 or 7.
+ * If sub period is 365 days and the current year is a leap year we assume you want to get the
+ * day one year ago and change the value to 366 days therefore.
+ *
+ * @return array An array with two elements, a string for the date before $date and
+ * a Period instance for the period before $date.
+ * @api
+ */
+ public static function getDateXPeriodsAgo($subXPeriods, $date = false, $period = false)
{
if ($date === false) {
$date = Common::getRequestVar('date');
@@ -374,20 +392,24 @@ public static function getLastDate($date = false, $period = false)
$period = Common::getRequestVar('period');
}
+ if (365 == $subXPeriods && 'day' == $period && Date::today()->isLeapYear()) {
+ $subXPeriods = 366;
+ }
+
// can't get the last date for range periods & dates that use lastN/previousN
$strLastDate = false;
- $lastPeriod = false;
+ $lastPeriod = false;
if ($period != 'range' && !preg_match('/(last|previous)([0-9]*)/', $date, $regs)) {
if (strpos($date, ',')) // date in the form of 2011-01-01,2011-02-02
{
$rangePeriod = new Range($period, $date);
- $lastStartDate = $rangePeriod->getDateStart()->addPeriod(-1, $period);
- $lastEndDate = $rangePeriod->getDateEnd()->addPeriod(-1, $period);
+ $lastStartDate = $rangePeriod->getDateStart()->subPeriod($subXPeriods, $period);
+ $lastEndDate = $rangePeriod->getDateEnd()->subPeriod($subXPeriods, $period);
$strLastDate = "$lastStartDate,$lastEndDate";
} else {
- $lastPeriod = Date::factory($date)->addPeriod(-1, $period);
+ $lastPeriod = Date::factory($date)->subPeriod($subXPeriods, $period);
$strLastDate = $lastPeriod->toString();
}
}
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index febe013b737..a8ab8292f49 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -255,7 +255,7 @@ public function assignTemplateVar($vars, $value = null)
*/
protected function isThereDataToDisplay()
{
- return true;
+ return !empty($this->dataTable) && 0 < $this->dataTable->getRowsCount();
}
/**
diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js
index 9c752de251c..b202199e71c 100644
--- a/plugins/CoreHome/javascripts/dataTable.js
+++ b/plugins/CoreHome/javascripts/dataTable.js
@@ -81,6 +81,10 @@ DataTable.getDataTableByReport = function (report) {
$.extend(DataTable.prototype, UIControl.prototype, {
+ _init: function (domElem) {
+ // initialize your dataTable in your plugin
+ },
+
//initialisation function
init: function () {
var domElem = this.$element;
@@ -91,6 +95,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
this.loadedSubDataTable = {};
this.isEmpty = $('.pk-emptyDataTable', domElem).length > 0;
this.bindEventsAndApplyStyle(domElem);
+ this._init(domElem);
this.initialized = true;
},
diff --git a/plugins/CoreHome/templates/_dataTable.twig b/plugins/CoreHome/templates/_dataTable.twig
index c024a8e9a46..234d90ea943 100644
--- a/plugins/CoreHome/templates/_dataTable.twig
+++ b/plugins/CoreHome/templates/_dataTable.twig
@@ -17,7 +17,7 @@
{% if error is defined %}
{{ error.message }}
{% else %}
- {% if dataTable is empty or dataTable.getRowsCount() == 0 or dataTableHasNoData|default(false) %}
+ {% if dataTable is empty or dataTableHasNoData|default(false) %}
{% if showReportDataWasPurgedMessage is defined and showReportDataWasPurgedMessage %}
{{ 'CoreHome_DataForThisReportHasBeenPurged'|translate(deleteReportsOlderThan) }}
diff --git a/plugins/Insights/API.php b/plugins/Insights/API.php
new file mode 100644
index 00000000000..ebfab42d321
--- /dev/null
+++ b/plugins/Insights/API.php
@@ -0,0 +1,212 @@
+getMoversAndShakers($idSite, $period, $date, 'Actions_getPageUrls', 4, 4),
+ $this->getMoversAndShakers($idSite, $period, $date, 'Actions_getPageTitles', 4, 4),
+ $this->getMoversAndShakers($idSite, $period, $date, 'Referrers_getKeywords', 4, 4),
+ $this->getMoversAndShakers($idSite, $period, $date, 'Referrers_getCampaigns', 4, 4),
+ $this->getMoversAndShakers($idSite, $period, $date, 'Referrers_getAll', 4, 4),
+ $this->getMoversAndShakers($idSite, $period, $date, 'MultiSites_getAll', 4, 4),
+ );
+
+ $map = new DataTable\Map();
+
+ foreach ($tables as $table) {
+ $map->addTable($table, $table->getMetadata('reportName'));
+ }
+
+ return $map;
+ }
+
+ // force $limitX and ignore minVisitsPercent, minGrowthPercent
+ // segment
+ public function getMoversAndShakers(
+ $idSite, $period, $date, $reportUniqueId = '', $limitIncreaser = 5, $limitDecreaser = 5, $basedOnTotalMetric = false,
+ $minVisitsPercent = 2, $minGrowthPercent = 20, $orderBy = 'absolute',
+ $comparedToXPeriods = 1, $filterBy = '')
+ {
+ $metric = 'nb_visits';
+
+ $processedReport = new ProcessedReport();
+ $report = $processedReport->getReportMetadataByUniqueId($idSite, $reportUniqueId);
+
+ $lastDate = Range::getDateXPeriodsAgo(abs($comparedToXPeriods), $date, $period);
+
+ $currentReport = $this->requestReport($idSite, $period, $date, $report, $metric);
+ $lastReport = $this->requestReport($idSite, $period, $lastDate[0], $report, $metric);
+
+ $totalValue = $this->getTotalValue($idSite, $period, $date, $basedOnTotalMetric, $currentReport, $metric);
+ $minVisits = $this->getMinVisits($totalValue, $minVisitsPercent);
+
+ $considerMovers = false;
+ $considerNew = false;
+ $considerDisappeared = false;
+
+ switch ($filterBy) {
+ case '':
+ $considerMovers = true;
+ $considerNew = true;
+ $considerDisappeared = true;
+ break;
+ case 'movers':
+ $considerMovers = true;
+ break;
+ case 'new':
+ $considerNew = true;
+ break;
+ case 'disappeared':
+ $considerDisappeared = true;
+ break;
+ }
+
+ $dataTable = new DataTable();
+ $dataTable->filter(
+ 'Piwik\Plugins\Insights\DataTable\Filter\Insight',
+ array(
+ $currentReport,
+ $lastReport,
+ $metric,
+ $totalValue,
+ $filterBy,
+ $considerMovers,
+ $considerNew,
+ $considerDisappeared
+ )
+ );
+
+ $dataTable->filter(
+ 'Piwik\Plugins\Insights\DataTable\Filter\MinGrowth',
+ array(
+ 'growth_percent_numeric',
+ $minGrowthPercent
+ )
+ );
+
+ $dataTable->filter(
+ 'Piwik\Plugins\Insights\DataTable\Filter\RemoveIrrelevant',
+ array(
+ $metric,
+ $minVisits
+ )
+ );
+
+ $dataTable->filter(
+ 'Piwik\Plugins\Insights\DataTable\Filter\OrderBy',
+ array(
+ $this->getOrderByColumn($orderBy)
+ )
+ );
+
+ $dataTable->filter(
+ 'Piwik\Plugins\Insights\DataTable\Filter\Limit',
+ array(
+ 'growth_percent_numeric',
+ $limitIncreaser,
+ $limitDecreaser
+ )
+ );
+
+ $dataTable->setMetadataValues(array(
+ 'reportName' => $report['name'],
+ 'metricName' => $report['metrics'][$metric],
+ 'date' => $date,
+ 'lastDate' => $lastDate[0],
+ 'period' => $period,
+ 'report' => $report,
+ 'totalValue' => $totalValue,
+ 'minVisits' => $minVisits
+ ));
+
+ return $dataTable;
+ }
+
+ private function requestReport($idSite, $period, $date, $report, $metric)
+ {
+ $params = array(
+ 'method' => $report['module'] . '.' . $report['action'],
+ 'format' => 'original',
+ 'idSite' => $idSite,
+ 'period' => $period,
+ 'date' => $date,
+ 'flat' => 1,
+ 'filter_limit' => 10000,
+ 'showColumns' => $metric
+ );
+
+ if (!empty($report['parameters']) && is_array($report['parameters'])) {
+ $params = array_merge($params, $report['parameters']);
+ }
+
+ $request = new ApiRequest($params);
+ $table = $request->process();
+
+ return $table;
+ }
+
+ private function getMinVisits($totalValue, $minVisitsPercent)
+ {
+ $minVisits = (int) ceil(($totalValue / 100) * $minVisitsPercent);
+
+ return $minVisits;
+ }
+
+ private function getTotalValue($idSite, $period, $date, $basedOnTotalMetric, DataTable $currentReport, $metric)
+ {
+ if ($basedOnTotalMetric) {
+ $totals = $currentReport->getMetadata('totals');
+
+ if (!empty($totals[$metric])) {
+ $totalValue = $totals[$metric];
+ } else {
+ $totalValue = 0;
+ }
+
+ return $totalValue;
+ }
+
+ $visits = VisitsSummaryAPI::getInstance()->get($idSite, $period, $date, false, array($metric));
+ $totalValue = $visits->getFirstRow()->getColumn($metric);
+
+ return $totalValue;
+ }
+
+ private function getOrderByColumn($orderBy)
+ {
+ if ('relative' == $orderBy) {
+ $orderByColumn = 'growth_percent_numeric';
+ } elseif ('absolute' == $orderBy) {
+ $orderByColumn = 'difference';
+ } elseif ('importance' == $orderBy) {
+ $orderByColumn = 'importance';
+ } else {
+ throw new \Exception('Unsupported orderBy');
+ }
+
+ return $orderByColumn;
+ }
+}
diff --git a/plugins/Insights/Controller.php b/plugins/Insights/Controller.php
new file mode 100644
index 00000000000..ed67b9fada0
--- /dev/null
+++ b/plugins/Insights/Controller.php
@@ -0,0 +1,39 @@
+setBasicVariablesView($view);
+
+ $view->moversAndShakers = API::getInstance()->getOverallMoversAndShakers($idSite, $period, $date);
+ $view->showNoDataMessage = false;
+ $view->showInsightsControls = false;
+ $view->properties = array(
+ 'show_increase' => true,
+ 'show_decrease' => true
+ );
+
+ return $view->render();
+ }
+}
diff --git a/plugins/Insights/DataTable/Filter/Insight.php b/plugins/Insights/DataTable/Filter/Insight.php
new file mode 100644
index 00000000000..4adabdb1236
--- /dev/null
+++ b/plugins/Insights/DataTable/Filter/Insight.php
@@ -0,0 +1,104 @@
+totalValue = $totalValue;
+ $this->filterBy = $filterBy;
+ $this->currentDataTable = $currentDataTable;
+ $this->considerMovers = $considerMovers;
+ $this->considerNew = $considerNew;
+ $this->considerDisappeared = $considerDisappeared;
+ }
+
+ public function filter($table)
+ {
+ foreach ($this->currentDataTable->getRows() as $key => $row) {
+ $pastRow = $this->getPastRowFromCurrent($row);
+ $oldValue = 0;
+
+ if (!$pastRow && !$this->considerNew) {
+ continue;
+ }
+
+ if ($pastRow && $this->considerMovers) {
+ $oldValue = $pastRow->getColumn($this->columnValueToRead);
+ } elseif ($pastRow) {
+ continue;
+ }
+
+ $difference = $this->getDividend($row);
+ if ($difference === false) {
+ continue;
+ }
+
+ $newValue = $row->getColumn($this->columnValueToRead);
+ $divisor = $this->getDivisor($row);
+
+ $growthPercentage = $this->formatValue($difference, $divisor);
+
+ $this->addRow($table, $row, $growthPercentage, $newValue, $oldValue, $difference);
+ }
+
+ if ($this->considerDisappeared) {
+ foreach ($this->pastDataTable->getRows() as $key => $row) {
+
+ if (!$this->getRowFromTable($this->currentDataTable, $row)) {
+ continue;
+ }
+
+ $newValue = 0;
+ $oldValue = $row->getColumn($this->columnValueToRead);
+ $difference = $newValue - $oldValue;
+
+ $growthPercentage = '-100%';
+
+ $this->addRow($table, $row, $growthPercentage, $newValue, $oldValue, $difference);
+ }
+ }
+ }
+
+ private function getRowFromTable(DataTable $table, DataTable\Row $row)
+ {
+ return $table->getRowFromLabel($row->getColumn('label'));
+ }
+
+ private function addRow(DataTable $table, DataTable\Row $row, $growthPercentage, $newValue, $oldValue, $difference)
+ {
+ $columns = $row->getColumns();
+ $columns['growth_percent'] = $growthPercentage;
+ $columns['growth_percent_numeric'] = str_replace('%', '', $growthPercentage);
+ $columns['grown'] = $newValue >= $oldValue;
+ $columns['value_old'] = $oldValue;
+ $columns['value_new'] = $newValue;
+ $columns['difference'] = $difference;
+
+ if ($this->totalValue) {
+ $columns['importance'] = (abs($difference) / $this->totalValue) * 100; // magic formula here
+ }
+
+ $table->addRowFromArray(array(DataTable\Row::COLUMNS => $columns));
+ return $columns;
+ }
+}
\ No newline at end of file
diff --git a/plugins/Insights/DataTable/Filter/Limit.php b/plugins/Insights/DataTable/Filter/Limit.php
new file mode 100644
index 00000000000..9d39a39ff89
--- /dev/null
+++ b/plugins/Insights/DataTable/Filter/Limit.php
@@ -0,0 +1,48 @@
+columnToRead = $columnToRead;
+ $this->limitIncreaser = (int) $limitIncreaser;
+ $this->limitDecreaser = (int) $limitDecreaser;
+ }
+
+ public function filter($table)
+ {
+ $countIncreaser = 0;
+ $countDecreaser = 0;
+
+ foreach ($table->getRows() as $key => $row) {
+
+ if ($row->getColumn($this->columnToRead) >= 0) {
+ $countIncreaser++;
+
+ if ($countIncreaser > $this->limitIncreaser) {
+ $table->deleteRow($key);
+ }
+
+ } else {
+ $countDecreaser++;
+
+ if ($countDecreaser > $this->limitDecreaser) {
+ $table->deleteRow($key);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Insights/DataTable/Filter/MinGrowth.php b/plugins/Insights/DataTable/Filter/MinGrowth.php
new file mode 100644
index 00000000000..244eebf9d9e
--- /dev/null
+++ b/plugins/Insights/DataTable/Filter/MinGrowth.php
@@ -0,0 +1,44 @@
+growthColumn = $growthColumn;
+ $this->minGrowthPercent = $minGrowthPercent;
+ }
+
+ public function filter($table)
+ {
+ if (!$this->minGrowthPercent) {
+ return;
+ }
+
+ foreach ($table->getRows() as $key => $row) {
+
+ $growthNumeric = $row->getColumn($this->growthColumn);
+
+ if ($growthNumeric > $this->minGrowthPercent) {
+ continue;
+ } elseif ($growthNumeric < -$this->minGrowthPercent) {
+ continue;
+ }
+
+ $table->deleteRow($key);
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Insights/DataTable/Filter/OrderBy.php b/plugins/Insights/DataTable/Filter/OrderBy.php
new file mode 100644
index 00000000000..abb5c6bc7c4
--- /dev/null
+++ b/plugins/Insights/DataTable/Filter/OrderBy.php
@@ -0,0 +1,67 @@
+columnToRead = $columnToRead;
+ }
+
+ public function filter($table)
+ {
+ if (!$table->getRowsCount()) {
+ return;
+ }
+
+ $table->sort(array($this, 'sort'), $this->columnToRead);
+ }
+
+ public function sort(Row $a, Row $b)
+ {
+ $valA = $a->getColumn($this->columnToRead);
+ $valB = $b->getColumn($this->columnToRead);
+
+ if (!isset($valA) && !isset($valB)) {
+ return 0;
+ }
+
+ if (!isset($valA)) {
+ return 1;
+ }
+
+ if (!isset($valB)) {
+ return -1;
+ }
+
+ if ($valA > 0 && $valB < 0) {
+ return -1;
+ }
+
+ if ($valA < 0 && $valB < 0) {
+ return $valA < $valB ? -1 : 1;
+ }
+
+ if ($valA != $valB) {
+ return $valA < $valB ? 1 : -1;
+ }
+
+ return strnatcasecmp(
+ $a->getColumn('nb_visits'),
+ $b->getColumn('nb_visits')
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/Insights/DataTable/Filter/RemoveIrrelevant.php b/plugins/Insights/DataTable/Filter/RemoveIrrelevant.php
new file mode 100644
index 00000000000..7f82d1645d7
--- /dev/null
+++ b/plugins/Insights/DataTable/Filter/RemoveIrrelevant.php
@@ -0,0 +1,39 @@
+columnToRead = $columnToRead;
+ $this->minRequiredValue = $minRequiredValue;
+ }
+
+ public function filter($table)
+ {
+ if (!$this->minRequiredValue) {
+ return;
+ }
+
+ foreach ($table->getRows() as $key => $row) {
+
+ $value = $row->getColumn($this->columnToRead);
+
+ if ($this->minRequiredValue > $value) {
+ $table->deleteRow($key);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/plugins/Insights/Insights.php b/plugins/Insights/Insights.php
new file mode 100644
index 00000000000..b8c7a549d27
--- /dev/null
+++ b/plugins/Insights/Insights.php
@@ -0,0 +1,44 @@
+ 'addWidgets',
+ 'AssetManager.getJavaScriptFiles' => 'getJsFiles',
+ 'ViewDataTable.addViewDataTable' => 'getAvailableVisualizations'
+ );
+ }
+
+ public function getAvailableVisualizations(&$visualizations)
+ {
+ $visualizations[] = __NAMESPACE__ . '\\Visualizations\\Insight';
+ }
+
+ public function addWidgets()
+ {
+ WidgetsList::add('Insights_Category', 'Insights_OverviewWidgetTitle', 'Insights', 'getOverviewMoversAndShakers');
+ }
+
+ public function getJsFiles(&$jsFiles)
+ {
+ $jsFiles[] = "plugins/Insights/javascripts/insightsDataTable.js";
+ }
+
+}
diff --git a/plugins/Insights/Visualizations/Insight.php b/plugins/Insights/Visualizations/Insight.php
new file mode 100644
index 00000000000..4a235b22293
--- /dev/null
+++ b/plugins/Insights/Visualizations/Insight.php
@@ -0,0 +1,95 @@
+requestConfig->apiMethodToRequestDataTable;
+ $report = str_replace('.', '_', $report);
+
+ $this->requestConfig->apiMethodToRequestDataTable = 'Insights.getMoversAndShakers';
+ $this->requestConfig->request_parameters_to_modify = array(
+ 'reportUniqueId' => $report,
+ 'minVisitsPercent' => $this->requestConfig->min_visits_percent,
+ 'minGrowthPercent' => $this->requestConfig->min_growth_percent,
+ 'basedOnTotalMetric' => $this->requestConfig->based_on_total_metric,
+ 'comparedToXPeriods' => $this->requestConfig->compared_to_x_periods_ago,
+ 'orderBy' => $this->requestConfig->order_by,
+ 'filterBy' => $this->requestConfig->filter_by,
+ 'limitIncreaser' => $this->requestConfig->limit_increaser,
+ 'limitDecreaser' => $this->requestConfig->limit_decreaser,
+ );
+ }
+
+ public static function getDefaultConfig()
+ {
+ return new Insight\Config();
+ }
+
+ public static function getDefaultRequestConfig()
+ {
+ return new Insight\RequestConfig();
+ }
+
+ public function isThereDataToDisplay()
+ {
+ return true;
+ }
+
+ public function afterAllFiltersAreApplied()
+ {
+ $this->assignTemplateVar('showNoDataMessage', true);
+ $this->assignTemplateVar('showInsightsControls', true);
+ $this->assignTemplateVar('period', Common::getRequestVar('period', null, 'string'));
+ }
+
+ public function beforeRender()
+ {
+ $this->config->datatable_js_type = 'InsightsDataTable';
+ $this->config->show_limit_control = false;
+ $this->config->show_pagination_control = false;
+ $this->config->show_offset_information = false;
+ $this->config->show_search = false;
+ $this->config->title = 'Insights of ' . $this->config->title;
+ }
+
+ public static function canDisplayViewDataTable(ViewDataTable $view)
+ {
+ $period = Common::getRequestVar('period', null, 'string');
+ $date = Common::getRequestVar('date', null, 'string');
+
+ $lastDate = Range::getDateXPeriodsAgo(1, $date, $period);
+
+ if (empty($lastDate[0])) {
+ return false;
+ }
+
+ return parent::canDisplayViewDataTable($view);
+ }
+}
diff --git a/plugins/Insights/Visualizations/Insight/Config.php b/plugins/Insights/Visualizations/Insight/Config.php
new file mode 100644
index 00000000000..15c5f1cd2e3
--- /dev/null
+++ b/plugins/Insights/Visualizations/Insight/Config.php
@@ -0,0 +1,18 @@
+disable_generic_filters = true;
+ $this->disable_queued_filters = true;
+
+ $properties = array(
+ 'min_growth_percent',
+ 'min_visits_percent',
+ 'based_on_total_metric',
+ 'order_by',
+ 'compared_to_x_periods_ago',
+ 'filter_by',
+ 'limit_increaser',
+ 'limit_decreaser'
+ );
+
+ $this->addPropertiesThatShouldBeAvailableClientSide($properties);
+ $this->addPropertiesThatCanBeOverwrittenByQueryParams($properties);
+ }
+
+}
diff --git a/plugins/Insights/images/idea.png b/plugins/Insights/images/idea.png
new file mode 100644
index 00000000000..05ecb8feb5a
Binary files /dev/null and b/plugins/Insights/images/idea.png differ
diff --git a/plugins/Insights/javascripts/insightsDataTable.js b/plugins/Insights/javascripts/insightsDataTable.js
new file mode 100644
index 00000000000..400e50022ea
--- /dev/null
+++ b/plugins/Insights/javascripts/insightsDataTable.js
@@ -0,0 +1,101 @@
+/*!
+ * Piwik - Web Analytics
+ *
+ * @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'),
+ DataTable = exports.DataTable,
+ dataTablePrototype = DataTable.prototype;
+
+ /**
+ * UI control that handles extra functionality for Actions datatables.
+ *
+ * @constructor
+ */
+ exports.InsightsDataTable = function (element) {
+ this.parentAttributeParent = '';
+ this.parentId = '';
+ this.disabledRowDom = {}; // to handle double click on '+' row
+
+ DataTable.call(this, element);
+ };
+
+ $.extend(exports.InsightsDataTable.prototype, dataTablePrototype, {
+
+ handleRowActions: function () {},
+
+ _init: function (domElem) {
+ this.initMinGrowthPercentage(domElem);
+ this.initMinVisitsPercent(domElem);
+ this.initBasedOnTotalMetric(domElem);
+ this.initShowIncreaseOrDecrease(domElem);
+ this.initOrderBy(domElem);
+ this.initComparedToXPeriodsAgo(domElem);
+ this.initFilterBy(domElem);
+ },
+
+ initShowIncreaseOrDecrease: function (domElem) {
+ var self = this;
+ $('[name=showIncreaseOrDecrease]', domElem).bind('change', function (event) {
+ var value = event.target.value;
+
+ self.param.limit_increaser = (value == 'both' || value == 'increase') ? '5' : '0';
+ self.param.limit_decreaser = (value == 'both' || value == 'decrease') ? '5' : '0';
+ self.reloadAjaxDataTable(true);
+ });
+ },
+
+ initMinGrowthPercentage: function (domElem) {
+ var self = this;
+ $('[name=minGrowthPercent]', domElem).bind('change', function (event) {
+ self.param.min_growth_percent = event.target.value;
+ self.reloadAjaxDataTable(true);
+ });
+ },
+
+ initOrderBy: function (domElem) {
+ var self = this;
+ $('[name=orderBy]', domElem).bind('change', function (event) {
+ self.param.order_by = event.target.value;
+ self.reloadAjaxDataTable(true);
+ });
+ },
+
+ initMinVisitsPercent: function (domElem) {
+ var self = this;
+ $('[name=minVisitsPercent]', domElem).bind('change', function (event) {
+ self.param.min_visits_percent = event.target.value;
+ self.reloadAjaxDataTable(true);
+ });
+ },
+
+ initBasedOnTotalMetric: function (domElem) {
+ var self = this;
+ $('[name=basedOnTotalMetric]', domElem).bind('change', function (event) {
+ self.param.based_on_total_metric = event.target.value;
+ self.reloadAjaxDataTable(true);
+ });
+ },
+
+ initComparedToXPeriodsAgo: function (domElem) {
+ var self = this;
+ $('[name=comparedToXPeriodsAgo]', domElem).bind('change', function (event) {
+ self.param.compared_to_x_periods_ago = event.target.value;
+ self.reloadAjaxDataTable(true);
+ });
+ },
+
+ initFilterBy: function (domElem) {
+ var self = this;
+ $('[name=filterBy]', domElem).bind('change', function (event) {
+ self.param.filter_by = event.target.value;
+ self.reloadAjaxDataTable(true);
+ });
+ }
+ });
+
+})(jQuery, require);
\ No newline at end of file
diff --git a/plugins/Insights/lang/en.json b/plugins/Insights/lang/en.json
new file mode 100644
index 00000000000..cde88325336
--- /dev/null
+++ b/plugins/Insights/lang/en.json
@@ -0,0 +1,6 @@
+{
+ "Insights": {
+ "OverviewWidgetTitle": "Overview of movers and shakers",
+ "Category": "Insights"
+ }
+}
\ No newline at end of file
diff --git a/plugins/Insights/plugin.json b/plugins/Insights/plugin.json
new file mode 100644
index 00000000000..985eca4c6d7
--- /dev/null
+++ b/plugins/Insights/plugin.json
@@ -0,0 +1,15 @@
+{
+ "name": "Insights",
+ "version": "0.1.0",
+ "description": "Get insights",
+ "theme": false,
+ "license": "GPL v3+",
+ "homepage": "http://piwik.org",
+ "authors": [
+ {
+ "name": "Piwik",
+ "email": "hello@piwik.org",
+ "homepage": "http://piwik.org"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/plugins/Insights/templates/index.twig b/plugins/Insights/templates/index.twig
new file mode 100644
index 00000000000..fc91a813306
--- /dev/null
+++ b/plugins/Insights/templates/index.twig
@@ -0,0 +1,5 @@
+{% for dataTable in moversAndShakers.getDataTables() %}
+
+ {% include "@Insights/insightVisualization.twig" %}
+
+{% endfor %}
\ No newline at end of file
diff --git a/plugins/Insights/templates/insightControls.twig b/plugins/Insights/templates/insightControls.twig
new file mode 100644
index 00000000000..c0314bf0a4d
--- /dev/null
+++ b/plugins/Insights/templates/insightControls.twig
@@ -0,0 +1,93 @@
+
+ Show only rows with a
+
+ minimum impact of
+
+ {% for i in range(0, 20) %}
+ {{ i }}%
+ {% endfor %}
+ {% for i in range(22, 100, 2) %}
+ {{ i }}%
+ {% endfor %}
+
+
+ {% if properties.show_based_on_total_metric %}
+
+ compared to metric
+ compared to total visits
+
+ {% endif %}
+
+ and min growth of
+
+ {% for i in range(0, 1, 1) %}
+ {{ i }}%
+ {% endfor %}
+ {% for i in range(5, 100, 5) %}
+ {{ i }}%
+ {% endfor %}
+ {% for i in range(200, 1000, 100) %}
+ {{ i }}%
+ {% endfor %}
+
+
+ {% if period == 'day' %}
+
+
+ compare to previous day
+
+
+ compare to same day in previous week
+
+
+ compare to same day in previous year
+
+
+ {% elseif period == 'month' %}
+
+
+ compare to previous month
+
+
+ compare to same month in previous year
+
+
+ {% endif %}
+
+ Order them by
+
+
+ absolute
+
+
+ relative
+
+
+ importance
+
+ value
+
+ and show
+
+ {% if properties.show_filter_by %}
+
+ all
+ only movers
+ only new entries
+ only disappeared entries
+
+ {% endif %}
+
+ if they
+
+
+ increase or decrease
+
+
+ increase
+
+
+ decrease
+
+
+
\ No newline at end of file
diff --git a/plugins/Insights/templates/insightVisualization.twig b/plugins/Insights/templates/insightVisualization.twig
new file mode 100644
index 00000000000..8ef6f7184fb
--- /dev/null
+++ b/plugins/Insights/templates/insightVisualization.twig
@@ -0,0 +1,52 @@
+
+ {% if dataTable.getRowsCount > 0 %}
+
+
+
+
+ {{ dataTable.getMetadata('reportName') }}
+
+
+ {{ dataTable.getMetadata('metricName') }}
+
+
+ Evolution
+
+
+
+
+
+ {% for row in dataTable.getRows %}
+
+
+
+ {{ row.getColumn('label') }}
+
+
+ {% if row.getColumn('grown') %}
+ +{{ row.getColumn('difference') }}
+
+ +{{ row.getColumn('growth_percent') }}
+
+ {% else %}
+ {{ row.getColumn('difference') }}
+
+ {{ row.getColumn('growth_percent') }}
+
+ {% endif %}
+
+ {% endfor %}
+
+
+
+ {% elseif showNoDataMessage %}
+
+ No rows match the criteria
+
+ {% endif %}
+
+ {% if showInsightsControls %}
+ {% include "@Insights/insightControls.twig" %}
+ {% endif %}
+
\ No newline at end of file