diff --git a/oss/piwik/trunk/plugins/VisitTime/API.php b/oss/piwik/trunk/plugins/VisitTime/API.php
new file mode 100644
index 00000000..602f5e3a
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitTime/API.php
@@ -0,0 +1,55 @@
+getDataTable($name);
+ $dataTable->filter('Sort', array('label', 'asc', true));
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getTimeLabel'));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ return $dataTable;
+ }
+
+ public function getVisitInformationPerLocalTime( $idSite, $period, $date )
+ {
+ return $this->getDataTable('VisitTime_localTime', $idSite, $period, $date );
+ }
+
+ public function getVisitInformationPerServerTime( $idSite, $period, $date )
+ {
+ return $this->getDataTable('VisitTime_serverTime', $idSite, $period, $date );
+ }
+}
+
+function Piwik_getTimeLabel($label)
+{
+ return sprintf(Piwik_Translate('VisitTime_NHour'), $label);
+}
diff --git a/oss/piwik/trunk/plugins/VisitTime/Controller.php b/oss/piwik/trunk/plugins/VisitTime/Controller.php
new file mode 100644
index 00000000..baece1cf
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitTime/Controller.php
@@ -0,0 +1,60 @@
+dataTableVisitInformationPerLocalTime = $this->getVisitInformationPerLocalTime(true);
+ $view->dataTableVisitInformationPerServerTime = $this->getVisitInformationPerServerTime(true);
+ echo $view->render();
+ }
+
+ public function getVisitInformationPerServerTime( $fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory( 'graphVerticalBar');
+ $view->init( $this->pluginName, __FUNCTION__, "VisitTime.getVisitInformationPerServerTime" );
+
+ $view->setColumnsToDisplay( array('label','nb_visits') );
+ $view->setSortedColumn( 'label', 'asc' );
+ $view->setColumnTranslation('label', Piwik_Translate('VisitTime_ColumnServerTime'));
+ $view->setLimit( 24 );
+ $view->setGraphLimit( 24 );
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformation();
+ $view->enableShowGoals();
+
+ return $this->renderView($view, $fetch);
+ }
+
+ public function getVisitInformationPerLocalTime( $fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory( 'graphVerticalBar');
+ $view->init( $this->pluginName, __FUNCTION__, "VisitTime.getVisitInformationPerLocalTime" );
+ $view->setColumnTranslation('label', Piwik_Translate('VisitTime_ColumnLocalTime'));
+ $view->setColumnsToDisplay( array('label','nb_visits') );
+ $view->setSortedColumn( 'label', 'asc' );
+ $view->setLimit( 24 );
+ $view->setGraphLimit( 24 );
+ $view->disableSearchBox();
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformation();
+
+ return $this->renderView($view, $fetch);
+ }
+}
diff --git a/oss/piwik/trunk/plugins/VisitTime/VisitTime.php b/oss/piwik/trunk/plugins/VisitTime/VisitTime.php
new file mode 100644
index 00000000..f059a9a0
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitTime/VisitTime.php
@@ -0,0 +1,147 @@
+ Piwik_Translate('VisitTime_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ 'Goals.getAvailableGoalSegments' => 'addGoalSegments',
+ );
+ return $hooks;
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget( 'VisitTime_SubmenuTimes', 'VisitTime_WidgetLocalTime', 'VisitTime', 'getVisitInformationPerLocalTime');
+ Piwik_AddWidget( 'VisitTime_SubmenuTimes', 'VisitTime_WidgetServerTime', 'VisitTime', 'getVisitInformationPerServerTime');
+ }
+
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'VisitTime_SubmenuTimes', array('module' => 'VisitTime', 'action' => 'index'));
+ }
+
+ function addGoalSegments( $notification )
+ {
+ $segments =& $notification->getNotificationObject();
+ $segments[] = array(
+ 'group' => Piwik_Translate('VisitTime_ColumnServerTime'),
+ 'name' => Piwik_Translate('VisitTime_ColumnServerTime'),
+ 'module' => 'VisitTime',
+ 'action' => 'getVisitInformationPerServerTime',
+ );
+ }
+
+ function archivePeriod( $notification )
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+ $dataTableToSum = array(
+ 'VisitTime_localTime',
+ 'VisitTime_serverTime',
+ );
+ $archiveProcessing->archiveDataTable($dataTableToSum);
+ }
+
+ public function archiveDay( $notification )
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+ $this->archiveDayAggregateVisits($archiveProcessing);
+ $this->archiveDayAggregateGoals($archiveProcessing);
+ $this->archiveDayRecordInDatabase($archiveProcessing);
+ }
+
+ protected function archiveDayAggregateVisits($archiveProcessing)
+ {
+ $labelSQL = "HOUR(visitor_localtime)";
+ $this->interestByLocalTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+
+ $labelSQL = "HOUR(visit_last_action_time)";
+ $this->interestByServerTime = $archiveProcessing->getArrayInterestForLabel($labelSQL);
+ $this->interestByServerTime = $this->convertServerTimeToLocalTimezone($this->interestByServerTime, $archiveProcessing);
+ }
+
+ protected function convertServerTimeToLocalTimezone($interestByServerTime, $archiveProcessing)
+ {
+ $date = Piwik_Date::factory($archiveProcessing->getStartDatetimeUTC())->toString();
+ $timezone = $archiveProcessing->site->getTimezone();
+ $visitsByHourTz = array();
+ foreach($interestByServerTime as $hour => $stats)
+ {
+ $datetime = $date . ' '.$hour.':00:00';
+ $hourInTz = (int)Piwik_Date::factory($datetime, $timezone)->toString('H');
+ $visitsByHourTz[$hourInTz] = $stats;
+ }
+ return $visitsByHourTz;
+ }
+
+ protected function archiveDayAggregateGoals($archiveProcessing)
+ {
+ $query = $archiveProcessing->queryConversionsBySingleSegment("HOUR(server_time)");
+ $goalByServerTime = array();
+ while($row = $query->fetch())
+ {
+ $goalByServerTime[$row['label']][$row['idgoal']] = $archiveProcessing->getGoalRowFromQueryRow($row);
+ }
+ $goalByServerTime = $this->convertServerTimeToLocalTimezone($goalByServerTime, $archiveProcessing);
+ foreach($goalByServerTime as $hour => $goals)
+ {
+ $this->interestByServerTime[$hour][Piwik_Archive::INDEX_GOALS] = $goals;
+ }
+ $archiveProcessing->enrichConversionsByLabelArray($this->interestByServerTime);
+ }
+
+ protected function archiveDayRecordInDatabase($archiveProcessing)
+ {
+ $tableLocalTime = $archiveProcessing->getDataTableFromArray($this->interestByLocalTime);
+ $this->makeSureAllHoursAreSet($tableLocalTime, $archiveProcessing);
+ $archiveProcessing->insertBlobRecord('VisitTime_localTime', $tableLocalTime->getSerialized());
+ destroy($tableLocalTime);
+
+ $tableServerTime = $archiveProcessing->getDataTableFromArray($this->interestByServerTime);
+ $this->makeSureAllHoursAreSet($tableServerTime, $archiveProcessing);
+ $archiveProcessing->insertBlobRecord('VisitTime_serverTime', $tableServerTime->getSerialized());
+ destroy($tableServerTime);
+ }
+
+ private function makeSureAllHoursAreSet($table, $archiveProcessing)
+ {
+ for($i=0; $i<=23; $i++)
+ {
+ if($table->getRowFromLabel($i) === false)
+ {
+ $row = $archiveProcessing->getNewInterestRowLabeled($i);
+ $table->addRow( $row );
+ }
+ }
+ }
+}
+
diff --git a/oss/piwik/trunk/plugins/VisitTime/templates/index.tpl b/oss/piwik/trunk/plugins/VisitTime/templates/index.tpl
new file mode 100644
index 00000000..82af946c
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitTime/templates/index.tpl
@@ -0,0 +1,9 @@
+
+
{'VisitTime_LocalTime'|translate}
+{$dataTableVisitInformationPerLocalTime}
+
+
+
+
{'VisitTime_ServerTime'|translate}
+{$dataTableVisitInformationPerServerTime}
+
diff --git a/oss/piwik/trunk/plugins/VisitorInterest/API.php b/oss/piwik/trunk/plugins/VisitorInterest/API.php
new file mode 100644
index 00000000..9abae6e8
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitorInterest/API.php
@@ -0,0 +1,109 @@
+getDataTable($name);
+ $dataTable->filter('Sort',array(Piwik_Archive::INDEX_NB_VISITS));
+ $dataTable->queueFilter('ReplaceColumnNames');
+ $dataTable->queueFilter('Sort', array('label', 'asc', true));
+ return $dataTable;
+ }
+
+ public function getNumberOfVisitsPerVisitDuration( $idSite, $period, $date )
+ {
+ $dataTable = $this->getDataTable('VisitorInterest_timeGap', $idSite, $period, $date);
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getDurationLabel'));
+ return $dataTable;
+ }
+
+ public function getNumberOfVisitsPerPage( $idSite, $period, $date )
+ {
+ $dataTable = $this->getDataTable('VisitorInterest_pageGap', $idSite, $period, $date);
+ $dataTable->queueFilter('ColumnCallbackReplace', array('label', 'Piwik_getPageGapLabel'));
+ return $dataTable;
+ }
+}
+
+function Piwik_getDurationLabel($label)
+{
+ if(($pos = strpos($label,'-')) !== false)
+ {
+ $min = substr($label, 0, $pos);
+ $max = substr($label, $pos+1);
+
+ if($min == 0 || $min == 30)
+ {
+ $XYSeconds = Piwik_Translate('VisitorInterest_BetweenXYSeconds');
+ return sprintf($XYSeconds, $min, $max);
+ }
+ else
+ {
+ $min = $min / 60;
+ $max = $max / 60;
+ $XYMin = Piwik_Translate('VisitorInterest_BetweenXYMinutes');
+ return sprintf($XYMin, $min, $max);
+ }
+ }
+ if(!is_numeric($label))
+ {
+ return $label;
+ }
+ $time = intval($label) / 60;
+ $plusXMin = Piwik_Translate('VisitorInterest_PlusXMin');
+ return sprintf($plusXMin, $time . urlencode('+'));
+}
+
+function Piwik_getPageGapLabel($label)
+{
+ $return = false;
+ if(($pos = strpos($label,'-')) !== false)
+ {
+ $min = substr($label, 0, $pos);
+ $max = substr($label, $pos+1);
+
+ if($min == $max)
+ {
+ $return = $min;
+ }
+ }
+ if(!$return)
+ {
+ $return = $label;
+ }
+
+ if($return == 1)
+ {
+ return Piwik_Translate('VisitorInterest_OnePage');
+ }
+
+ return sprintf(Piwik_Translate('VisitorInterest_NPages'), $return);
+}
diff --git a/oss/piwik/trunk/plugins/VisitorInterest/Controller.php b/oss/piwik/trunk/plugins/VisitorInterest/Controller.php
new file mode 100644
index 00000000..d5fb8eb1
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitorInterest/Controller.php
@@ -0,0 +1,58 @@
+dataTableNumberOfVisitsPerVisitDuration = $this->getNumberOfVisitsPerVisitDuration(true);
+ $view->dataTableNumberOfVisitsPerPage = $this->getNumberOfVisitsPerPage(true);
+ echo $view->render();
+ }
+
+ function getNumberOfVisitsPerVisitDuration( $fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory( 'cloud' );
+ $view->init( $this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsPerVisitDuration" );
+
+ $view->setColumnsToDisplay( array('label','nb_visits') );
+ $view->setSortedColumn( 'label', 'asc' );
+ $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_ColumnVisitDuration'));
+ $view->disableSort();
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformation();
+ $view->disableSearchBox();
+ $view->disableShowAllColumns();
+
+ return $this->renderView($view, $fetch);
+ }
+
+ function getNumberOfVisitsPerPage( $fetch = false)
+ {
+ $view = Piwik_ViewDataTable::factory( 'cloud' );
+ $view->init( $this->pluginName, __FUNCTION__, "VisitorInterest.getNumberOfVisitsPerPage" );
+ $view->setColumnsToDisplay( array('label','nb_visits') );
+ $view->setSortedColumn( 'nb_visits', 'asc' );
+ $view->setColumnTranslation('label', Piwik_Translate('VisitorInterest_ColumnPagesPerVisit'));
+ $view->disableExcludeLowPopulation();
+ $view->disableOffsetInformation();
+ $view->disableSearchBox();
+ $view->disableSort();
+ $view->disableShowAllColumns();
+
+ return $this->renderView($view, $fetch);
+ }
+}
diff --git a/oss/piwik/trunk/plugins/VisitorInterest/VisitorInterest.php b/oss/piwik/trunk/plugins/VisitorInterest/VisitorInterest.php
new file mode 100644
index 00000000..13baab42
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitorInterest/VisitorInterest.php
@@ -0,0 +1,177 @@
+ Piwik_Translate('VisitorInterest_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ $hooks = array(
+ 'ArchiveProcessing_Day.compute' => 'archiveDay',
+ 'ArchiveProcessing_Period.compute' => 'archivePeriod',
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ );
+ return $hooks;
+ }
+
+ protected $timeGap = array(
+ array(0, 0.5),
+ array(0.5, 1),
+ array(1, 2),
+ array(2, 4),
+ array(4, 6),
+ array(6, 8),
+ array(8, 11),
+ array(11, 15),
+ array(15)
+ );
+
+ protected $pageGap = array(
+ array(1, 1),
+ array(2, 2),
+ array(3, 3),
+ array(4, 4),
+ array(5, 5),
+ array(6, 7),
+ array(8, 10),
+ array(11, 14),
+ array(15, 20),
+ array(20)
+ );
+
+ function addWidgets()
+ {
+ Piwik_AddWidget( 'General_Visitors', 'VisitorInterest_WidgetLengths', 'VisitorInterest', 'getNumberOfVisitsPerVisitDuration');
+ Piwik_AddWidget( 'General_Visitors', 'VisitorInterest_WidgetPages', 'VisitorInterest', 'getNumberOfVisitsPerPage');
+ }
+
+ function addMenu()
+ {
+ Piwik_RenameMenuEntry('General_Visitors', 'VisitFrequency_SubmenuFrequency',
+ 'General_Visitors', 'VisitorInterest_Engagement' );
+ }
+
+ function postLoad()
+ {
+ Piwik_AddAction('template_headerVisitsFrequency', array('Piwik_VisitorInterest','headerVisitsFrequency'));
+ Piwik_AddAction('template_footerVisitsFrequency', array('Piwik_VisitorInterest','footerVisitsFrequency'));
+ }
+
+
+ function archivePeriod( $notification )
+ {
+ $archiveProcessing = $notification->getNotificationObject();
+
+ $dataTableToSum = array(
+ 'VisitorInterest_timeGap',
+ 'VisitorInterest_pageGap',
+ );
+ $archiveProcessing->archiveDataTable($dataTableToSum);
+ }
+
+ public function archiveDay( $notification )
+ {
+ $this->archiveProcessing = $notification->getNotificationObject();
+
+ $recordName = 'VisitorInterest_timeGap';
+ $tableTimegap = $this->getTableTimeGap();
+ $this->archiveProcessing->insertBlobRecord($recordName, $tableTimegap->getSerialized());
+
+ $recordName = 'VisitorInterest_pageGap';
+ $tablePagegap = $this->getTablePageGap();
+ $this->archiveProcessing->insertBlobRecord($recordName, $tablePagegap->getSerialized());
+ }
+
+ protected function getTablePageGap()
+ {
+ $select = array();
+ foreach($this->pageGap as $gap)
+ {
+ if(count($gap) == 2)
+ {
+ $minGap = $gap[0];
+ $maxGap = $gap[1];
+ $gapName = "'$minGap-$maxGap'";
+ $select[] = "sum(case when visit_total_actions between $minGap and $maxGap then 1 else 0 end) as $gapName ";
+ }
+ else
+ {
+ $minGap = $gap[0];
+ $plusEncoded = urlencode('+');
+ $gapName = "'".$minGap.$plusEncoded."'";
+ $select[] = "sum(case when visit_total_actions > $minGap then 1 else 0 end) as $gapName ";
+ }
+ }
+ $toSelect = implode(" , ", $select);
+
+ return $this->archiveProcessing->getSimpleDataTableFromSelect($toSelect, Piwik_Archive::INDEX_NB_VISITS);
+ }
+
+ protected function getTableTimeGap()
+ {
+ $select = array();
+ foreach($this->timeGap as $gap)
+ {
+ if(count($gap) == 2)
+ {
+ $minGap = $gap[0] * 60;
+ $maxGap = $gap[1] * 60;
+
+ $gapName = "'".$minGap."-".$maxGap."'";
+ $select[] = "sum(case when visit_total_time between $minGap and $maxGap then 1 else 0 end) as $gapName ";
+ }
+ else
+ {
+ $minGap = $gap[0] * 60;
+ $gapName = "'$minGap'";
+ $select[] = "sum(case when visit_total_time > $minGap then 1 else 0 end) as $gapName ";
+ }
+ }
+ $toSelect = implode(" , ", $select);
+
+ $table = $this->archiveProcessing->getSimpleDataTableFromSelect($toSelect, Piwik_Archive::INDEX_NB_VISITS);
+ return $table;
+ }
+
+ static public function headerVisitsFrequency($notification)
+ {
+ $out =& $notification->getNotificationObject();
+ $out = '';
+ }
+
+ static public function footerVisitsFrequency($notification)
+ {
+ $out =& $notification->getNotificationObject();
+ $out = '
+
+ ';
+ $out .= Piwik_FrontController::getInstance()->fetchDispatch('VisitorInterest','index');
+ $out .= '
';
+ }
+}
+
diff --git a/oss/piwik/trunk/plugins/VisitorInterest/templates/index.tpl b/oss/piwik/trunk/plugins/VisitorInterest/templates/index.tpl
new file mode 100644
index 00000000..f3855a44
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitorInterest/templates/index.tpl
@@ -0,0 +1,7 @@
+
+{'VisitorInterest_VisitsPerDuration'|translate}
+{$dataTableNumberOfVisitsPerVisitDuration}
+
+{'VisitorInterest_VisitsPerNbOfPages'|translate}
+{$dataTableNumberOfVisitsPerPage}
+
diff --git a/oss/piwik/trunk/plugins/VisitsSummary/API.php b/oss/piwik/trunk/plugins/VisitsSummary/API.php
new file mode 100644
index 00000000..99b1e612
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitsSummary/API.php
@@ -0,0 +1,111 @@
+getDataTableFromNumeric($toFetch);
+ if($bounceRateRequested !== false)
+ {
+ $dataTable->filter('ColumnCallbackAddColumnPercentage', array('bounce_rate', 'bounce_count', 'nb_visits', 0));
+ $dataTable->deleteColumns($toFetch);
+ }
+ return $dataTable;
+ }
+
+ protected function getNumeric( $idSite, $period, $date, $toFetch )
+ {
+ Piwik::checkUserHasViewAccess( $idSite );
+ $archive = Piwik_Archive::build($idSite, $period, $date );
+ $dataTable = $archive->getNumeric($toFetch);
+ return $dataTable;
+ }
+
+ public function getVisits( $idSite, $period, $date )
+ {
+ return $this->getNumeric( $idSite, $period, $date, 'nb_visits');
+ }
+
+ public function getUniqueVisitors( $idSite, $period, $date )
+ {
+ return $this->getNumeric( $idSite, $period, $date, 'nb_uniq_visitors');
+ }
+
+ public function getActions( $idSite, $period, $date )
+ {
+ return $this->getNumeric( $idSite, $period, $date, 'nb_actions');
+ }
+
+ public function getMaxActions( $idSite, $period, $date )
+ {
+ return $this->getNumeric( $idSite, $period, $date, 'max_actions');
+ }
+
+ public function getBounceCount( $idSite, $period, $date )
+ {
+ return $this->getNumeric( $idSite, $period, $date, 'bounce_count');
+ }
+
+ public function getVisitsConverted( $idSite, $period, $date )
+ {
+ return $this->getNumeric( $idSite, $period, $date, 'nb_visits_converted');
+ }
+
+ public function getSumVisitsLength( $idSite, $period, $date )
+ {
+ return $this->getNumeric( $idSite, $period, $date, 'sum_visit_length');
+ }
+
+ public function getSumVisitsLengthPretty( $idSite, $period, $date )
+ {
+ return Piwik::getPrettyTimeFromSeconds($this->getSumVisitsLength( $idSite, $period, $date ));
+ }
+}
diff --git a/oss/piwik/trunk/plugins/VisitsSummary/Controller.php b/oss/piwik/trunk/plugins/VisitsSummary/Controller.php
new file mode 100644
index 00000000..1c480cf1
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitsSummary/Controller.php
@@ -0,0 +1,89 @@
+setPeriodVariablesView($view);
+ $view->graphEvolutionVisitsSummary = $this->getEvolutionGraph( true, array('nb_visits') );
+ $this->setSparklinesAndNumbers($view);
+ echo $view->render();
+ }
+
+ public function getSparklines()
+ {
+ $view = Piwik_View::factory('sparklines');
+ $this->setSparklinesAndNumbers($view);
+ echo $view->render();
+ }
+
+ public function getEvolutionGraph( $fetch = false, $columns = false)
+ {
+ $view = $this->getLastUnitGraph($this->pluginName, __FUNCTION__, "VisitsSummary.get");
+ if(empty($columns))
+ {
+ $columns = Piwik_Common::getRequestVar('columns');
+ }
+ $view->setColumnsToDisplay($columns);
+ return $this->renderView($view, $fetch);
+ }
+
+ static public function getVisitsSummary()
+ {
+ $requestString = "method=VisitsSummary.get".
+ "&format=original".
+ // we disable filters for example "search for pattern", in the case this method is called
+ // by a method that already calls the API with some generic filters applied
+ "&disable_generic_filters=1";
+ $request = new Piwik_API_Request($requestString);
+ return $request->process();
+ }
+
+ static public function getVisits()
+ {
+ $requestString = "method=VisitsSummary.getVisits".
+ "&format=original".
+ "&disable_generic_filters=1";
+ $request = new Piwik_API_Request($requestString);
+ return $request->process();
+ }
+
+ protected function setSparklinesAndNumbers($view)
+ {
+ $view->urlSparklineNbVisits = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_visits')));
+ $view->urlSparklineNbActions = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_actions')));
+ $view->urlSparklineSumVisitLength = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('sum_visit_length')));
+ $view->urlSparklineMaxActions = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('max_actions')));
+ $view->urlSparklineBounceRate = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('bounce_rate')));
+
+ $dataTableVisit = self::getVisitsSummary();
+ $dataRow = $dataTableVisit->getFirstRow();
+ if($view->period != 'year')
+ {
+ $view->urlSparklineNbUniqVisitors = $this->getUrlSparkline( 'getEvolutionGraph', array('columns' => array('nb_uniq_visitors')));
+ $view->nbUniqVisitors = $dataRow->getColumn('nb_uniq_visitors');
+ }
+ $nbVisits = $dataRow->getColumn('nb_visits');
+ $view->nbVisits = $nbVisits;
+ $view->nbActions = $dataRow->getColumn('nb_actions');
+ $view->sumVisitLength = $dataRow->getColumn('sum_visit_length');
+ $nbBouncedVisits = $dataRow->getColumn('bounce_count');
+ $view->bounceRate = Piwik::getPercentageSafe($nbBouncedVisits, $nbVisits);
+ $view->maxActions = $dataRow->getColumn('max_actions');
+ }
+}
diff --git a/oss/piwik/trunk/plugins/VisitsSummary/VisitsSummary.php b/oss/piwik/trunk/plugins/VisitsSummary/VisitsSummary.php
new file mode 100644
index 00000000..4d64386a
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitsSummary/VisitsSummary.php
@@ -0,0 +1,51 @@
+ Piwik_Translate('VisitsSummary_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ return $info;
+ }
+
+ function getListHooksRegistered()
+ {
+ return array(
+ 'WidgetsList.add' => 'addWidgets',
+ 'Menu.add' => 'addMenu',
+ );
+ }
+
+ function addWidgets()
+ {
+ Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetLastVisits', 'VisitsSummary', 'getEvolutionGraph', array('columns' => array('nb_visits')));
+ Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetVisits', 'VisitsSummary', 'getSparklines');
+ Piwik_AddWidget( 'VisitsSummary_VisitsSummary', 'VisitsSummary_WidgetOverviewGraph', 'VisitsSummary', 'index');
+ }
+
+ function addMenu()
+ {
+ Piwik_AddMenu('General_Visitors', 'VisitsSummary_SubmenuOverview', array('module' => 'VisitsSummary', 'action' => 'index'), true);
+ }
+}
+
+
diff --git a/oss/piwik/trunk/plugins/VisitsSummary/templates/index.tpl b/oss/piwik/trunk/plugins/VisitsSummary/templates/index.tpl
new file mode 100644
index 00000000..4cf972d9
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitsSummary/templates/index.tpl
@@ -0,0 +1,14 @@
+
+
+
+{'VisitsSummary_EvolutionOverLastPeriods'|translate:$periodsNames.$period.plural}
+{$graphEvolutionVisitsSummary}
+
+{'VisitsSummary_Report'|translate}
+{include file=VisitsSummary/templates/sparklines.tpl}
+
+
+
+{'VisitsSummary_GenerateTime'|translate:$totalTimeGeneration:$totalNumberOfQueries}
+{if $totalNumberOfQueries != 0}, {'VisitsSummary_GenerateQueries'|translate:$totalNumberOfQueries}{/if}
+
diff --git a/oss/piwik/trunk/plugins/VisitsSummary/templates/sparklines.tpl b/oss/piwik/trunk/plugins/VisitsSummary/templates/sparklines.tpl
new file mode 100644
index 00000000..8dfc3161
--- /dev/null
+++ b/oss/piwik/trunk/plugins/VisitsSummary/templates/sparklines.tpl
@@ -0,0 +1,13 @@
+
+
{sparkline src=$urlSparklineNbVisits} {'VisitsSummary_NbVisits'|translate:"$nbVisits "}
+ {if isset($urlSparklineNbUniqVisitors)}
+
{sparkline src=$urlSparklineNbUniqVisitors} {'VisitsSummary_NbUniqueVisitors'|translate:"$nbUniqVisitors "}
+ {/if}
+
{sparkline src=$urlSparklineNbActions} {'VisitsSummary_NbActionsDescription'|translate:"$nbActions "}
+
+
+
{sparkline src=$urlSparklineSumVisitLength} {assign var=sumtimeVisitLength value=$sumVisitLength|sumtime} {'VisitsSummary_TotalTime'|translate:"$sumtimeVisitLength "}
+
{sparkline src=$urlSparklineMaxActions} {'VisitsSummary_MaxNbActions'|translate:"$maxActions "}
+
{sparkline src=$urlSparklineBounceRate} {'VisitsSummary_NbVisitsBounced'|translate:"$bounceRate% "}
+
+
diff --git a/oss/piwik/trunk/plugins/Widgetize/Controller.php b/oss/piwik/trunk/plugins/Widgetize/Controller.php
new file mode 100644
index 00000000..74011fb8
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/Controller.php
@@ -0,0 +1,69 @@
+availableWidgets = json_encode(Piwik_GetWidgetsList());
+ $this->setGeneralVariablesView($view);
+ echo $view->render();
+ }
+
+ function testJsInclude1()
+ {
+ $view = Piwik_View::factory('test_jsinclude');
+ $view->url1 = '?module=Widgetize&action=js&moduleToWidgetize=UserSettings&actionToWidgetize=getBrowser&idSite=1&period=day&date=yesterday';
+ $view->url2 = '?module=Widgetize&action=js&moduleToWidgetize=API&actionToWidgetize=index&method=ExamplePlugin.getGoldenRatio&format=original';
+ echo $view->render();
+ }
+
+ function testJsInclude2()
+ {
+ $view = Piwik_View::factory('test_jsinclude2');
+ $view->url1 = '?module=Widgetize&action=js&moduleToWidgetize=UserSettings&actionToWidgetize=getBrowser&idSite=1&period=day&date=yesterday';
+ $view->url2 = '?module=Widgetize&action=js&moduleToWidgetize=UserCountry&actionToWidgetize=getCountry&idSite=1&period=day&date=yesterday&viewDataTable=cloud&show_footer=0';
+ $view->url3 = '?module=Widgetize&action=js&moduleToWidgetize=Referers&actionToWidgetize=getKeywords&idSite=1&period=day&date=yesterday&viewDataTable=table&show_footer=0';
+ echo $view->render();
+ }
+
+ function js()
+ {
+ Piwik_API_Request::reloadAuthUsingTokenAuth();
+ $controllerName = Piwik_Common::getRequestVar('moduleToWidgetize');
+ $actionName = Piwik_Common::getRequestVar('actionToWidgetize');
+ $parameters = array ( $fetch = true );
+ $content = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName, $parameters);
+ $view = Piwik_View::factory('js');
+ $view->piwikUrl = Piwik_Url::getCurrentUrlWithoutFileName();
+ $content = str_replace(array("\t","\n","\r\n","\r"), "", $content);
+ $view->content = $content;
+ echo $view->render();
+ }
+
+ function iframe()
+ {
+ Piwik_API_Request::reloadAuthUsingTokenAuth();
+ $controllerName = Piwik_Common::getRequestVar('moduleToWidgetize');
+ $actionName = Piwik_Common::getRequestVar('actionToWidgetize');
+ $parameters = array ( $fetch = true );
+ $outputDataTable = Piwik_FrontController::getInstance()->fetchDispatch( $controllerName, $actionName, $parameters);
+ $view = Piwik_View::factory('iframe');
+ $view->content = $outputDataTable;
+ echo $view->render();
+ }
+}
diff --git a/oss/piwik/trunk/plugins/Widgetize/Widgetize.php b/oss/piwik/trunk/plugins/Widgetize/Widgetize.php
new file mode 100644
index 00000000..9e50ad83
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/Widgetize.php
@@ -0,0 +1,28 @@
+ Piwik_Translate('Widgetize_PluginDescription'),
+ 'author' => 'Piwik',
+ 'author_homepage' => 'http://piwik.org/',
+ 'version' => Piwik_Version::VERSION,
+ );
+ }
+}
diff --git a/oss/piwik/trunk/plugins/Widgetize/templates/iframe.tpl b/oss/piwik/trunk/plugins/Widgetize/templates/iframe.tpl
new file mode 100644
index 00000000..06b28eec
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/templates/iframe.tpl
@@ -0,0 +1,14 @@
+
+
+{loadJavascriptTranslations plugins='CoreHome'}
+{include file="CoreHome/templates/js_global_variables.tpl"}
+{include file="CoreHome/templates/js_css_includes.tpl"}
+
+
+
+
+{$content}
+
+
+
+
diff --git a/oss/piwik/trunk/plugins/Widgetize/templates/index.tpl b/oss/piwik/trunk/plugins/Widgetize/templates/index.tpl
new file mode 100644
index 00000000..3e705fb0
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/templates/index.tpl
@@ -0,0 +1,97 @@
+{assign var=showSitesSelection value=true}
+{assign var=showPeriodSelection value=true}
+{include file="CoreHome/templates/header.tpl"}
+{loadJavascriptTranslations plugins='Dashboard'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{literal}
+
+{/literal}
+
+
+
+
With Piwik, you can export your Web Analytics reports on your blog, website, or intranet dashboard... in one click.
+
› Widget authentication: If you want your widgets to be viewable by everybody, you first have to set the 'view' permissions
+ to the anonymous user in the Users Management section .
+ Alternatively, if you are publishing widgets on a password protected or private page,
+ you don't necessarily have to allow 'anonymous' to view your reports. In this case, you can add the secret token_auth parameter (found in the API page ) in the widget URL.
+
+
› Widgetize the full dashboard: You can also display the full Piwik dashboard in your application or website in an IFRAME (see example ).
+ The date parameter can be set to a specific calendar date, "today", or "yesterday". The period parameter can be set to "day", "week", "month", or "year".
+ The language parameter can be set to the language code of a translation, such as language=fr.
+ For example, for idSite=1 and date=yesterday, you can write:
+
+
› Select a report, and copy paste in your page the embed code below the widget:
+
+
+
+
+
+
+
+
diff --git a/oss/piwik/trunk/plugins/Widgetize/templates/js.tpl b/oss/piwik/trunk/plugins/Widgetize/templates/js.tpl
new file mode 100644
index 00000000..a92c76e6
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/templates/js.tpl
@@ -0,0 +1,19 @@
+{loadJavascriptTranslations disableOutputScriptTag=1 plugins='CoreHome'}
+
+document.write(' ');
+document.write(' ');
+document.write(' ');
+document.write(' ');
+
+document.write(' ');
+document.write(' ');
+document.write(' ');
+document.write(' ');
+document.write(' ');
+document.write(' ');
+document.write(' ');
+document.write(' ');
+
+var content = '{$content|escape:'javascript'}';
+document.write('document.write(content) ');
+
diff --git a/oss/piwik/trunk/plugins/Widgetize/templates/test_jsinclude.tpl b/oss/piwik/trunk/plugins/Widgetize/templates/test_jsinclude.tpl
new file mode 100644
index 00000000..b2416ace
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/templates/test_jsinclude.tpl
@@ -0,0 +1,20 @@
+
+
+This text is after the JS INCLUDE
+
+
+Test tag cloud in a JS include
+
+
+
+This text is after the JS INCLUDE
+
+Test calling the API in Javascript
+using a javascript include. This is a dirty way (much better to ajax call the API!) but an interesting show case.
+
+Powered by Piwik
+This text is after the JS INCLUDE 2
+
+
diff --git a/oss/piwik/trunk/plugins/Widgetize/templates/test_jsinclude2.tpl b/oss/piwik/trunk/plugins/Widgetize/templates/test_jsinclude2.tpl
new file mode 100644
index 00000000..fb5d288e
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/templates/test_jsinclude2.tpl
@@ -0,0 +1,27 @@
+
+
+This text is after the JS INCLUDE
+
+Test table in a JS include
+
+
+Powered by Piwik
+
+This text is after the JS INCLUDE
+
+Test another table in a JS include
+
+
+Powered by Piwik
+
+This text is after the 2nd JS INCLUDE
+
+Test tag cloud in a JS include
+
+
+Powered by Piwik
+
+This text is after the JS INCLUDE
+
+
+
diff --git a/oss/piwik/trunk/plugins/Widgetize/templates/widgetize.js b/oss/piwik/trunk/plugins/Widgetize/templates/widgetize.js
new file mode 100644
index 00000000..5f8a3c36
--- /dev/null
+++ b/oss/piwik/trunk/plugins/Widgetize/templates/widgetize.js
@@ -0,0 +1,102 @@
+function widgetize()
+{
+ var self = this;
+
+ this.getInputFormWithHtml = function(inputId, htmlEmbed)
+ {
+ return ' ';
+ }
+
+ this.getEmbedUrl = function( parameters, exportFormat )
+ {
+ copyParameters = {};
+ for(var variableName in parameters) {
+ copyParameters[variableName] = parameters[variableName];
+ }
+ copyParameters['moduleToWidgetize'] = parameters['module'];
+ copyParameters['actionToWidgetize'] = parameters['action'];
+ delete copyParameters['action'];
+ delete copyParameters['module'];
+ var sourceUrl;
+ sourceUrl = document.location.protocol + '//' + document.location.hostname + document.location.pathname + '?';
+ sourceUrl += "module=Widgetize" +
+ "&action="+exportFormat+
+ "&"+piwikHelper.getQueryStringFromParameters(copyParameters)+
+ "&idSite="+piwik.idSite+
+ "&period="+piwik.period+
+ "&date="+piwik.currentDateString+
+ "&disableLink=1";
+ return sourceUrl;
+ }
+
+ this.deleteEmbedElements = function()
+ {
+ $('#exportButtons').remove();
+ }
+
+ this.htmlentities = function(s)
+ {
+ return s.replace( /[<>&]/g, function(m) { return "&" + m.charCodeAt(0) + ";"; });
+ }
+
+ this.callbackAddExportButtonsUnderWidget = function ( widgetUniqueId,
+ loadedWidgetElement)
+ {
+ widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
+ widgetName = widget["name"];
+ widgetParameters = widget['parameters'];
+
+ self.deleteEmbedElements();
+ var exportButtonsElement = $('');
+
+ // We first build the HTML code that will load the widget in an IFRAME
+ var widgetIframeHtml = ''+
+ ''+
+ '
';
+
+ // Add the input field containing the widget in an Iframe
+ $(exportButtonsElement).append(
+ ''+
+ '› Embed Iframe '+
+ ''+
+ self.getInputFormWithHtml('iframeEmbed', widgetIframeHtml)+
+ ' '+
+ '
'
+ );
+
+ // Add the Flash Export if a flash is found in the widget
+ $(loadedWidgetElement)
+ .find('embed,object')
+ .each(function() {
+ var htmlEmbed = $(this).parent().html();
+
+ htmlEmbed = htmlEmbed.replace(/ (data=")/, ' $1' + unescape(piwik.piwik_url));
+ htmlEmbed = htmlEmbed.replace(/ (value=")x-(data-file=)/, ' $1$2' + piwik.piwik_url + 'index.php');
+
+ $(exportButtonsElement).append(
+ ''+
+ '› Embed Flash '+
+ ''+
+ self.getInputFormWithHtml('flashEmbed', htmlEmbed) +
+ ' '+
+ '
'
+ );
+ });
+
+ // We then replace the div iframeDivToExport with the actual Iframe html
+ $('#iframeDivToExport')
+ .html(widgetIframeHtml);
+
+ // Finally we append the content to the parent widget DIV
+ $(loadedWidgetElement)
+ .parent()
+ .append(exportButtonsElement);
+
+ // JS is buggy at least on IE
+ //var widgetJS = '