Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <thomas.steur@gmail.com>2015-07-03 03:54:27 +0300
committersgiehl <stefan@piwik.org>2015-10-06 18:25:13 +0300
commit9ba8f216fd7856ce5fef06bf82ecb8f8a2e7e630 (patch)
tree6ce07d18a85d00b39ab720abe042361c0775aead /plugins/CoreHome
parent8ccc9dc05da021325cdbf141a548637fa52f16b2 (diff)
generate pages instead of implementing them in each controller
Diffstat (limited to 'plugins/CoreHome')
-rw-r--r--plugins/CoreHome/Categories/ActionsCategory.php17
-rw-r--r--plugins/CoreHome/Categories/DevicesSubcategory.php19
-rw-r--r--plugins/CoreHome/Categories/EngagementSubcategory.php19
-rw-r--r--plugins/CoreHome/Categories/SoftwareSubcategory.php19
-rw-r--r--plugins/CoreHome/Categories/VisitorsCategory.php17
-rw-r--r--plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php19
-rw-r--r--plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php2
-rw-r--r--plugins/CoreHome/Controller.php35
-rw-r--r--plugins/CoreHome/CoreHome.php32
-rw-r--r--plugins/CoreHome/Widgets.php63
-rw-r--r--plugins/CoreHome/Widgets/GetDonateForm.php48
-rw-r--r--plugins/CoreHome/Widgets/GetPromoVideo.php44
-rw-r--r--plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js31
-rw-r--r--plugins/CoreHome/angularjs/activity-indicator/activityindicator.html3
-rw-r--r--plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js4
-rw-r--r--plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js8
-rw-r--r--plugins/CoreHome/angularjs/common/filters/escape.js16
-rw-r--r--plugins/CoreHome/angularjs/common/services/global-ajax-queue.js14
-rw-r--r--plugins/CoreHome/angularjs/common/services/piwik-url.js54
-rw-r--r--plugins/CoreHome/angularjs/common/services/report-metadata-model.js52
-rw-r--r--plugins/CoreHome/angularjs/common/services/reporting-pages-model.js58
-rw-r--r--plugins/CoreHome/angularjs/history/history.service.js21
-rw-r--r--plugins/CoreHome/angularjs/http404check.js6
-rw-r--r--plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html2
-rw-r--r--plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js13
-rw-r--r--plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js74
-rw-r--r--plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js153
-rw-r--r--plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js125
-rw-r--r--plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html40
-rw-r--r--plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js31
-rw-r--r--plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js196
-rw-r--r--plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js56
-rw-r--r--plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html25
-rw-r--r--plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js31
-rw-r--r--plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html25
-rw-r--r--plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js67
-rw-r--r--plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html10
-rw-r--r--plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js32
-rw-r--r--plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html13
-rw-r--r--plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js134
-rw-r--r--plugins/CoreHome/angularjs/widget/widget.directive.html23
-rw-r--r--plugins/CoreHome/angularjs/widget/widget.directive.js93
-rw-r--r--plugins/CoreHome/javascripts/broadcast.js67
-rwxr-xr-xplugins/CoreHome/javascripts/corehome.js54
-rw-r--r--plugins/CoreHome/javascripts/dataTable.js1
-rw-r--r--plugins/CoreHome/javascripts/menu.js114
-rw-r--r--plugins/CoreHome/javascripts/menu_init.js19
-rw-r--r--plugins/CoreHome/javascripts/sparkline.js28
-rw-r--r--plugins/CoreHome/lang/en.json3
-rw-r--r--plugins/CoreHome/stylesheets/coreHome.less6
-rw-r--r--plugins/CoreHome/stylesheets/zen-mode.less16
-rw-r--r--plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig29
-rw-r--r--plugins/CoreHome/templates/_indexContent.twig3
-rw-r--r--plugins/CoreHome/templates/getDefaultIndexView.twig5
-rwxr-xr-xplugins/CoreHome/templates/widgetContainer.twig18
55 files changed, 1757 insertions, 350 deletions
diff --git a/plugins/CoreHome/Categories/ActionsCategory.php b/plugins/CoreHome/Categories/ActionsCategory.php
new file mode 100644
index 0000000000..e995df110d
--- /dev/null
+++ b/plugins/CoreHome/Categories/ActionsCategory.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Categories;
+
+use Piwik\Category\Category;
+
+class ActionsCategory extends Category
+{
+ protected $id = 'General_Actions';
+ protected $order = 10;
+}
diff --git a/plugins/CoreHome/Categories/DevicesSubcategory.php b/plugins/CoreHome/Categories/DevicesSubcategory.php
new file mode 100644
index 0000000000..9d68e5b30c
--- /dev/null
+++ b/plugins/CoreHome/Categories/DevicesSubcategory.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Categories;
+
+use Piwik\Category\Subcategory;
+
+class DevicesSubcategory extends Subcategory
+{
+ protected $categoryId = 'General_Visitors';
+ protected $id = 'DevicesDetection_Devices';
+ protected $order = 15;
+
+}
diff --git a/plugins/CoreHome/Categories/EngagementSubcategory.php b/plugins/CoreHome/Categories/EngagementSubcategory.php
new file mode 100644
index 0000000000..1a4f4b1d40
--- /dev/null
+++ b/plugins/CoreHome/Categories/EngagementSubcategory.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Categories;
+
+use Piwik\Category\Subcategory;
+
+class EngagementSubcategory extends Subcategory
+{
+ protected $categoryId = 'General_Visitors';
+ protected $id = 'VisitorInterest_Engagement';
+ protected $order = 30;
+
+}
diff --git a/plugins/CoreHome/Categories/SoftwareSubcategory.php b/plugins/CoreHome/Categories/SoftwareSubcategory.php
new file mode 100644
index 0000000000..34188afe97
--- /dev/null
+++ b/plugins/CoreHome/Categories/SoftwareSubcategory.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Categories;
+
+use Piwik\Category\Subcategory;
+
+class SoftwareSubcategory extends Subcategory
+{
+ protected $categoryId = 'General_Visitors';
+ protected $id = 'DevicesDetection_Software';
+ protected $order = 20;
+
+}
diff --git a/plugins/CoreHome/Categories/VisitorsCategory.php b/plugins/CoreHome/Categories/VisitorsCategory.php
new file mode 100644
index 0000000000..c4604c4035
--- /dev/null
+++ b/plugins/CoreHome/Categories/VisitorsCategory.php
@@ -0,0 +1,17 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Categories;
+
+use Piwik\Category\Category;
+
+class VisitorsCategory extends Category
+{
+ protected $id = 'General_Visitors';
+ protected $order = 5;
+}
diff --git a/plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php b/plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php
new file mode 100644
index 0000000000..3db3e260d2
--- /dev/null
+++ b/plugins/CoreHome/Categories/VisitorsOverviewSubcategory.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Categories;
+
+use Piwik\Category\Subcategory;
+
+class VisitorsOverviewSubcategory extends Subcategory
+{
+ protected $categoryId = 'General_Visitors';
+ protected $id = 'General_Overview';
+ protected $order = 2;
+
+}
diff --git a/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php b/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php
index e590e19871..b56364aa7f 100644
--- a/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php
+++ b/plugins/CoreHome/Columns/Metrics/AverageTimeOnSite.php
@@ -38,7 +38,7 @@ class AverageTimeOnSite extends ProcessedMetric
public function format($value, Formatter $formatter)
{
- return $formatter->getPrettyTimeFromSeconds($value);
+ return $formatter->getPrettyTimeFromSeconds($value, true);
}
public function getTranslatedName()
diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index a61419e5fb..a0866c0ff2 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -11,12 +11,13 @@ namespace Piwik\Plugins\CoreHome;
use Exception;
use Piwik\API\Request;
use Piwik\Common;
+use Piwik\DataTable\Renderer\Json;
use Piwik\Date;
use Piwik\FrontController;
-use Piwik\Menu\MenuReporting;
use Piwik\Notification\Manager as NotificationManager;
use Piwik\Piwik;
use Piwik\Plugin\Report;
+use Piwik\Widget\Widget;
use Piwik\Plugins\CoreHome\DataTableRowAction\MultiRowEvolution;
use Piwik\Plugins\CoreHome\DataTableRowAction\RowEvolution;
use Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient;
@@ -28,7 +29,6 @@ use Piwik\UpdateCheck;
use Piwik\Url;
use Piwik\View;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
-use Piwik\Plugin\Widgets as PluginWidgets;
class Controller extends \Piwik\Plugin\Controller
{
@@ -49,40 +49,38 @@ class Controller extends \Piwik\Plugin\Controller
return 'redirectToCoreHomeIndex';
}
- public function renderReportMenu(Report $report)
+ public function renderReportWidget(Report $report)
{
Piwik::checkUserHasSomeViewAccess();
$this->checkSitePermission();
$report->checkIsEnabled();
- $menuTitle = $report->getMenuTitle();
-
- if (empty($menuTitle)) {
- throw new Exception('This report is not supposed to be displayed in the menu, please define a $menuTitle in your report.');
- }
-
- $menuTitle = $this->translator->translate($menuTitle);
- $content = $this->renderReportWidget($report);
-
- return View::singleReport($menuTitle, $content);
+ return $report->render();
}
- public function renderReportWidget(Report $report)
+ public function renderWidgetContainer()
{
Piwik::checkUserHasSomeViewAccess();
$this->checkSitePermission();
- $report->checkIsEnabled();
+ $view = new View('@CoreHome/widgetContainer');
+ $view->isWidgetized = (bool) Common::getRequestVar('widget', 0, 'int');
+ $view->containerId = Common::getRequestVar('containerId', null, 'string');
- return $report->render();
+ return $view->render();
}
- public function renderWidget(PluginWidgets $widget, $method)
+ /**
+ * @param Widget $widget
+ * @return mixed
+ * @throws Exception
+ */
+ public function renderWidget($widget)
{
Piwik::checkUserHasSomeViewAccess();
- return $widget->$method();
+ return $widget->render();
}
function redirectToCoreHomeIndex()
@@ -133,7 +131,6 @@ class Controller extends \Piwik\Plugin\Controller
{
$view = new View('@CoreHome/getDefaultIndexView');
$this->setGeneralVariablesView($view);
- $view->menu = MenuReporting::getInstance()->getMenu();
$view->dashboardSettingsControl = new DashboardManagerControl();
$view->content = '';
return $view;
diff --git a/plugins/CoreHome/CoreHome.php b/plugins/CoreHome/CoreHome.php
index 23da8326aa..c8b2b428d0 100644
--- a/plugins/CoreHome/CoreHome.php
+++ b/plugins/CoreHome/CoreHome.php
@@ -14,6 +14,13 @@ namespace Piwik\Plugins\CoreHome;
class CoreHome extends \Piwik\Plugin
{
/**
+ * Defines a widget container layout that will display all widgets within a container inside a "tab" menu
+ * where on the left side a link is shown for each widget and on the right side the selected widget.
+ * @api
+ */
+ const WIDGET_CONTAINER_LAYOUT_BY_DIMENSION = 'ByDimension';
+
+ /**
* @see Piwik\Plugin::registerEvents
*/
public function registerEvents()
@@ -105,8 +112,6 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/javascripts/dataTable_rowactions.js";
$jsFiles[] = "plugins/CoreHome/javascripts/popover.js";
$jsFiles[] = "plugins/CoreHome/javascripts/broadcast.js";
- $jsFiles[] = "plugins/CoreHome/javascripts/menu.js";
- $jsFiles[] = "plugins/CoreHome/javascripts/menu_init.js";
$jsFiles[] = "plugins/CoreHome/javascripts/calendar.js";
$jsFiles[] = "plugins/CoreHome/javascripts/sparkline.js";
$jsFiles[] = "plugins/CoreHome/javascripts/corehome.js";
@@ -120,8 +125,12 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/piwikApp.config.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/service.module.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/global-ajax-queue.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-api.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-url.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/report-metadata-model.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/services/reporting-pages-model.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/filter.module.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/translate.js";
@@ -130,6 +139,7 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/length.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/trim.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/pretty-url.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/common/filters/escape.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.module.js";
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js";
@@ -146,6 +156,8 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/history/history.service.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js";
+
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-model.service.js";
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.controller.js";
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector.directive.js";
@@ -163,6 +175,21 @@ class CoreHome extends \Piwik\Plugin
$jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.controller.js";
$jsFiles[] = "plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/widget/widget.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js";
+
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js";
+ $jsFiles[] = "plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js";
}
public function getClientSideTranslationKeys(&$translationKeys)
@@ -258,5 +285,6 @@ class CoreHome extends \Piwik\Plugin
$translationKeys[] = 'CoreHome_UndoPivotBySubtable';
$translationKeys[] = 'CoreHome_PivotBySubtable';
$translationKeys[] = 'General_LearnMore';
+ $translationKeys[] = 'CoreHome_NoSuchPage';
}
}
diff --git a/plugins/CoreHome/Widgets.php b/plugins/CoreHome/Widgets.php
deleted file mode 100644
index 17d888bd35..0000000000
--- a/plugins/CoreHome/Widgets.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-/**
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- *
- */
-namespace Piwik\Plugins\CoreHome;
-
-use Piwik\Common;
-use Piwik\Piwik;
-use Piwik\Translation\Translator;
-use Piwik\View;
-
-class Widgets extends \Piwik\Plugin\Widgets
-{
- protected $category = 'Example Widgets';
-
- /**
- * @var Translator
- */
- private $translator;
-
- public function __construct(Translator $translator)
- {
- $this->translator = $translator;
- }
-
- protected function init()
- {
- $this->addWidget('CoreHome_SupportPiwik', 'getDonateForm');
- $this->addWidget('Installation_Welcome', 'getPromoVideo');
- }
-
- /**
- * Renders and echo's the in-app donate form w/ slider.
- */
- public function getDonateForm()
- {
- $view = new View('@CoreHome/getDonateForm');
-
- if (Common::getRequestVar('widget', false)
- && Piwik::hasUserSuperUserAccess()) {
- $view->footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess');
- }
-
- return $view->render();
- }
-
- /**
- * Renders and echo's HTML that displays the Piwik promo video.
- */
- public function getPromoVideo()
- {
- $view = new View('@CoreHome/getPromoVideo');
- $view->shareText = $this->translator->translate('CoreHome_SharePiwikShort');
- $view->shareTextLong = $this->translator->translate('CoreHome_SharePiwikLong');
- $view->promoVideoUrl = 'https://www.youtube.com/watch?v=OslfF_EH81g';
-
- return $view->render();
- }
-}
diff --git a/plugins/CoreHome/Widgets/GetDonateForm.php b/plugins/CoreHome/Widgets/GetDonateForm.php
new file mode 100644
index 0000000000..093fe8c3a2
--- /dev/null
+++ b/plugins/CoreHome/Widgets/GetDonateForm.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Widgets;
+
+use Piwik\Common;
+use Piwik\Piwik;
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+use Piwik\Translation\Translator;
+use Piwik\View;
+
+class GetDonateForm extends Widget
+{
+ /**
+ * @var Translator
+ */
+ private $translator;
+
+ public function __construct(Translator $translator)
+ {
+ $this->translator = $translator;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('Example Widgets');
+ $config->setName('CoreHome_SupportPiwik');
+ $config->setOrder(5);
+ }
+
+ public function render()
+ {
+ $view = new View('@CoreHome/getDonateForm');
+
+ if (Common::getRequestVar('widget', false)
+ && Piwik::hasUserSuperUserAccess()) {
+ $view->footerMessage = $this->translator->translate('CoreHome_OnlyForSuperUserAccess');
+ }
+
+ return $view->render();
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/Widgets/GetPromoVideo.php b/plugins/CoreHome/Widgets/GetPromoVideo.php
new file mode 100644
index 0000000000..49636b5e0e
--- /dev/null
+++ b/plugins/CoreHome/Widgets/GetPromoVideo.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\CoreHome\Widgets;
+
+use Piwik\Widget\Widget;
+use Piwik\Widget\WidgetConfig;
+use Piwik\Translation\Translator;
+use Piwik\View;
+
+class GetPromoVideo extends Widget
+{
+ /**
+ * @var Translator
+ */
+ private $translator;
+
+ public function __construct(Translator $translator)
+ {
+ $this->translator = $translator;
+ }
+
+ public static function configure(WidgetConfig $config)
+ {
+ $config->setCategoryId('Example Widgets');
+ $config->setName('Installation_Welcome');
+ $config->setOrder(10);
+ }
+
+ public function render()
+ {
+ $view = new View('@CoreHome/getPromoVideo');
+ $view->shareText = $this->translator->translate('CoreHome_SharePiwikShort');
+ $view->shareTextLong = $this->translator->translate('CoreHome_SharePiwikLong');
+ $view->promoVideoUrl = 'https://www.youtube.com/watch?v=OslfF_EH81g';
+
+ return $view->render();
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
new file mode 100644
index 0000000000..4417cea7ac
--- /dev/null
+++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.directive.js
@@ -0,0 +1,31 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Shows a general loading message while [loading] is set to true.
+ *
+ * @param {Boolean} loading If true, the activity indicator is shown, otherwise the indicator is hidden.
+ *
+ * Example:
+ * <div piwik-activity-indicator loading="true|false"></div>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikActivityIndicator', piwikActivityIndicator);
+
+ piwikActivityIndicator.$inject = ['piwik'];
+
+ function piwikActivityIndicator(piwik){
+ return {
+ restrict: 'A',
+ transclude: true,
+ scope: {
+ loading: '='
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/activity-indicator/activityindicator.html?cb=' + piwik.cacheBuster
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
new file mode 100644
index 0000000000..f6080ae817
--- /dev/null
+++ b/plugins/CoreHome/angularjs/activity-indicator/activityindicator.html
@@ -0,0 +1,3 @@
+<div ng-show="loading" class="loadingPiwik">
+ <img src="plugins/Morpheus/images/loading-blue.gif" alt=""/>{{ 'General_LoadingData'|translate }}
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js b/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js
index 89d572cedf..2eef346ec6 100644
--- a/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js
+++ b/plugins/CoreHome/angularjs/ajax-form/ajax-form.directive.js
@@ -132,7 +132,9 @@
scope.ajaxForm.data[name] = val;
if (!skipScopeApply) {
- scope.$apply();
+ setTimeout(function () {
+ scope.$apply();
+ }, 0);
}
}
};
diff --git a/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js b/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js
index 0617c5df88..ee1a2cf219 100644
--- a/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js
+++ b/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js
@@ -24,13 +24,17 @@
function onClickOutsideElement (event) {
if (element.has(event.target).length === 0) {
- scope.$apply(attr.piwikFocusAnywhereButHere);
+ setTimeout(function () {
+ scope.$apply(attr.piwikFocusAnywhereButHere);
+ }, 0);
}
}
function onEscapeHandler (event) {
if (event.which === 27) {
- scope.$apply(attr.piwikFocusAnywhereButHere);
+ setTimeout(function () {
+ scope.$apply(attr.piwikFocusAnywhereButHere);
+ }, 0);
}
}
diff --git a/plugins/CoreHome/angularjs/common/filters/escape.js b/plugins/CoreHome/angularjs/common/filters/escape.js
new file mode 100644
index 0000000000..382e84b5ac
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/filters/escape.js
@@ -0,0 +1,16 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp.filter').filter('escape', escape);
+
+ function escape() {
+
+ return function(value) {
+ return piwikHelper.escape(piwikHelper.htmlEntities(value));
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js b/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js
new file mode 100644
index 0000000000..f831995810
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/global-ajax-queue.js
@@ -0,0 +1,14 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp.service').service('globalAjaxQueue', ajaxQueue);
+
+ function ajaxQueue() {
+
+ return globalAjaxQueue;
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/piwik-url.js b/plugins/CoreHome/angularjs/common/services/piwik-url.js
new file mode 100644
index 0000000000..f2d6e9bb0c
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/piwik-url.js
@@ -0,0 +1,54 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp.service').service('piwikUrl', piwikUrl);
+
+ piwikUrl.$inject = ['$location', 'piwik'];
+
+ /**
+ * Similar to angulars $location but works around some limitation. Use it if you need to access search params
+ */
+ function piwikUrl($location, piwik) {
+
+ var model = {
+ getSearchParam: getSearchParam
+ }
+
+ return model;
+
+ function getSearchParam(paramName)
+ {
+ if (paramName === 'segment') {
+ var hash = window.location.href.split('#');
+ if (hash && hash[1]) {
+ return piwik.broadcast.getValueFromHash(paramName, hash[1]);
+ }
+
+ return broadcast.getValueFromUrl(paramName);
+ }
+
+ // available in global scope
+ var search = $location.search();
+
+ if (!search[paramName]) {
+ // see https://github.com/angular/angular.js/issues/7239 (issue is resolved but problem still exists)
+ search[paramName] = piwik.broadcast.getValueFromUrl(paramName);
+ }
+
+ if (search[paramName]) {
+ var value = search[paramName];
+
+ if (angular.isArray(search[paramName])) {
+ // use last one. Eg when having period=day&period=year angular would otherwise return ['day', 'year']
+ return value[value.length - 1];
+ }
+
+ return value;
+ }
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/report-metadata-model.js b/plugins/CoreHome/angularjs/common/services/report-metadata-model.js
new file mode 100644
index 0000000000..f158861423
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/report-metadata-model.js
@@ -0,0 +1,52 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp.service').factory('reportMetadataModel', reportMetadataModel);
+
+ reportMetadataModel.$inject = ['piwik', 'piwikApi'];
+
+ function reportMetadataModel (piwik, piwikApi) {
+
+ var reportsPromise = null;
+
+ var model = {
+ reports: [],
+ fetchReportMetadata: fetchReportMetadata,
+ findReport: findReport
+ };
+
+ return model;
+
+ function findReport(module, action)
+ {
+ var found = [];
+
+ angular.forEach(model.reports, function (report) {
+ if (report.module === module && report.action === action) {
+ found = report;
+ }
+ });
+
+ return found;
+ }
+
+ function fetchReportMetadata()
+ {
+ if (!reportsPromise) {
+ reportsPromise = piwikApi.fetch({
+ method: 'API.getReportMetadata',
+ idSites: piwik.idSite || piwik.broadcast.getValueFromUrl('idSite'),
+ }).then(function (response) {
+ model.reports = response;
+ return response;
+ });
+ }
+
+ return reportsPromise;
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js b/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js
new file mode 100644
index 0000000000..1b1e406c95
--- /dev/null
+++ b/plugins/CoreHome/angularjs/common/services/reporting-pages-model.js
@@ -0,0 +1,58 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp.service').factory('reportingPagesModel', reportingPagesModelService);
+
+ reportingPagesModelService.$inject = ['piwikApi'];
+
+ function reportingPagesModelService (piwikApi) {
+ var fetchAllPagesPromise = false;
+
+ // those sites are going to be displayed
+ var model = {
+ pages : [],
+ findPage: findPage,
+ reloadAllPages : reloadAllPages,
+ getAllPages : getAllPages
+ };
+
+ return model;
+
+ function findPage(categoryId, subcategoryId)
+ {
+ var found = null;
+
+ angular.forEach(model.pages, function (page) {
+ if (page &&
+ page.category && page.subcategory &&
+ page.category.id === categoryId && ('' + page.subcategory.id) === subcategoryId) {
+ found = page;
+ }
+ });
+
+ return found;
+ }
+
+ function reloadAllPages()
+ {
+ fetchAllPagesPromise = null;
+ return getAllPages();
+ }
+
+ function getAllPages()
+ {
+ if (!fetchAllPagesPromise) {
+ fetchAllPagesPromise = piwikApi.fetch({method: 'API.getReportPagesMetadata'}).then(function (response) {
+ model.pages = response;
+ return response;
+ });
+ }
+
+ return fetchAllPagesPromise;
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/history/history.service.js b/plugins/CoreHome/angularjs/history/history.service.js
index 2c6486a517..7dadf35ad0 100644
--- a/plugins/CoreHome/angularjs/history/history.service.js
+++ b/plugins/CoreHome/angularjs/history/history.service.js
@@ -36,6 +36,19 @@
loadCurrentPage();
}
+ function cleanHash(hash)
+ {
+ var chars = ['#', '/', '?'];
+ for (var i = 0; i != chars.length; ++i) {
+ var charToRemove = chars[i];
+ if (hash.charAt(0) == charToRemove) {
+ hash = hash.substring(1);
+ }
+ }
+
+ return hash;
+ }
+
// currently, the AJAX content URL is stored in $location.search(), but before it was stored in $location.path().
// this function makes sure URLs like http://piwik.net/?...#/module=Whatever&action=whatever still work.
function changePathToSearch() {
@@ -82,13 +95,7 @@
function load(hash) {
// make sure the hash is just the query parameter values, w/o a starting #, / or ? char. broadcast.pageload & $location.path should get neither
- var chars = ['#', '/', '?'];
- for (var i = 0; i != chars.length; ++i) {
- var charToRemove = chars[i];
- if (hash.charAt(0) == charToRemove) {
- hash = hash.substring(1);
- }
- }
+ hash = cleanHash(hash);
if (location.hash === '#?' + hash) {
loadCurrentPage(); // it would not trigger a location change success event as URL is the same, call it manually
diff --git a/plugins/CoreHome/angularjs/http404check.js b/plugins/CoreHome/angularjs/http404check.js
index f2e2a86edf..5b720c9472 100644
--- a/plugins/CoreHome/angularjs/http404check.js
+++ b/plugins/CoreHome/angularjs/http404check.js
@@ -1,9 +1,9 @@
(function () {
angular.module('piwikApp').factory('http404CheckInterceptor', http404CheckInterceptor);
- http404CheckInterceptor.$inject = ['$q'];
+ http404CheckInterceptor.$inject = ['$q', 'globalAjaxQueue'];
- function http404CheckInterceptor($q) {
+ function http404CheckInterceptor($q, globalAjaxQueue) {
function isClientError(rejection)
{
@@ -15,8 +15,8 @@
}
return {
-
'responseError': function(rejection) {
+
if (rejection &&
isClientError(rejection) &&
rejection.config &&
diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
index d1964d1ac4..14191ac20e 100644
--- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
+++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html
@@ -23,7 +23,7 @@
class="reset"
src="plugins/CoreHome/images/reset_search.png"/>
</div>
- <div ng-transclude>
+ <div ng-transclude ng-click="selectItem($event)">
</div>
</div>
diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js
index 00fbf87d7b..313ef00d6c 100644
--- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js
+++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.js
@@ -34,10 +34,10 @@
templateUrl: 'plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.html?cb=' + piwik.cacheBuster,
link: function(scope, element, attrs) {
- element.find('.item').on('click', function () {
- var $self = angular.element(this);
+ scope.selectItem = function (event) {
+ var $self = angular.element(event.target);
- if ($self.hasClass('disabled') || $self.hasClass('separator')) {
+ if (!$self.hasClass('item') || $self.hasClass('disabled') || $self.hasClass('separator')) {
return;
}
@@ -47,11 +47,14 @@
});
}
scope.$eval('view.showItems = false');
- scope.$apply();
+
+ setTimeout(function () {
+ scope.$apply();
+ }, 0);
element.find('.item').removeClass('active');
$self.addClass('active');
- });
+ };
scope.searchItems = function (searchTerm)
{
diff --git a/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js b/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js
new file mode 100644
index 0000000000..ea15f025b0
--- /dev/null
+++ b/plugins/CoreHome/angularjs/popover-handler/popover-handler.directive.js
@@ -0,0 +1,74 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * When present in the page it listens to a popover URL parameter.
+ *
+ * If present it will try to load the related content in a popover or if the URL is empty it will close an
+ * opened popover.
+ *
+ * Example:
+ * <div piwik-popover-handler></div>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikPopoverHandler', piwikPopoverHandler);
+
+ piwikPopoverHandler.$inject = ['$location', '$rootScope', 'piwik'];
+
+ function piwikPopoverHandler($location, $rootScope, piwik){
+
+ return {
+ restrict: 'A',
+ scope: {},
+ controller: function () {
+
+ function close()
+ {
+ Piwik_Popover.close();
+ }
+
+ function open(popoverParam)
+ {
+ // in case the $ was encoded (e.g. when using copy&paste on urls in some browsers)
+ popoverParam = decodeURIComponent(popoverParam);
+ // revert special encoding from broadcast.propagateNewPopoverParameter()
+ popoverParam = popoverParam.replace(/\$/g, '%');
+ popoverParam = decodeURIComponent(popoverParam);
+
+ var popoverParamParts = popoverParam.split(':');
+ var handlerName = popoverParamParts[0];
+ popoverParamParts.shift();
+ var param = popoverParamParts.join(':');
+ if (typeof piwik.broadcast.popoverHandlers[handlerName] != 'undefined'
+ && !piwik.broadcast.isLoginPage()) {
+ piwik.broadcast.popoverHandlers[handlerName](param);
+ }
+ }
+
+ function openOrClose()
+ {
+ // should be rather done by routing
+ var popoverParam = $location.search().popover;
+ if (popoverParam) {
+ open(popoverParam);
+ } else {
+ close();
+ }
+ }
+
+ $rootScope.$on('$locationChangeSuccess', function () {
+ // should be rather done by routing
+ $(function () {
+ // make sure all popover handles were registered
+ openOrClose();
+ });
+ });
+
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js
new file mode 100644
index 0000000000..adc1b97936
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu-model.js
@@ -0,0 +1,153 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').factory('reportingMenuModel', reportingMenuModelService);
+
+ reportingMenuModelService.$inject = ['$filter', '$q', 'piwikApi', 'reportingPagesModel', '$location'];
+
+ function reportingMenuModelService ($filter, $q, piwikApi, reportingPagesModel, $location) {
+
+ // those sites are going to be displayed
+ var model = {
+ menu: [],
+ selected: [],
+ fetchMenuItems: fetchMenuItems,
+ reloadMenuItems: reloadMenuItems,
+ findSubcategory: findSubcategory
+ };
+
+ return model;
+
+ function isNumeric(text) {
+ return !isNaN(parseFloat(text)) && isFinite(text);
+ }
+
+ function findSubcategory(categoryId, subcategoryId)
+ {
+ var foundCategory = null;
+ var foundSubcategory = null;
+
+ angular.forEach(model.menu, function (category) {
+ if (category.id !== categoryId) {
+ return;
+ }
+ angular.forEach(category.subcategories, function (subcategory) {
+ if (subcategory.id === subcategoryId) {
+ foundCategory = category;
+ foundSubcategory = subcategory;
+ }
+ });
+ });
+
+ return {category: foundCategory, subcategory: foundSubcategory};
+ }
+
+ function buildMenuFromPages(pages)
+ {
+ var menu = [];
+
+ var activeCategory = $location.search().category;
+ var activeSubcategory = $location.search().subcategory;
+
+ var categoriesHandled = {};
+ angular.forEach(pages, function (page, key) {
+ var category = page.category;
+ var categoryId = category.id;
+
+ if (categoriesHandled[categoryId]) {
+ return;
+ }
+
+ categoriesHandled[categoryId] = true;
+
+ if (activeCategory && category.id === activeCategory) {
+ // this doesn't really belong here but placed it here for convenience
+ category.active = true;
+ category.hover = true;
+ }
+
+ category.subcategories = [];
+
+ var goalsGroup = false;
+
+ angular.forEach(pages, function (page, key) {
+ if (page.category.id === categoryId) {
+ var subcategory = page.subcategory;
+
+ if (subcategory.id === activeSubcategory) {
+ subcategory.active = true;
+ }
+
+ if (page.widgets && page.widgets[0] && page.category.id === 'Goals_Goals' && isNumeric(page.subcategory.id)) {
+ // we handle a goal
+ if (!goalsGroup) {
+ goalsGroup = angular.copy(subcategory);
+ goalsGroup.name = $filter('translate')('Goals_ChooseGoal');
+ goalsGroup.isGroup = true;
+ goalsGroup.subcategories = [];
+ goalsGroup.order = 10;
+ }
+
+ if (subcategory.active) {
+ goalsGroup.name = subcategory.name;
+ }
+
+ var goalId = page.subcategory.id;
+ subcategory.tooltip = subcategory.name + ' (id = ' + goalId + ' )';
+
+ goalsGroup.subcategories.push(subcategory);
+ return;
+ }
+
+ category.subcategories.push(subcategory);
+ }
+ });
+
+ if (goalsGroup && goalsGroup.subcategories && goalsGroup.subcategories.length <= 3) {
+ angular.forEach(goalsGroup.subcategories, function (subcategory) {
+ category.subcategories.push(subcategory);
+ });
+ } else if(goalsGroup) {
+ category.subcategories.push(goalsGroup);
+ }
+
+ category.subcategories = sortMenuItems(category.subcategories);
+
+ menu.push(category);
+
+ return menu;
+ });
+
+ menu = sortMenuItems(menu);
+
+ return menu;
+ }
+
+ function sortMenuItems(menu) {
+ return $filter('orderBy')(menu, 'order');
+ };
+
+ function reloadMenuItems()
+ {
+ var pagesPromise = reportingPagesModel.reloadAllPages();
+ return pagesPromise.then(function (pages) {
+ model.menu = buildMenuFromPages(pages);
+ });
+ }
+
+ function fetchMenuItems()
+ {
+ var pagesPromise = reportingPagesModel.getAllPages();
+
+ return pagesPromise.then(function (pages) {
+ model.menu = buildMenuFromPages(pages);
+
+ return model.menu;
+ });
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
new file mode 100644
index 0000000000..85dc0f17c5
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.controller.js
@@ -0,0 +1,125 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ReportingMenuController', ReportingMenuController);
+
+ ReportingMenuController.$inject = ['$scope', 'piwik', '$location', '$timeout', 'reportingMenuModel', '$rootScope'];
+
+ function ReportingMenuController($scope, piwik, $location, $timeout, menuModel, $rootScope) {
+
+ function markAllCategoriesAsInactive()
+ {
+ angular.forEach(menuModel.menu, function (cat) {
+ cat.active = false;
+ cat.hover = false;
+ angular.forEach(cat.subcategories, function (subcat) {
+ subcat.active = false;
+ });
+ });
+ }
+
+ function getUrlParam(param)
+ {
+ var value = piwik.broadcast.getValueFromHash(param);
+ if (!value) {
+ value = piwik.broadcast.getValueFromUrl(param);
+ }
+ return value;
+ }
+
+ $scope.menuModel = menuModel;
+
+ var timeoutPromise = null;
+
+ // show subcategories of the currently hovered category
+ $scope.enterCategory = function (category) {
+
+ if (timeoutPromise) {
+ $timeout.cancel(timeoutPromise);
+ }
+
+ angular.forEach(menuModel.menu, function (cat) {
+ cat.hover = false;
+ });
+
+ category.hover = true;
+ };
+
+ // show subcategories of the current active category again (after 2 sec max)
+ $scope.leaveCategory = function (category) {
+
+ if (timeoutPromise) {
+ $timeout.cancel(timeoutPromise);
+ }
+
+ timeoutPromise = $timeout(function () {
+ angular.forEach(menuModel.menu, function (cat) {
+ if (cat.active) {
+ cat.hover = true;
+ } else {
+ cat.hover = false;
+ }
+ });
+ }, 2000);
+ };
+
+ // highlight the currently hovered subcategory (and category)
+ $scope.enterSubcategory = function (category, subcategory) {
+ if (!category || !subcategory) {
+ return;
+ }
+
+ markAllCategoriesAsInactive();
+
+ category.active = true;
+ category.hover = true;
+ subcategory.active = true;
+ };
+
+ var idSite = getUrlParam('idSite');
+ var period = getUrlParam('period');
+ var date = getUrlParam('date');
+ var segment = getUrlParam('segment');
+
+ $scope.makeUrl = function (category, subcategory) {
+ var url = 'idSite=' + idSite + '&period=' + period + '&date=' + date + '&category=' + category.id + '&subcategory=' + subcategory.id;
+ if (segment) {
+ url+= '&segment='+ segment;
+ }
+ return url;
+ }
+
+ $scope.loadSubcategory = function (category, subcategory) {
+ if (subcategory && subcategory.active) {
+ // this menu item is already active, a location change success would not be triggered,
+ // instead trigger an event
+ $rootScope.$emit('loadPage', category.id, subcategory.id);
+ }
+ };
+
+ menuModel.fetchMenuItems().then(function (menu) {
+ if (!$location.search().subcategory) {
+ // load first, initial page if no subcategory is present
+ $scope.enterSubcategory(menu[0], menu[0].subcategories[0]);
+ $location.search($scope.makeUrl(menu[0], menu[0].subcategories[0]));
+ }
+ });
+
+ $rootScope.$on('$locationChangeSuccess', function () {
+ var category = $location.search().category;
+ var subcategory = $location.search().subcategory;
+
+ if (!category || !subcategory) {
+ return;
+ }
+
+ var found = menuModel.findSubcategory(category, subcategory);
+ $scope.enterSubcategory(found.category, found.subcategory);
+ });
+
+ }
+})();
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
new file mode 100644
index 0000000000..bbfd687517
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html
@@ -0,0 +1,40 @@
+<div class="Menu--dashboard">
+ <ul class="Menu-tabList">
+ <li ng-repeat="category in menuModel.menu"
+ ng-class="{'sfActive': category.active, 'sfHover': category.hover}"
+ ng-mouseenter="enterCategory(category)"
+ ng-mouseleave="leaveCategory(category)">
+ <a class="menuItem"
+ ng-href="#?{{ makeUrl(category,category.subcategories[0]) }}"
+ ng-click="loadSubcategory(category, category.subcategories[0])">
+ {{ category.name }}
+ </a>
+ <ul ng-show="(category.subcategories|length) > 1">
+ <li ng-repeat="subcategory in category.subcategories"
+ ng-class="{'sfHover': subcategory.active}">
+ <div ng-if="subcategory.isGroup" piwik-menudropdown show-search="true" menu-title="{{ subcategory.name|escape }}">
+ <a class="item"
+ ng-repeat="subcat in subcategory.subcategories"
+ title="{{ subcat.tooltip }}"
+ ng-class="{'active': subcat.active}"
+ ng-href="#?{{ makeUrl(category,subcat) }}"
+ ng-click='loadSubcategory(category, subcat)'>
+ {{ subcat.name }}
+ </a>
+ </div>
+
+ <a ng-if="!subcategory.isGroup"
+ ng-href="#?{{ makeUrl(category,subcategory) }}"
+ class="menuItem"
+ ng-click='loadSubcategory(category, subcategory)'>
+ {{ subcategory.name }}
+ </a>
+ </li>
+ </ul>
+ </li>
+ <li id="Searchmenu">
+ <span piwik-quick-access></span>
+ </li>
+ </ul>
+
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js
new file mode 100644
index 0000000000..7cdd9e951b
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.js
@@ -0,0 +1,31 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Shows the Piwik reporting menu.
+ *
+ * It automatically calls the API to fetch all data.
+ *
+ * Example:
+ * <div piwik-reporting-menu></div>
+ */
+
+(function () {
+ angular.module('piwikApp').directive('piwikReportingMenu', piwikReportingMenu);
+
+ piwikReportingMenu.$inject = ['piwik'];
+
+ function piwikReportingMenu(piwik){
+
+ return {
+ restrict: 'A',
+ scope: {},
+ templateUrl: 'plugins/CoreHome/angularjs/reporting-menu/reportingmenu.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'ReportingMenuController'
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js
new file mode 100644
index 0000000000..4018fcee8c
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage-model.js
@@ -0,0 +1,196 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').factory('reportingPageModel', reportingPageModelService);
+
+ reportingPageModelService.$inject = ['$filter', 'piwikApi', 'reportingPagesModel', 'reportMetadataModel'];
+
+ function reportingPageModelService ($filter, piwikApi, reportingPagesModel, reportMetadataModel) {
+ var init = false;
+
+ // those sites are going to be displayed
+ var model = {
+ fetchPage: fetchPage,
+ resetPage: resetPage,
+ widgets: [],
+ page: null,
+ pageContentUrl: '',
+ evolutionReports: [],
+ sparklineReports: []
+ };
+
+ return model;
+
+ function resetPage()
+ {
+ model.page = null;
+ model.widgets = [];
+ model.pageContentUrl = '';
+ model.evolutionReports = [];
+ model.sparklineReports = [];
+ }
+
+ function sortWidgets(widgets)
+ {
+ return $filter('orderBy')(widgets, 'order');
+ }
+
+ function shouldBeRenderedWithFullWidth(widget)
+ {
+ // rather controller logic
+ if ((widget.isContainer && widget.layout && widget.layout === 'ByDimension')
+ || widget.viewDataTable === 'bydimension') {
+ return true;
+ }
+
+ return widget.viewDataTable && widget.viewDataTable === 'tableAllColumns';
+ }
+
+ function buildPage(page)
+ {
+ if (!page) {
+ return;
+ }
+
+ var widgets = [];
+ var evolutionReports = [];
+ var sparklineReports = [];
+ var reportsToIgnore = [];
+
+ angular.forEach(page.widgets, function (widget) {
+
+ if (isIgnoredReport(reportsToIgnore, widget)) {
+ return;
+ }
+
+ reportsToIgnore = reportsToIgnore.concat(getRelatedReports(widget));
+
+ if (widget.viewDataTable && widget.viewDataTable === 'graphEvolution') {
+ evolutionReports.push(widget);
+ } else if (widget.viewDataTable && widget.viewDataTable === 'sparklines') {
+ sparklineReports.push(widget);
+ } else {
+ widgets.push(widget);
+ }
+ });
+
+ widgets = sortWidgets(widgets);
+
+ var groupedWidgets = [];
+
+ if (widgets.length === 1) {
+ // if there is only one widget, we always display it full width
+ groupedWidgets = widgets;
+ } else {
+ for (var i = 0; i < widgets.length; i++) {
+ var widget = widgets[i];
+
+ if (shouldBeRenderedWithFullWidth(widget) || (widgets[i+1] && shouldBeRenderedWithFullWidth(widgets[i+1]))) {
+ widget.widgets = sortWidgets(widget.widgets);
+
+ groupedWidgets.push(widget);
+ } else {
+
+ var counter = 0;
+ var left = [widget];
+ var right = [];
+
+ while (widgets[i+1] && !shouldBeRenderedWithFullWidth(widgets[i+1])) {
+ i++;
+ counter++;
+ if (counter % 2 === 0) {
+ left.push(widgets[i]);
+ } else {
+ right.push(widgets[i]);
+ }
+ }
+
+ groupedWidgets.push({group: true, left: left, right: right});
+ }
+ }
+ }
+
+ var copyWidgets = angular.copy(groupedWidgets);
+ var copyEvolution = angular.copy(evolutionReports);
+ var copySparklines = angular.copy(sparklineReports);
+
+ if (copyEvolution.length) {
+ copyEvolution = markWidgetsInFirstRowOfPage(copyEvolution);
+ } else if (copySparklines.length) {
+ copySparklines = markWidgetsInFirstRowOfPage(copySparklines);
+ } else {
+ copyWidgets = markWidgetsInFirstRowOfPage(copyWidgets);
+ }
+
+ // angular.copy forces the page to re-render. Otherwise it won't reload some pages
+ model.evolutionReports = copyEvolution;
+ model.sparklineReports = copySparklines;
+ model.widgets = copyWidgets;
+ }
+
+ function markWidgetsInFirstRowOfPage(widgets)
+ {
+ if (widgets && widgets[0]) {
+ if (widgets[0].group) {
+ markWidgetsInFirstRowOfPage(widgets[0].left);
+ markWidgetsInFirstRowOfPage(widgets[0].right);
+ } else {
+ widgets[0].isFirstInPage = true;
+ }
+ }
+
+ return widgets;
+ }
+
+ function getRelatedReports(widget)
+ {
+ if (widget.isReport) {
+ var report = reportMetadataModel.findReport(widget.module, widget.action);
+
+ if (report && report.relatedReports) {
+ return report.relatedReports;
+ }
+ }
+
+ return [];
+ }
+
+ function isIgnoredReport(reportsToIgnore, widget)
+ {
+ var found = false;
+
+ if (widget.isReport) {
+ angular.forEach(reportsToIgnore, function (report) {
+ if (report.module === widget.module &&
+ report.action === widget.action) {
+ found = true;
+ }
+ });
+ }
+
+ return found;
+ }
+
+ function fetchPage(category, subcategory)
+ {
+ resetPage();
+
+ var pagesPromise = reportingPagesModel.getAllPages();
+ var reportsPromise = reportMetadataModel.fetchReportMetadata();
+
+ return pagesPromise.then(function () {
+ model.page = reportingPagesModel.findPage(category, subcategory);
+
+ reportsPromise.then(function () {
+ buildPage(model.page);
+ });
+
+ return model.page;
+ });
+ }
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
new file mode 100644
index 0000000000..cf0d424b55
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.controller.js
@@ -0,0 +1,56 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+(function () {
+ angular.module('piwikApp').controller('ReportingPageController', ReportingPageController);
+
+ ReportingPageController.$inject = ['$scope', 'piwik', '$rootScope', '$location', 'reportingPageModel'];
+
+ function ReportingPageController($scope, piwik, $rootScope, $location, pageModel) {
+ pageModel.resetPage();
+ $scope.pageModel = pageModel;
+
+ var currentCategory = null;
+ var currentSubcategory = null;
+
+ $scope.renderPage = function (category, subcategory) {
+ if (!category || !subcategory) {
+ pageModel.resetPage();
+ $scope.loading = false;
+ return;
+ }
+
+ currentCategory = category;
+ currentSubcategory = subcategory;
+
+ pageModel.fetchPage(category, subcategory).then(function () {
+ $scope.hasNoPage = !pageModel.page;
+ $scope.loading = false;
+ });
+ }
+
+ $scope.loading = true; // we only set loading on initial load
+
+ $scope.renderPage($location.search().category, $location.search().subcategory);
+
+ $rootScope.$on('$locationChangeSuccess', function () {
+ // should be handled by $route
+ var category = $location.search().category;
+ var subcategory = $location.search().subcategory;
+
+ if (category === currentCategory && subcategory === currentSubcategory) {
+ // this page is already loaded
+ return;
+ }
+
+ $scope.renderPage(category, subcategory);
+ });
+
+ $rootScope.$on('loadPage', function (event, category, subcategory) {
+ $scope.renderPage(category, subcategory);
+ });
+ }
+})();
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
new file mode 100644
index 0000000000..9a8974e313
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html
@@ -0,0 +1,25 @@
+<div class="reporting-page">
+
+ <div piwik-activity-indicator loading="loading"/>
+
+ <div ng-show="hasNoPage">{{ 'CoreHome_NoSuchPage'|translate }}</div>
+
+ <div class="row" ng-repeat="evolutionReport in pageModel.evolutionReports">
+ <div class="col-md-12" piwik-widget="evolutionReport"></div>
+ </div>
+
+ <div class="row" ng-repeat="sparklineReport in pageModel.sparklineReports">
+ <div class="col-md-12" piwik-widget="sparklineReport"></div>
+ </div>
+
+ <div class="row" ng-repeat="widget in pageModel.widgets">
+ <div class="col-md-12" ng-if="!widget.group" piwik-widget="widget"></div>
+
+ <div ng-if="widget.group" class="col-md-6">
+ <div ng-repeat="widgetInGroup in widget.left" piwik-widget="widgetInGroup"></div>
+ </div>
+ <div ng-if="widget.group" class="col-md-6">
+ <div ng-repeat="widgetInGroup in widget.right" piwik-widget="widgetInGroup"></div>
+ </div>
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js
new file mode 100644
index 0000000000..a146ed3112
--- /dev/null
+++ b/plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.js
@@ -0,0 +1,31 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Shows a piwik reporting page.
+ *
+ * The content to be displayed is automatically loaded via API based on the current URL. The URL parameters
+ * 'category' and 'subcategory' need to be present in the URL in order to see something in the reporting page.
+ *
+ * Example:
+ * <div piwik-reporting-page></div>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikReportingPage', piwikReportingPage);
+
+ piwikReportingPage.$inject = ['piwik'];
+
+ function piwikReportingPage(piwik){
+
+ return {
+ restrict: 'A',
+ scope: {},
+ templateUrl: 'plugins/CoreHome/angularjs/reporting-page/reportingpage.directive.html?cb=' + piwik.cacheBuster,
+ controller: 'ReportingPageController'
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
new file mode 100644
index 0000000000..db98dee895
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html
@@ -0,0 +1,25 @@
+<div class="reportsByDimensionView">
+
+ <div class="entityList">
+ <div class='dimensionCategory' ng-repeat="category in widgetsByCategory">
+ {{ category.name }}
+ <ul class='listCircle'>
+ <li ng-repeat="widget in category.widgets"
+ class="reportDimension"
+ ng-class="{activeDimension: (selectedWidget.uniqueId===widget.uniqueId)}"
+ ng-click="selectWidget(widget)">
+ <span class='dimension'>{{widget.name}}</span>
+ </li>
+ </ul>
+ </div>
+ </div>
+
+ <div style="float:left;max-width:900px;">
+ <h2 ng-if="selectedWidget.name" class="noTopMargin">{{ selectedWidget.name }}</h2>
+
+ <div ng-if="selectedWidget.parameters" class="dimensionReport"
+ piwik-widget-loader="selectedWidget.parameters"></div>
+ </div>
+ <div class="clear"></div>
+
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js
new file mode 100644
index 0000000000..1620be2a9d
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.js
@@ -0,0 +1,67 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Renders a widget that is a container widget having the layout "ByDimension".
+ *
+ * The "ByDimension" layout shows a menu on the left letting you choose any widgets within this container. The
+ * currently selected widget is shown on the right.
+ *
+ * @param {Object} piwikWidgetByDimensionContainer a widget object as returned by the WidgetMetadata API.
+ *
+ * Example:
+ * <div piwik-widget-by-dimension-container="containerWidget"></div>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidgetByDimensionContainer', piwikWidgetContainer);
+
+ piwikWidgetContainer.$inject = ['piwik', '$filter'];
+
+ function piwikWidgetContainer(piwik, $filter){
+ return {
+ restrict: 'A',
+ scope: {
+ container: '=piwikWidgetByDimensionContainer'
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget-bydimension-container/widget-bydimension-container.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, ngModel) {
+
+ var widgetsSorted = $filter('orderBy')(scope.container.widgets, 'order');
+ var widgetsByCategory = {};
+
+ angular.forEach(widgetsSorted, function (widget) {
+ var category = widget.subcategory.name;
+
+ if (!widgetsByCategory[category]) {
+ widgetsByCategory[category] = {name: category, order: widget.order, widgets: []};
+ }
+
+ widgetsByCategory[category].widgets.push(widget);
+ });
+
+ // only an array can be sorted
+ var finalWidgetsByCategory = [];
+ angular.forEach(widgetsByCategory, function (category) {
+ finalWidgetsByCategory.push(category);
+ });
+
+ scope.widgetsByCategory = $filter('orderBy')(finalWidgetsByCategory, 'order');
+
+ scope.selectWidget = function (widget) {
+ scope.selectedWidget = widget;
+ }
+
+ if (widgetsSorted && widgetsSorted.length) {
+ scope.selectWidget(widgetsSorted[0]);
+ }
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html
new file mode 100644
index 0000000000..21ebd3d0cd
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html
@@ -0,0 +1,10 @@
+<div>
+ <!-- custom template prevents recursion -->
+ <script id="mywidget.html" type="text/ng-template">
+ <div piwik-widget="widget"></div>
+ </script>
+
+ <div ng-repeat="widget in container.widgets">
+ <div ng-include src="'mywidget.html'"/>
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
new file mode 100644
index 0000000000..c378637c71
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.js
@@ -0,0 +1,32 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Renders a widget that is a container widget having no specific layout (which is the default).
+ *
+ * It shows all widgets vertically aligned one widget after another.
+ *
+ * @param {Object} piwikWidgetContainer a widget object as returned by the WidgetMetadata API.
+ *
+ * Example:
+ * <div piwik-widget-container="containerWidget"></div>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidgetContainer', piwikWidgetContainer);
+
+ piwikWidgetContainer.$inject = ['piwik'];
+
+ function piwikWidgetContainer(piwik){
+ return {
+ restrict: 'A',
+ scope: {
+ container: '=piwikWidgetContainer'
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget-container/widgetcontainer.directive.html?cb=' + piwik.cacheBuster
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
new file mode 100644
index 0000000000..d1f0cb51f1
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html
@@ -0,0 +1,13 @@
+<div>
+
+ <div piwik-activity-indicator loading="loading"/>
+
+ <div ng-show="loadingFailed">
+ <div class="notification system notification-error">
+ {{ 'General_ErrorRequest'|translate:(''):('') }}
+ </div>
+ </div>
+
+ <div class="theWidgetContent"></div>
+
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
new file mode 100644
index 0000000000..4e1859e789
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.js
@@ -0,0 +1,134 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Loads any custom widget or URL based on the given parameters.
+ *
+ * The currently active idSite, period, date and segment (if needed) is automatically appended to the parameters. If
+ * this widget is removed from the DOM and requests are in progress, these requests will be aborted. A loading message
+ * or an error message on failure is shown as well. It's kinda similar to ng-include but there it is not possible to
+ * listen to HTTP errors etc.
+ *
+ * Example:
+ * <div piwik-widget-loader="{module: '', action: '', ...}"></div>
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidgetLoader', piwikWidgetLoader);
+
+ piwikWidgetLoader.$inject = ['piwik', 'piwikUrl', '$http', '$compile', '$q'];
+
+ function piwikWidgetLoader(piwik, piwikUrl, $http, $compile, $q){
+ return {
+ restrict: 'A',
+ transclude: true,
+ scope: {
+ piwikWidgetLoader: '='
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget-loader/widgetloader.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, ngModel) {
+ var changeCounter = 0,
+ currentScope,
+ currentElement,
+ httpCanceler,
+ contentNode = element.find('.theWidgetContent');
+
+ var cleanupLastWidgetContent = function() {
+ if (currentElement) {
+ currentElement.remove();
+ currentElement = null;
+ }
+ if (currentScope) {
+ currentScope.$destroy();
+ currentScope = null;
+ }
+ };
+
+ var abortHttpRequestIfNeeded = function () {
+ if (httpCanceler) {
+ httpCanceler.resolve();
+ httpCanceler = null;
+ }
+ }
+
+ function getFullWidgetUrl(parameters) {
+
+ var url = $.param(parameters);
+
+ var idSite = piwikUrl.getSearchParam('idSite');
+ var period = piwikUrl.getSearchParam('period');
+ var date = piwikUrl.getSearchParam('date');
+ var segment = piwikUrl.getSearchParam('segment');
+
+ url += '&idSite=' + idSite + '&period=' + period;
+ url += '&date=' + date + '&random=' + parseInt(Math.random() * 10000);
+
+ if (segment) {
+ url += '&segment=' + segment;
+ }
+
+ return '?' + url;
+ }
+
+ function loadWidgetUrl(parameters, thisChangeId)
+ {
+ scope.loading = true;
+
+ var url = getFullWidgetUrl(parameters);
+
+ abortHttpRequestIfNeeded();
+ cleanupLastWidgetContent();
+
+ httpCanceler = $q.defer();
+
+ $http.get(url, {timeout: httpCanceler.promise}).success(function(response) {
+ if (thisChangeId !== changeCounter || !response) {
+ // another widget was requested meanwhile, ignore this response
+ return;
+ }
+
+ httpCanceler = null;
+
+ var newScope = scope.$new();
+ currentScope = newScope;
+
+ scope.loading = false;
+ scope.loadingFailed = false;
+
+ currentElement = contentNode.html(response).children();
+ $compile(currentElement)(newScope);
+
+ }).error(function () {
+ if (thisChangeId !== changeCounter) {
+ // another widget was requested meanwhile, ignore this response
+ return;
+ }
+
+ httpCanceler = null;
+
+ cleanupLastWidgetContent();
+
+ scope.loading = false;
+ scope.loadingFailed = true;
+ });
+ }
+
+ scope.$watch('piwikWidgetLoader', function (parameters, oldUrl) {
+ if (parameters) {
+ loadWidgetUrl(parameters, ++changeCounter);
+ }
+ });
+
+ element.on('$destroy', function() {
+ abortHttpRequestIfNeeded();
+ });
+ };
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget/widget.directive.html b/plugins/CoreHome/angularjs/widget/widget.directive.html
new file mode 100644
index 0000000000..86cdc91291
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget/widget.directive.html
@@ -0,0 +1,23 @@
+<div id="{{ widget.uniqueId }}"
+ ng-show="view.showWidget"
+ class="{{widget.viewDataTable}}"
+ ng-class="{'smallTopMargin': (!widget.isFirstInPage && (!widget.name || widgetized))}"
+ >
+ <!--smallTopMargin: we display small margin if it's not the first widget on the page and if there's no headline -->
+ <h2 ng-if="!widget.parameters.widget && widget.name && !widgetized"
+ piwik-enriched-headline
+ ng-class="{'noTopMargin': widget.isFirstInPage}"
+ feature-name="{{widget.name}}">{{widget.name}}</h2>
+ <h2 ng-if="widget.parameters.widget && widget.name && !widgetized">{{widget.name}}</h2>
+
+ <div ng-if="!widget.isContainer && widget.parameters"
+ piwik-widget-loader="widget.parameters"></div>
+
+ <div ng-if="widget.isContainer && widget.layout!='ByDimension'">
+ <div piwik-widget-container="widget"></div>
+ </div>
+
+ <div ng-if="widget.isContainer && widget.layout=='ByDimension'">
+ <div piwik-widget-by-dimension-container="widget"></div>
+ </div>
+</div> \ No newline at end of file
diff --git a/plugins/CoreHome/angularjs/widget/widget.directive.js b/plugins/CoreHome/angularjs/widget/widget.directive.js
new file mode 100644
index 0000000000..86574870f2
--- /dev/null
+++ b/plugins/CoreHome/angularjs/widget/widget.directive.js
@@ -0,0 +1,93 @@
+/*!
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+/**
+ * Renders any kind of widget. If you have a widget and you want to have it rendered, use this directive. It will
+ * display a name on top and the actual widget below. It can handle any kind of widget, no matter whether it is a
+ * regular widget or a container.
+ *
+ * @param {Object} piwikWidget A widget object as returned by the WidgetMetadata API.
+ * @param {Object} piwikWidget.middlewareParameters If present, we will request a URL using the given parameters and
+ * only if this URL returns a JSON `true` the widget will be shown.
+ * Otherwise the widget won't be shown.
+ * @param {String} containerId If you do not have a widget object but a containerId we will find the correct widget
+ * object based on the given containerId. Be aware that we might not find the widget if
+ * it is for example not available for the current user or period/date.
+ * @param {Boolean} widgetized true if the widget is widgetized (eg in Dashboard or exported). In this case we will add
+ * a URL parameter widget=1 to all widgets. Eg sparklines will be then displayed one after
+ * another (vertically aligned) instead of two next to each other.
+ *
+ * Example:
+ * <div piwik-widget="widget"></div>
+ * <div piwik-widget containerid="widgetGoalsOverview"></div> // in this case we will find the correct widget automatically
+ * <div piwik-widget="widget" widetized="true"></div> // disables rating feature, no initial headline
+ */
+(function () {
+ angular.module('piwikApp').directive('piwikWidget', piwikWidget);
+
+ piwikWidget.$inject = ['piwik', 'piwikApi'];
+
+ function piwikWidget(piwik, piwikApi){
+
+ function findContainerWidget(containerId, scope) {
+ widgetsHelper.getAvailableWidgets(function (categorizedWidgets) {
+
+ angular.forEach(categorizedWidgets, function (widgets) {
+ angular.forEach(widgets, function (widget) {
+
+ if (widget && widget.isContainer && widget.parameters.containerId === containerId) {
+ widget = angular.copy(widget);
+ if (scope.widgetized) {
+ widget.isFirstInPage = '1';
+ widget.parameters.widget = '1';
+ angular.forEach(widget.widgets, function (widget) {
+ widget.parameters.widget = '1';
+ });
+ }
+ scope.widget = widget;
+ applyMiddleware(scope);
+ }
+ });
+ });
+
+ });
+ }
+
+ function applyMiddleware(scope)
+ {
+ if (!scope.widget.middlewareParameters) {
+ scope.$eval('view.showWidget = true');
+ } else {
+ var params = angular.copy(scope.widget.middlewareParameters);
+ piwikApi.fetch(params).then(function (response) {
+ var enabled = response ? 'true' : 'false';
+ scope.$eval('view.showWidget = ' + enabled);
+ });
+ }
+ }
+
+ return {
+ restrict: 'A',
+ scope: {
+ widget: '=?piwikWidget',
+ widgetized: '=?',
+ containerid: '='
+ },
+ templateUrl: 'plugins/CoreHome/angularjs/widget/widget.directive.html?cb=' + piwik.cacheBuster,
+ compile: function (element, attrs) {
+
+ return function (scope, element, attrs, ngModel) {
+ if (scope.widget) {
+ applyMiddleware(scope);
+ } else if (attrs.containerid) {
+ findContainerWidget(attrs.containerid, scope);
+ }
+ }
+ }
+ };
+ }
+})(); \ No newline at end of file
diff --git a/plugins/CoreHome/javascripts/broadcast.js b/plugins/CoreHome/javascripts/broadcast.js
index 706a8e59d5..3d5fb0432c 100644
--- a/plugins/CoreHome/javascripts/broadcast.js
+++ b/plugins/CoreHome/javascripts/broadcast.js
@@ -159,7 +159,6 @@ var broadcast = {
} else {
// start page
Piwik_Popover.close();
-
$('.pageWrap #content:not(.admin)').empty();
}
},
@@ -173,6 +172,7 @@ var broadcast = {
},
/**
+ * ONLY USED BY OVERLAY
* propagateAjax -- update hash values then make ajax calls.
* example :
* 1) <a href="javascript:broadcast.propagateAjax('module=Referrers&action=getKeywords')">View keywords report</a>
@@ -229,6 +229,47 @@ var broadcast = {
},
/**
+ * propagateAjax -- update hash values then make ajax calls.
+ * example :
+ * 1) <a href="javascript:broadcast.propagateAjax('module=Referrers&action=getKeywords')">View keywords report</a>
+ * 2) Main menu li also goes through this function.
+ *
+ * Will propagate your new value into the current hash string and make ajax calls.
+ *
+ * NOTE: this method will only make ajax call and replacing main content.
+ *
+ * @param {string} ajaxUrl querystring with parameters to be updated
+ * @param {boolean} [disableHistory] the hash change won't be available in the browser history
+ * @return {void}
+ */
+ buildReportingUrl: function (ajaxUrl, disableHistory) {
+
+ // available in global scope
+ var currentHashStr = broadcast.getHash();
+
+ ajaxUrl = ajaxUrl.replace(/^\?|&#/, '');
+
+ var params_vals = ajaxUrl.split("&");
+ for (var i = 0; i < params_vals.length; i++) {
+ currentHashStr = broadcast.updateParamValue(params_vals[i], currentHashStr);
+ }
+
+ // if the module is not 'Goals', we specifically unset the 'idGoal' parameter
+ // this is to ensure that the URLs are clean (and that clicks on graphs work as expected - they are broken with the extra parameter)
+ var action = broadcast.getParamValue('action', currentHashStr);
+ if (action != 'goalReport' && action != 'ecommerceReport' && action != 'products' && action != 'sales') {
+ currentHashStr = broadcast.updateParamValue('idGoal=', currentHashStr);
+ }
+ // unset idDashboard if use doesn't display a dashboard
+ var module = broadcast.getParamValue('module', currentHashStr);
+ if (module != 'Dashboard') {
+ currentHashStr = broadcast.updateParamValue('idDashboard=', currentHashStr);
+ }
+
+ return '#' + currentHashStr;
+ },
+
+ /**
* propagateNewPage() -- update url value and load new page,
* Example:
* 1) We want to update idSite to both search query and hash then reload the page,
@@ -349,9 +390,9 @@ var broadcast = {
*/
propagateNewPopoverParameter: function (handlerName, value) {
// init broadcast if not already done (it is required to make popovers work in widgetize mode)
- broadcast.init(true);
+ //broadcast.init(true);
- var hash = broadcast.getHashFromUrl(window.location.href);
+ var $location = angular.element(document).injector().get('$location');
var popover = '';
if (handlerName) {
@@ -365,24 +406,14 @@ var broadcast = {
}
if ('' == value || 'undefined' == typeof value) {
- var newHash = hash.replace(/(&?popover=.*)/, '');
- } else if (broadcast.getParamValue('popover', hash)) {
- var newHash = broadcast.updateParamValue('popover='+popover, hash);
- } else if (hash && hash != '#') {
- var newHash = hash + '&popover=' + popover
+ $location.search('popover', '');
} else {
- var newHash = '#popover='+popover;
- }
-
- // never use an empty hash, as that might reload the page
- if ('' == newHash) {
- newHash = '#';
+ $location.search('popover', popover);
}
- broadcast.forceReload = false;
- angular.element(document).injector().invoke(function (historyService) {
- historyService.load(newHash);
- });
+ setTimeout(function () {
+ angular.element(document).injector().get('$rootScope').$apply();
+ }, 1);
},
/**
diff --git a/plugins/CoreHome/javascripts/corehome.js b/plugins/CoreHome/javascripts/corehome.js
index 5bddc23f9e..6e8729611b 100755
--- a/plugins/CoreHome/javascripts/corehome.js
+++ b/plugins/CoreHome/javascripts/corehome.js
@@ -114,60 +114,6 @@
handleSectionToggle(this, 'inline', !$(this).is(':checked'));
});
- //
- // reports by dimension list behavior
- //
-
- // when a report dimension is clicked, load the appropriate report
- var currentWidgetLoading = null;
- $('body').on('click', '.reportDimension', function (e) {
- var view = $(this).closest('.reportsByDimensionView'),
- report = $('.dimensionReport', view),
- loading = $('.loadingPiwik', view);
-
- // make this dimension the active one
- $('.activeDimension', view).removeClass('activeDimension');
- $(this).addClass('activeDimension');
-
- // hide the visible report & show the loading elem
- report.hide();
- loading.show();
-
- // load the report using the data-url attribute (which holds the URL to the report)
- var widgetParams = broadcast.getValuesFromUrl($(this).attr('data-url'));
- for (var key in widgetParams) {
- widgetParams[key] = decodeURIComponent(widgetParams[key]);
- }
-
- var widgetUniqueId = widgetParams.module + widgetParams.action;
- currentWidgetLoading = widgetUniqueId;
-
- widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParams, function (response) {
- // if the widget that was loaded was not for the latest clicked link, do nothing w/ the response
- if (widgetUniqueId != currentWidgetLoading) {
- return;
- }
-
- loading.hide();
- report.css('display', 'inline-block').html($(response));
-
- // scroll to report
- piwikHelper.lazyScrollTo(report, 400);
- }, function (deferred, status) {
- if (status == 'abort' || !deferred || deferred.status < 400 || deferred.status >= 600) {
- return;
- }
-
- loading.hide();
-
- var errorMessage = _pk_translate('General_ErrorRequest', ['', '']);
- if ($('#loadingError').html()) {
- errorMessage = $('#loadingError').html();
- }
-
- report.css('display', 'inline-block').html('<div class="dimensionLoadingError">' + errorMessage + '</div>');
- });
- });
});
}(jQuery));
diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js
index 0561185de8..4bd5fb609a 100644
--- a/plugins/CoreHome/javascripts/dataTable.js
+++ b/plugins/CoreHome/javascripts/dataTable.js
@@ -1,3 +1,4 @@
+
/*!
* Piwik - free/libre analytics platform
*
diff --git a/plugins/CoreHome/javascripts/menu.js b/plugins/CoreHome/javascripts/menu.js
deleted file mode 100644
index 2f5e16c827..0000000000
--- a/plugins/CoreHome/javascripts/menu.js
+++ /dev/null
@@ -1,114 +0,0 @@
-/*!
- * Piwik - free/libre analytics platform
- *
- * @link http://piwik.org
- * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
- */
-
-/**
- * @constructor
- */
-function menu() {
- this.param = {};
-}
-
-menu.prototype =
-{
- resetTimer: null,
-
- adaptSubMenuHeight: function() {
- var subNavHeight = $('.sfHover > ul').outerHeight();
- $('.nav_sep').height(subNavHeight);
- },
-
- overMainLI: function () {
- var $this = $(this);
- $this.siblings().removeClass('sfHover');
- $this.addClass('sfHover');
- menu.prototype.adaptSubMenuHeight();
- clearTimeout(menu.prototype.resetTimer);
- },
-
- outMainLI: function () {
- clearTimeout(menu.prototype.resetTimer);
- menu.prototype.resetTimer = setTimeout(function() {
- $('.Menu-tabList > .sfHover', this.menuNode).removeClass('sfHover');
- $('.Menu-tabList > .sfActive', this.menuNode).addClass('sfHover');
- menu.prototype.adaptSubMenuHeight();
- }, 2000);
- },
-
- onItemClick: function (e) {
- if (e.which === 2) {
- return;
- }
- $('.Menu--dashboard').trigger('piwikSwitchPage', this);
- broadcast.propagateAjax( $(this).attr('href').substr(1) );
- return false;
- },
-
- init: function () {
- this.menuNode = $('.Menu--dashboard');
-
- this.menuNode.find("li:has(ul),li#Searchmenu").hover(this.overMainLI, this.outMainLI);
- this.menuNode.find("li:has(ul),li#Searchmenu").focusin(this.overMainLI);
-
- this.menuNode.find('a.menuItem').click(this.onItemClick);
-
- menu.prototype.adaptSubMenuHeight();
- },
-
- activateMenu: function (module, action, params) {
- params = params || {};
- params.module = module;
- params.action = action;
-
- this.menuNode.find('li').removeClass('sfHover').removeClass('sfActive');
- var $activeLink = this.menuNode.find('a').filter(function () {
- var url = $(this).attr('href');
- if (!url) {
- return false;
- }
-
- for (var key in params) {
- if (!params.hasOwnProperty(key)
- || !params[key]
- ) {
- continue;
- }
-
- var actual = broadcast.getValueFromHash(key, url);
- if (actual != params[key]) {
- return false;
- }
- }
-
- return true;
- });
-
- $activeLink.closest('li').addClass('sfHover');
- $activeLink.closest('li.menuTab').addClass('sfActive').addClass('sfHover');
- },
-
- // getting the right li is a little tricky since goals uses idGoal, and overview is index.
- getSubmenuID: function (module, id, action) {
- var $li = '';
- // So, if module is Goals, id is present, and action is not Index, must be one of the goals
- if ((module == 'Goals' || module == 'Ecommerce') && id != '' && (action != 'index')) {
- $li = $("#" + module + "_" + action + "_" + id);
- // if module is Dashboard and id is present, must be one of the dashboards
- } else if (module == 'Dashboard') {
- if (!id) id = 1;
- $li = $("#" + module + "_" + action + "_" + id);
- } else {
- $li = $("#" + module + "_" + action);
- }
- return $li;
- },
-
- loadFirstSection: function () {
- if (broadcast.isHashExists() == false) {
- $('li:first a:first', this.menuNode).click().addClass('sfHover').addClass('sfActive');
- }
- }
-};
diff --git a/plugins/CoreHome/javascripts/menu_init.js b/plugins/CoreHome/javascripts/menu_init.js
deleted file mode 100644
index 490c859185..0000000000
--- a/plugins/CoreHome/javascripts/menu_init.js
+++ /dev/null
@@ -1,19 +0,0 @@
-$(function () {
- var isPageHasMenu = $('.Menu--dashboard').size();
- var isPageIsAdmin = $('#content.admin').size();
- if (isPageHasMenu) {
- piwikMenu = new menu();
- piwikMenu.init();
- piwikMenu.loadFirstSection();
- }
-
- if(isPageIsAdmin) {
- // don't use broadcast in admin pages
- return;
- }
- if(isPageHasMenu) {
- broadcast.init();
- } else {
- broadcast.init(true);
- }
-});
diff --git a/plugins/CoreHome/javascripts/sparkline.js b/plugins/CoreHome/javascripts/sparkline.js
index fc3b74f692..256e6f1589 100644
--- a/plugins/CoreHome/javascripts/sparkline.js
+++ b/plugins/CoreHome/javascripts/sparkline.js
@@ -35,14 +35,30 @@ piwik.initSparklines = function() {
};
window.initializeSparklines = function () {
- var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable'];
+ var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable', 'forceView', 'random'];
- $("[data-graph-id]").each(function () {
+ $('.graphEvolution [data-report]').each(function () {
var graph = $(this);
- // try to find sparklines and add them clickable behaviour
- graph.parent().find('div.sparkline').each(function () {
+ // we search for .widget to make sure eg in the Dashboard to not update any graph of another report
+ var selectorsToFindParent = ['.widget', '.reporting-page', 'body'];
+ var index = 0, selector, parent;
+ for (index; index < selectorsToFindParent.length; index++) {
+ selector = selectorsToFindParent[index];
+ parent = graph.parents(selector).first();
+ if (parent && parent.length) {
+ break;
+ }
+ }
+
+ if (!parent || !parent.length) {
+ return;
+ }
+ var sparklines = parent.find('div.sparkline');
+
+ // try to find sparklines and add them clickable behaviour
+ sparklines.each(function () {
// find the sparkline and get it's src attribute
var sparklineUrl = $('img', this).attr('data-src');
@@ -66,8 +82,8 @@ window.initializeSparklines = function () {
// on click, reload the graph with the new url
$(this).off('click.sparkline');
$(this).on('click.sparkline', function () {
- var reportId = graph.attr('data-graph-id'),
- dataTable = $(require('piwik/UI').DataTable.getDataTableByReport(reportId));
+ var reportId = graph.attr('data-report'),
+ dataTable = graph;
// when the metrics picker is used, the id of the data table might be updated (which is correct behavior).
// for example, in goal reports it might change from GoalsgetEvolutionGraph to GoalsgetEvolutionGraph1.
diff --git a/plugins/CoreHome/lang/en.json b/plugins/CoreHome/lang/en.json
index a6bab4a544..7b3d4bbcf3 100644
--- a/plugins/CoreHome/lang/en.json
+++ b/plugins/CoreHome/lang/en.json
@@ -48,6 +48,7 @@
"YouAreUsingTheLatestVersion": "You are using the latest version of Piwik!",
"ClickRowToExpandOrContract": "Click this row to expand or contract the subtable.",
"UndoPivotBySubtable": "This report has been pivoted %s Undo pivot",
- "PivotBySubtable": "This report is not pivoted %s Pivot by %s"
+ "PivotBySubtable": "This report is not pivoted %s Pivot by %s",
+ "NoSuchPage": "This page does not exist"
}
}
diff --git a/plugins/CoreHome/stylesheets/coreHome.less b/plugins/CoreHome/stylesheets/coreHome.less
index 5474eae3d2..63607d749f 100644
--- a/plugins/CoreHome/stylesheets/coreHome.less
+++ b/plugins/CoreHome/stylesheets/coreHome.less
@@ -120,6 +120,12 @@ div.ui-datepicker {
display: none;
}
+.reporting-page {
+ .sparklines {
+ max-width: 1250px;
+ }
+}
+
div .sparkline {
float: left;
clear: both;
diff --git a/plugins/CoreHome/stylesheets/zen-mode.less b/plugins/CoreHome/stylesheets/zen-mode.less
index 124c3395ff..112c6b2da6 100644
--- a/plugins/CoreHome/stylesheets/zen-mode.less
+++ b/plugins/CoreHome/stylesheets/zen-mode.less
@@ -77,13 +77,25 @@
#content:not(.admin), .widget {
- h2:nth-of-type(n+2) {
- margin-top: 40px;
+ .reporting-page {
+ .smallTopMargin:not(.graphEvolution) {
+ margin-top: 20px;
+ }
}
h2 {
+ margin-top: 40px;
padding-left: 10px;
font-size: 24px;
+
+ &.noTopMargin {
+ margin-top: 0px;
+ }
+ }
+
+ .widget [piwik-widget-container] [piwik-widget]:first-child h2 {
+ // eg Visits Overview with Graph should not have a margin-top
+ margin-top: 0px;
}
}
diff --git a/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig b/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig
deleted file mode 100644
index 57ec8279d5..0000000000
--- a/plugins/CoreHome/templates/ReportsByDimension/_reportsByDimension.twig
+++ /dev/null
@@ -1,29 +0,0 @@
-<div class="reportsByDimensionView">
-
- <div class="entityList">
- {% for category, dimensions in dimensionCategories %}
- {% set firstCategory = (loop.index0 == 0) %}
- <div class='dimensionCategory'>
- {{ category|translate }}
- <ul class='listCircle'>
- {% for idx, dimension in dimensions %}
- <li class="reportDimension {% if idx == 0 and firstCategory %}activeDimension{% endif %}"
- data-url="{{ dimension.url }}">
- <span class='dimension'>{{ dimension.title|translate }}</span>
- </li>
- {% endfor %}
- </ul>
- </div>
- {% endfor %}
- </div>
-
- <div style="float:left;max-width:900px;">
- <div class="loadingPiwik" style="display:none;">
- <img src="plugins/Morpheus/images/loading-blue.gif" alt=""/>{{ 'General_LoadingData'|translate }}
- </div>
-
- <div class="dimensionReport">{{ firstReport|raw }}</div>
- </div>
- <div class="clear"></div>
-
-</div>
diff --git a/plugins/CoreHome/templates/_indexContent.twig b/plugins/CoreHome/templates/_indexContent.twig
index 46d2f13579..671c26e782 100644
--- a/plugins/CoreHome/templates/_indexContent.twig
+++ b/plugins/CoreHome/templates/_indexContent.twig
@@ -12,8 +12,11 @@
{{ ajax.loadingDiv() }}
+ <div piwik-popover></div>
+
<div id="content" class="home">
{% if content %}{{ content }}{% endif %}
+ <div piwik-reporting-page ng-cloak></div>
</div>
<div class="clear"></div>
</div>
diff --git a/plugins/CoreHome/templates/getDefaultIndexView.twig b/plugins/CoreHome/templates/getDefaultIndexView.twig
index 7b46ca3f76..4efd453159 100644
--- a/plugins/CoreHome/templates/getDefaultIndexView.twig
+++ b/plugins/CoreHome/templates/getDefaultIndexView.twig
@@ -4,9 +4,8 @@
{% include "@CoreHome/_siteSelectHeader.twig" %}
-{% if (menu is defined and menu) %}
- {% include "@CoreHome/_menu.twig" %}
-{% endif %}
+<div piwik-reporting-menu></div>
+<div class="nav_sep"></div>
{% include "@CoreHome/_indexContent.twig" %}
diff --git a/plugins/CoreHome/templates/widgetContainer.twig b/plugins/CoreHome/templates/widgetContainer.twig
new file mode 100755
index 0000000000..b3eda12997
--- /dev/null
+++ b/plugins/CoreHome/templates/widgetContainer.twig
@@ -0,0 +1,18 @@
+<div>
+ <div piwik-widget
+ containerid="{{ containerId|e('html_attr') }}"
+ widgetized="{% if isWidgetized %}true{% else %}false{% endif %}"></div>
+
+ <script type="text/javascript">
+ $(function () {
+
+ var piwikWidget = $('[piwik-widget][containerid={{ containerId|e('js') }}]');
+
+ angular.element(document).injector().invoke(function($compile) {
+ var scope = angular.element(piwikWidget).scope();
+ $compile(piwikWidget)(scope.$new());
+ });
+
+ });
+ </script>
+</div> \ No newline at end of file