add icons for Character groups

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

View file

@ -0,0 +1,147 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\Dashboard;
use Piwik\Piwik;
use Piwik\WidgetsList;
/**
* This API is the <a href='http://piwik.org/docs/analytics-api/reference/' target='_blank'>Dashboard API</a>: it gives information about dashboards.
*
* @method static \Piwik\Plugins\Dashboard\API getInstance()
*/
class API extends \Piwik\Plugin\API
{
private $dashboard = null;
protected function __construct()
{
$this->dashboard = new Dashboard();
}
/**
* Get each dashboard that belongs to a user including the containing widgets that are placed within each dashboard.
* If the user has not created any dashboard yet, the default dashboard will be returned.
*
* @return array[]
*/
public function getDashboards()
{
$dashboards = $this->getUserDashboards();
if (empty($dashboards)) {
$dashboards = array($this->getDefaultDashboard());
}
return $dashboards;
}
/**
* Get the default dashboard.
*
* @return array[]
*/
private function getDefaultDashboard()
{
$defaultLayout = $this->dashboard->getDefaultLayout();
$defaultLayout = $this->dashboard->decodeLayout($defaultLayout);
$defaultDashboard = array('name' => Piwik::translate('Dashboard_Dashboard'), 'layout' => $defaultLayout);
$widgets = $this->getExistingWidgetsWithinDashboard($defaultDashboard);
return $this->buildDashboard($defaultDashboard, $widgets);
}
/**
* Get all dashboards which a user has created.
*
* @return array[]
*/
private function getUserDashboards()
{
$userLogin = Piwik::getCurrentUserLogin();
$userDashboards = $this->dashboard->getAllDashboards($userLogin);
$dashboards = array();
foreach ($userDashboards as $userDashboard) {
if ($this->hasDashboardColumns($userDashboard)) {
$widgets = $this->getExistingWidgetsWithinDashboard($userDashboard);
$dashboards[] = $this->buildDashboard($userDashboard, $widgets);
}
}
return $dashboards;
}
private function getExistingWidgetsWithinDashboard($dashboard)
{
$columns = $this->getColumnsFromDashboard($dashboard);
$widgets = array();
$columns = array_filter($columns);
foreach ($columns as $column) {
foreach ($column as $widget) {
if ($this->widgetIsNotHidden($widget) && $this->widgetExists($widget)) {
$module = $widget->parameters->module;
$action = $widget->parameters->action;
$widgets[] = array('module' => $module, 'action' => $action);
}
}
}
return $widgets;
}
private function getColumnsFromDashboard($dashboard)
{
if (is_array($dashboard['layout'])) {
return $dashboard['layout'];
}
return $dashboard['layout']->columns;
}
private function hasDashboardColumns($dashboard)
{
if (is_array($dashboard['layout'])) {
return !empty($dashboard['layout']);
}
return !empty($dashboard['layout']->columns);
}
private function buildDashboard($dashboard, $widgets)
{
return array('name' => $dashboard['name'], 'widgets' => $widgets);
}
private function widgetExists($widget)
{
if (empty($widget->parameters)) {
return false;
}
$module = $widget->parameters->module;
$action = $widget->parameters->action;
return WidgetsList::isDefined($module, $action);
}
private function widgetIsNotHidden($widget)
{
return empty($widget->isHidden);
}
}

View file

@ -0,0 +1,345 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\Dashboard;
use Piwik\Common;
use Piwik\DataTable\Renderer\Json;
use Piwik\Db;
use Piwik\Piwik;
use Piwik\Session\SessionNamespace;
use Piwik\View;
use Piwik\WidgetsList;
/**
* Dashboard Controller
*
*/
class Controller extends \Piwik\Plugin\Controller
{
/**
* @var Dashboard
*/
private $dashboard;
protected function init()
{
parent::init();
$this->dashboard = new Dashboard();
}
protected function _getDashboardView($template)
{
$view = new View($template);
$this->setGeneralVariablesView($view);
$view->availableWidgets = Common::json_encode(WidgetsList::get());
$view->availableLayouts = $this->getAvailableLayouts();
$view->dashboardId = Common::getRequestVar('idDashboard', 1, 'int');
$view->dashboardLayout = $this->getLayout($view->dashboardId);
return $view;
}
public function embeddedIndex()
{
$view = $this->_getDashboardView('@Dashboard/embeddedIndex');
return $view->render();
}
public function index()
{
$view = $this->_getDashboardView('@Dashboard/index');
$view->dashboardSettingsControl = new DashboardManagerControl();
$view->dashboards = array();
if (!Piwik::isUserIsAnonymous()) {
$login = Piwik::getCurrentUserLogin();
$view->dashboards = $this->dashboard->getAllDashboards($login);
}
return $view->render();
}
public function getDashboardSettingsControl()
{
$view = new DashboardManagerControl();
$result = $view->render();
if (Common::getRequestVar('includeWidgetFactory', false)) {
$factoryTemplateView = new View("@Dashboard/_widgetFactoryTemplate");
$result .= $factoryTemplateView->render();
}
return $result;
}
public function getAvailableWidgets()
{
$this->checkTokenInUrl();
Json::sendHeaderJSON();
return Common::json_encode(WidgetsList::get());
}
public function getDashboardLayout()
{
$this->checkTokenInUrl();
$idDashboard = Common::getRequestVar('idDashboard', 1, 'int');
$layout = $this->getLayout($idDashboard);
return $layout;
}
/**
* Resets the dashboard to the default widget configuration
*/
public function resetLayout()
{
$this->checkTokenInUrl();
$layout = $this->dashboard->getDefaultLayout();
$idDashboard = Common::getRequestVar('idDashboard', 1, 'int');
if (Piwik::isUserIsAnonymous()) {
$session = new SessionNamespace("Dashboard");
$session->dashboardLayout = $layout;
$session->setExpirationSeconds(1800);
} else {
$this->saveLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout);
}
}
/**
* Records the layout in the DB for the given user.
*
* @param string $login
* @param int $idDashboard
* @param string $layout
*/
protected function saveLayoutForUser($login, $idDashboard, $layout)
{
$paramsBind = array($login, $idDashboard, $layout, $layout);
$query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
Common::prefixTable('user_dashboard'));
Db::query($query, $paramsBind);
}
/**
* Updates the name of a dashboard
*
* @param string $login
* @param int $idDashboard
* @param string $name
*/
protected function updateDashboardName($login, $idDashboard, $name)
{
$paramsBind = array($name, $login, $idDashboard);
$query = sprintf('UPDATE %s SET name = ? WHERE login = ? AND iddashboard = ?',
Common::prefixTable('user_dashboard'));
Db::query($query, $paramsBind);
}
/**
* Removes the dashboard with the given id
*/
public function removeDashboard()
{
$this->checkTokenInUrl();
if (Piwik::isUserIsAnonymous()) {
return;
}
$idDashboard = Common::getRequestVar('idDashboard', 1, 'int');
// first layout can't be removed
if ($idDashboard != 1) {
$query = sprintf('DELETE FROM %s WHERE iddashboard = ? AND login = ?',
Common::prefixTable('user_dashboard'));
Db::query($query, array($idDashboard, Piwik::getCurrentUserLogin()));
}
}
/**
* Outputs all available dashboards for the current user as a JSON string
*/
public function getAllDashboards()
{
$this->checkTokenInUrl();
if (Piwik::isUserIsAnonymous()) {
Json::sendHeaderJSON();
return '[]';
}
$login = Piwik::getCurrentUserLogin();
$dashboards = $this->dashboard->getAllDashboards($login);
Json::sendHeaderJSON();
return Common::json_encode($dashboards);
}
/**
* Creates a new dashboard for the current user
* User needs to be logged in
*/
public function createNewDashboard()
{
$this->checkTokenInUrl();
if (Piwik::isUserIsAnonymous()) {
return '0';
}
$user = Piwik::getCurrentUserLogin();
$nextId = $this->getNextIdDashboard($user);
$name = urldecode(Common::getRequestVar('name', '', 'string'));
$type = urldecode(Common::getRequestVar('type', 'default', 'string'));
$layout = '{}';
if ($type == 'default') {
$layout = $this->dashboard->getDefaultLayout();
}
$query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)',
Common::prefixTable('user_dashboard'));
Db::query($query, array($user, $nextId, $name, $layout));
Json::sendHeaderJSON();
return Common::json_encode($nextId);
}
private function getNextIdDashboard($login)
{
$nextIdQuery = sprintf('SELECT MAX(iddashboard)+1 FROM %s WHERE login = ?',
Common::prefixTable('user_dashboard'));
$nextId = Db::fetchOne($nextIdQuery, array($login));
if (empty($nextId)) {
$nextId = 1;
return $nextId;
}
return $nextId;
}
public function copyDashboardToUser()
{
$this->checkTokenInUrl();
if (!Piwik::hasUserSuperUserAccess()) {
return '0';
}
$login = Piwik::getCurrentUserLogin();
$name = urldecode(Common::getRequestVar('name', '', 'string'));
$user = urldecode(Common::getRequestVar('user', '', 'string'));
$idDashboard = Common::getRequestVar('dashboardId', 0, 'int');
$layout = $this->dashboard->getLayoutForUser($login, $idDashboard);
if ($layout !== false) {
$nextId = $this->getNextIdDashboard($user);
$query = sprintf('INSERT INTO %s (login, iddashboard, name, layout) VALUES (?, ?, ?, ?)',
Common::prefixTable('user_dashboard'));
Db::query($query, array($user, $nextId, $name, $layout));
Json::sendHeaderJSON();
return Common::json_encode($nextId);
}
}
/**
* Saves the layout for the current user
* anonymous = in the session
* authenticated user = in the DB
*/
public function saveLayout()
{
$this->checkTokenInUrl();
$layout = Common::unsanitizeInputValue(Common::getRequestVar('layout'));
$idDashboard = Common::getRequestVar('idDashboard', 1, 'int');
$name = Common::getRequestVar('name', '', 'string');
if (Piwik::isUserIsAnonymous()) {
$session = new SessionNamespace("Dashboard");
$session->dashboardLayout = $layout;
$session->setExpirationSeconds(1800);
} else {
$this->saveLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard, $layout);
if (!empty($name)) {
$this->updateDashboardName(Piwik::getCurrentUserLogin(), $idDashboard, $name);
}
}
}
/**
* Saves the layout as default
*/
public function saveLayoutAsDefault()
{
$this->checkTokenInUrl();
if (Piwik::hasUserSuperUserAccess()) {
$layout = Common::unsanitizeInputValue(Common::getRequestVar('layout'));
$paramsBind = array('', '1', $layout, $layout);
$query = sprintf('INSERT INTO %s (login, iddashboard, layout) VALUES (?,?,?) ON DUPLICATE KEY UPDATE layout=?',
Common::prefixTable('user_dashboard'));
Db::query($query, $paramsBind);
}
}
/**
* Get the dashboard layout for the current user (anonymous or logged user)
*
* @param int $idDashboard
*
* @return string $layout
*/
protected function getLayout($idDashboard)
{
if (Piwik::isUserIsAnonymous()) {
$session = new SessionNamespace("Dashboard");
if (!isset($session->dashboardLayout)) {
return $this->dashboard->getDefaultLayout();
}
$layout = $session->dashboardLayout;
} else {
$layout = $this->dashboard->getLayoutForUser(Piwik::getCurrentUserLogin(), $idDashboard);
}
if (!empty($layout)) {
$layout = $this->dashboard->removeDisabledPluginFromLayout($layout);
}
if (empty($layout)) {
$layout = $this->dashboard->getDefaultLayout();
}
return $layout;
}
/**
* Returns all available column layouts for the dashboard
*
* @return array
*/
protected function getAvailableLayouts()
{
return array(
array(100),
array(50, 50), array(67, 33), array(33, 67),
array(33, 33, 33), array(40, 30, 30), array(30, 40, 30), array(30, 30, 40),
array(25, 25, 25, 25)
);
}
}

View file

@ -0,0 +1,278 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*
*/
namespace Piwik\Plugins\Dashboard;
use Exception;
use Piwik\Common;
use Piwik\Db;
use Piwik\DbHelper;
use Piwik\Menu\MenuMain;
use Piwik\Menu\MenuTop;
use Piwik\Piwik;
use Piwik\Site;
use Piwik\WidgetsList;
/**
*/
class Dashboard extends \Piwik\Plugin
{
/**
* @see Piwik\Plugin::getListHooksRegistered
*/
public function getListHooksRegistered()
{
return array(
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'UsersManager.deleteUser' => 'deleteDashboardLayout',
'Menu.Reporting.addItems' => 'addMenus',
'Menu.Top.addItems' => 'addTopMenu',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
);
}
/**
* Returns the layout in the DB for the given user, or false if the layout has not been set yet.
* Parameters must be checked BEFORE this function call
*
* @param string $login
* @param int $idDashboard
*
* @return bool|string
*/
public function getLayoutForUser($login, $idDashboard)
{
$paramsBind = array($login, $idDashboard);
$query = sprintf('SELECT layout FROM %s WHERE login = ? AND iddashboard = ?',
Common::prefixTable('user_dashboard'));
$return = Db::fetchAll($query, $paramsBind);
if (count($return) == 0) {
return false;
}
return $return[0]['layout'];
}
public function getDefaultLayout()
{
$defaultLayout = $this->getLayoutForUser('', 1);
if (empty($defaultLayout)) {
if (Piwik::hasUserSuperUserAccess()) {
$topWidget = '{"uniqueId":"widgetCoreHomegetDonateForm",'
. '"parameters":{"module":"CoreHome","action":"getDonateForm"}},';
} else {
$topWidget = '{"uniqueId":"widgetCoreHomegetPromoVideo",'
. '"parameters":{"module":"CoreHome","action":"getPromoVideo"}},';
}
$defaultLayout = '[
[
{"uniqueId":"widgetVisitsSummarygetEvolutionGraphcolumnsArray","parameters":{"module":"VisitsSummary","action":"getEvolutionGraph","columns":"nb_visits"}},
{"uniqueId":"widgetLivewidget","parameters":{"module":"Live","action":"widget"}},
{"uniqueId":"widgetVisitorInterestgetNumberOfVisitsPerVisitDuration","parameters":{"module":"VisitorInterest","action":"getNumberOfVisitsPerVisitDuration"}}
],
[
' . $topWidget . '
{"uniqueId":"widgetReferrersgetKeywords","parameters":{"module":"Referrers","action":"getKeywords"}},
{"uniqueId":"widgetReferrersgetWebsites","parameters":{"module":"Referrers","action":"getWebsites"}}
],
[
{"uniqueId":"widgetUserCountryMapvisitorMap","parameters":{"module":"UserCountryMap","action":"visitorMap"}},
{"uniqueId":"widgetUserSettingsgetBrowser","parameters":{"module":"UserSettings","action":"getBrowser"}},
{"uniqueId":"widgetReferrersgetSearchEngines","parameters":{"module":"Referrers","action":"getSearchEngines"}},
{"uniqueId":"widgetVisitTimegetVisitInformationPerServerTime","parameters":{"module":"VisitTime","action":"getVisitInformationPerServerTime"}},
{"uniqueId":"widgetExampleRssWidgetrssPiwik","parameters":{"module":"ExampleRssWidget","action":"rssPiwik"}}
]
]';
}
$defaultLayout = $this->removeDisabledPluginFromLayout($defaultLayout);
return $defaultLayout;
}
public function getAllDashboards($login)
{
$dashboards = Db::fetchAll('SELECT iddashboard, name, layout
FROM ' . Common::prefixTable('user_dashboard') .
' WHERE login = ? ORDER BY iddashboard', array($login));
$nameless = 1;
foreach ($dashboards AS &$dashboard) {
if (empty($dashboard['name'])) {
$dashboard['name'] = Piwik::translate('Dashboard_DashboardOf', $login);
if ($nameless > 1) {
$dashboard['name'] .= " ($nameless)";
}
$nameless++;
}
$dashboard['name'] = Common::unsanitizeInputValue($dashboard['name']);
$layout = '[]';
if (!empty($dashboard['layout'])) {
$layout = $dashboard['layout'];
}
$dashboard['layout'] = $this->decodeLayout($layout);
}
return $dashboards;
}
private function isAlreadyDecodedLayout($layout)
{
return !is_string($layout);
}
public function removeDisabledPluginFromLayout($layout)
{
$layoutObject = $this->decodeLayout($layout);
// if the json decoding works (ie. new Json format)
// we will only return the widgets that are from enabled plugins
if (is_array($layoutObject)) {
$layoutObject = (object)array(
'config' => array('layout' => '33-33-33'),
'columns' => $layoutObject
);
}
if (empty($layoutObject) || empty($layoutObject->columns)) {
$layoutObject = (object)array(
'config' => array('layout' => '33-33-33'),
'columns' => array()
);
}
foreach ($layoutObject->columns as &$row) {
if (!is_array($row)) {
$row = array();
continue;
}
foreach ($row as $widgetId => $widget) {
if (isset($widget->parameters->module)) {
$controllerName = $widget->parameters->module;
$controllerAction = $widget->parameters->action;
if (!WidgetsList::isDefined($controllerName, $controllerAction)) {
unset($row[$widgetId]);
}
} else {
unset($row[$widgetId]);
}
}
}
$layout = $this->encodeLayout($layoutObject);
return $layout;
}
public function decodeLayout($layout)
{
if ($this->isAlreadyDecodedLayout($layout)) {
return $layout;
}
$layout = html_entity_decode($layout);
$layout = str_replace("\\\"", "\"", $layout);
$layout = str_replace("\n", "", $layout);
return Common::json_decode($layout, $assoc = false);
}
public function encodeLayout($layout)
{
return Common::json_encode($layout);
}
public function addMenus()
{
MenuMain::getInstance()->add('Dashboard_Dashboard', '', array('module' => 'Dashboard', 'action' => 'embeddedIndex', 'idDashboard' => 1), true, 5);
if (!Piwik::isUserIsAnonymous()) {
$login = Piwik::getCurrentUserLogin();
$dashboards = $this->getAllDashboards($login);
$pos = 0;
foreach ($dashboards as $dashboard) {
MenuMain::getInstance()->add('Dashboard_Dashboard', $dashboard['name'], array('module' => 'Dashboard', 'action' => 'embeddedIndex', 'idDashboard' => $dashboard['iddashboard']), true, $pos);
$pos++;
}
}
}
public function addTopMenu()
{
$tooltip = false;
try {
$idSite = Common::getRequestVar('idSite');
$tooltip = Piwik::translate('Dashboard_TopLinkTooltip', Site::getNameFor($idSite));
} catch (Exception $ex) {
// if no idSite parameter, show no tooltip
}
$urlParams = array('module' => 'CoreHome', 'action' => 'index');
MenuTop::addEntry('Dashboard_Dashboard', $urlParams, true, 1, $isHTML = false, $tooltip);
}
public function getJsFiles(&$jsFiles)
{
$jsFiles[] = "plugins/Dashboard/javascripts/widgetMenu.js";
$jsFiles[] = "libs/javascript/json2.js";
$jsFiles[] = "plugins/Dashboard/javascripts/dashboardObject.js";
$jsFiles[] = "plugins/Dashboard/javascripts/dashboardWidget.js";
$jsFiles[] = "plugins/Dashboard/javascripts/dashboard.js";
}
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/CoreHome/stylesheets/dataTable.less";
$stylesheets[] = "plugins/Dashboard/stylesheets/dashboard.less";
}
public function deleteDashboardLayout($userLogin)
{
Db::query('DELETE FROM ' . Common::prefixTable('user_dashboard') . ' WHERE login = ?', array($userLogin));
}
public function install()
{
$dashboard = "login VARCHAR( 100 ) NOT NULL ,
iddashboard INT NOT NULL ,
name VARCHAR( 100 ) NULL DEFAULT NULL ,
layout TEXT NOT NULL,
PRIMARY KEY ( login , iddashboard )";
DbHelper::createTable('user_dashboard', $dashboard);
}
public function uninstall()
{
Db::dropTables(Common::prefixTable('user_dashboard'));
}
public function getClientSideTranslationKeys(&$translationKeys)
{
$translationKeys[] = 'Dashboard_AddPreviewedWidget';
$translationKeys[] = 'Dashboard_WidgetPreview';
$translationKeys[] = 'Dashboard_Maximise';
$translationKeys[] = 'Dashboard_Minimise';
$translationKeys[] = 'Dashboard_LoadingWidget';
$translationKeys[] = 'Dashboard_WidgetNotFound';
$translationKeys[] = 'Dashboard_DashboardCopied';
$translationKeys[] = 'General_Close';
$translationKeys[] = 'General_Refresh';
}
}

View file

@ -0,0 +1,53 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\Dashboard;
use Piwik\View\UIControl;
/**
* Generates the HTML for the dashboard manager control.
*/
class DashboardManagerControl extends DashboardSettingsControlBase
{
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->jsClass = "DashboardManagerControl";
$this->cssIdentifier = "dashboard-manager";
$this->addDashboardActions();
$this->addGeneralActions();
}
private function addDashboardActions()
{
$this->dashboardActions['resetDashboard'] = 'Dashboard_ResetDashboard';
$this->dashboardActions['showChangeDashboardLayoutDialog'] = 'Dashboard_ChangeDashboardLayout';
if ($this->userLogin && $this->userLogin != 'anonymous') {
$this->dashboardActions['renameDashboard'] = 'Dashboard_RenameDashboard';
$this->dashboardActions['removeDashboard'] = 'Dashboard_RemoveDashboard';
if ($this->isSuperUser) {
$this->dashboardActions['setAsDefaultWidgets'] = 'Dashboard_SetAsDefaultWidgets';
$this->dashboardActions['copyDashboardToUser'] = 'Dashboard_CopyDashboardToUser';
}
}
}
private function addGeneralActions()
{
if ($this->userLogin && $this->userLogin != 'anonymous') {
$this->generalActions['createDashboard'] = 'Dashboard_CreateNewDashboard';
}
}
}

View file

@ -0,0 +1,30 @@
<?php
/**
* Piwik - Open source web analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
namespace Piwik\Plugins\Dashboard;
use Piwik\View\UIControl;
/**
* Generates the HTML for the dashboard manager control.
*/
abstract class DashboardSettingsControlBase extends UIControl
{
const TEMPLATE = "@Dashboard/_dashboardSettings";
/**
* Constructor.
*/
public function __construct()
{
parent::__construct();
$this->cssClass = "piwikTopControl dashboardSettings";
$this->dashboardActions = array();
$this->generalActions = array();
}
}

View file

@ -0,0 +1,306 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
function initDashboard(dashboardId, dashboardLayout) {
$('.dashboardSettings').show();
// Embed dashboard
if (!$('#topBars').length) {
$('.dashboardSettings').after($('#Dashboard'));
$('#Dashboard_embeddedIndex_' + dashboardId).addClass('sfHover');
}
widgetsHelper.getAvailableWidgets();
$('#dashboardWidgetsArea')
.on('dashboardempty', showEmptyDashboardNotification)
.dashboard({
idDashboard: dashboardId,
layout: dashboardLayout
});
$('#columnPreview').find('>div').each(function () {
var width = [];
$('div', this).each(function () {
width.push(this.className.replace(/width-/, ''));
});
$(this).attr('layout', width.join('-'));
});
$('#columnPreview').find('>div').on('click', function () {
$('#columnPreview').find('>div').removeClass('choosen');
$(this).addClass('choosen');
});
}
function createDashboard() {
$('#createDashboardName').val('');
piwikHelper.modalConfirm('#createDashboardConfirm', {yes: function () {
var dashboardName = $('#createDashboardName').val();
var type = ($('#dashboard_type_empty:checked').length > 0) ? 'empty' : 'default';
var ajaxRequest = new ajaxHelper();
ajaxRequest.setLoadingElement();
ajaxRequest.addParams({
module: 'Dashboard',
action: 'createNewDashboard'
}, 'get');
ajaxRequest.addParams({
name: encodeURIComponent(dashboardName),
type: type
}, 'post');
ajaxRequest.setCallback(
function (id) {
$('#dashboardWidgetsArea').dashboard('loadDashboard', id);
}
);
ajaxRequest.send(true);
}});
}
function resetDashboard() {
piwikHelper.modalConfirm('#resetDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); }});
}
function renameDashboard() {
$('#newDashboardName').val($('#dashboardWidgetsArea').dashboard('getDashboardName'));
piwikHelper.modalConfirm('#renameDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('setDashboardName', $('#newDashboardName').val()); }});
}
function removeDashboard() {
$('#removeDashboardConfirm').find('h2 span').text($('#dashboardWidgetsArea').dashboard('getDashboardName'));
piwikHelper.modalConfirm('#removeDashboardConfirm', {yes: function () { $('#dashboardWidgetsArea').dashboard('removeDashboard'); }});
}
function showChangeDashboardLayoutDialog() {
$('#columnPreview').find('>div').removeClass('choosen');
$('#columnPreview').find('>div[layout=' + $('#dashboardWidgetsArea').dashboard('getColumnLayout') + ']').addClass('choosen');
piwikHelper.modalConfirm('#changeDashboardLayout', {yes: function () {
$('#dashboardWidgetsArea').dashboard('setColumnLayout', $('#changeDashboardLayout').find('.choosen').attr('layout'));
}});
}
function showEmptyDashboardNotification() {
piwikHelper.modalConfirm('#dashboardEmptyNotification', {
resetDashboard: function () { $('#dashboardWidgetsArea').dashboard('resetLayout'); },
addWidget: function () { $('.dashboardSettings').trigger('click'); }
});
}
function setAsDefaultWidgets() {
piwikHelper.modalConfirm('#setAsDefaultWidgetsConfirm', {
yes: function () { $('#dashboardWidgetsArea').dashboard('saveLayoutAsDefaultWidgetLayout'); }
});
}
function copyDashboardToUser() {
$('#copyDashboardName').val($('#dashboardWidgetsArea').dashboard('getDashboardName'));
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'API',
method: 'UsersManager.getUsers',
format: 'json'
}, 'get');
ajaxRequest.setCallback(
function (availableUsers) {
$('#copyDashboardUser').empty();
$('#copyDashboardUser').append(
$('<option></option>').val(piwik.userLogin).text(piwik.userLogin)
);
$.each(availableUsers, function (index, user) {
if (user.login != 'anonymous' && user.login != piwik.userLogin) {
$('#copyDashboardUser').append(
$('<option></option>').val(user.login).text(user.login + ' (' + user.alias + ')')
);
}
});
}
);
ajaxRequest.send(true);
piwikHelper.modalConfirm('#copyDashboardToUserConfirm', {
yes: function () {
var copyDashboardName = $('#copyDashboardName').val();
var copyDashboardUser = $('#copyDashboardUser').val();
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Dashboard',
action: 'copyDashboardToUser'
}, 'get');
ajaxRequest.addParams({
name: encodeURIComponent(copyDashboardName),
dashboardId: $('#dashboardWidgetsArea').dashboard('getDashboardId'),
user: encodeURIComponent(copyDashboardUser)
}, 'post');
ajaxRequest.setCallback(
function (id) {
$('#alert').find('h2').text(_pk_translate('Dashboard_DashboardCopied'));
piwikHelper.modalConfirm('#alert', {});
}
);
ajaxRequest.send(true);
}
});
}
(function () {
var exports = window.require('piwik/UI');
var UIControl = exports.UIControl;
/**
* Contains logic common to all dashboard management controls. This is the JavaScript analog of
* the DashboardSettingsControlBase PHP class.
*
* @param {Element} element The HTML element generated by the SegmentSelectorControl PHP class. Should
* have the CSS class 'segmentEditorPanel'.
* @constructor
*/
var DashboardSettingsControlBase = function (element) {
UIControl.call(this, element);
// on menu item click, trigger action event on this
var self = this;
this.$element.on('click', 'ul.submenu li[data-action]', function (e) {
self.$element.toggleClass('visible');
$(self).trigger($(this).attr('data-action'));
});
// open manager on open
this.$element.on('click', function (e) {
if ($(e.target).is('.dashboardSettings,.dashboardSettings>span')) {
self.$element.toggleClass('visible');
// fix position
self.$element
.find('.widgetpreview-widgetlist')
.css('paddingTop', self.$element.find('.widgetpreview-categorylist').parent('li').position().top);
self.onOpen();
}
});
// handle manager close
this.onBodyMouseUp = function (e) {
if (!$(e.target).closest('.dashboardSettings').length
&& !$(e.target).is('.dashboardSettings')
) {
self.$element.widgetPreview('reset');
self.$element.removeClass('visible');
}
};
$('body').on('mouseup', this.onBodyMouseUp);
// setup widgetPreview
this.$element.widgetPreview({
isWidgetAvailable: function (widgetUniqueId) {
return self.isWidgetAvailable(widgetUniqueId);
},
onSelect: function (widgetUniqueId) {
var widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
self.$element.removeClass('visible');
self.widgetSelected(widget);
},
resetOnSelect: true
});
// on enter widget list category, reset widget preview
this.$element.on('mouseenter', '.submenu > li', function (event) {
if (!$('.widgetpreview-categorylist', event.target).length) {
self.$element.widgetPreview('reset');
}
});
};
$.extend(DashboardSettingsControlBase.prototype, UIControl.prototype, {
_destroy: function () {
UIControl.prototype._destroy.call(this);
$('body').off('mouseup', null, this.onBodyMouseUp);
}
});
exports.DashboardSettingsControlBase = DashboardSettingsControlBase;
/**
* Sets up and handles events for the dashboard manager control.
*
* @param {Element} element The HTML element generated by the SegmentSelectorControl PHP class. Should
* have the CSS class 'segmentEditorPanel'.
* @constructor
*/
var DashboardManagerControl = function (element) {
DashboardSettingsControlBase.call(this, element);
$(this).on('resetDashboard', function () {
this.hide();
resetDashboard();
});
$(this).on('showChangeDashboardLayoutDialog', function () {
this.hide();
showChangeDashboardLayoutDialog();
});
$(this).on('renameDashboard', function () {
this.hide();
renameDashboard();
});
$(this).on('removeDashboard', function () {
this.hide();
removeDashboard();
});
$(this).on('setAsDefaultWidgets', function () {
this.hide();
setAsDefaultWidgets();
});
$(this).on('copyDashboardToUser', function () {
this.hide();
copyDashboardToUser();
});
$(this).on('createDashboard', function () {
this.hide();
createDashboard();
});
};
$.extend(DashboardManagerControl.prototype, DashboardSettingsControlBase.prototype, {
onOpen: function () {
if ($('#dashboardWidgetsArea').dashboard('isDefaultDashboard')) {
$('.removeDashboardLink', this.$element).hide();
} else {
$('.removeDashboardLink', this.$element).show();
}
},
hide: function () {
this.$element.removeClass('visible');
},
isWidgetAvailable: function (widgetUniqueId) {
return !$('#dashboardWidgetsArea').find('[widgetId=' + widgetUniqueId + ']').length;
},
widgetSelected: function (widget) {
$('#dashboardWidgetsArea').dashboard('addWidget', widget.uniqueId, 1, widget.parameters, true, false);
}
});
DashboardManagerControl.initElements = function () {
UIControl.initElements(this, '.dashboard-manager');
};
exports.DashboardManagerControl = DashboardManagerControl;
}());

View file

@ -0,0 +1,577 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function ($) {
/**
* Current dashboard column layout
* @type {object}
*/
var dashboardLayout = {};
/**
* Id of current dashboard
* @type {int}
*/
var dashboardId = 1;
/**
* Name of current dashboard
* @type {string}
*/
var dashboardName = '';
/**
* Holds a reference to the dashboard element
* @type {object}
*/
var dashboardElement = null;
/**
* Boolean indicating wether the layout config has been changed or not
* @type {boolean}
*/
var dashboardChanged = false;
/**
* public methods of dashboard plugin
* all methods defined here are accessible with $(selector).dashboard('method', param, param, ...)
*/
var methods = {
/**
* creates a dashboard object
*
* @param {object} options
*/
init: function (options) {
dashboardElement = this;
if (options.idDashboard) {
dashboardId = options.idDashboard;
}
if (options.name) {
dashboardName = options.name;
}
if (options.layout) {
generateLayout(options.layout);
buildMenu();
} else {
methods.loadDashboard.apply(this, [dashboardId]);
}
return this;
},
/**
* Destroys the dashboard object and all its childrens
*
* @return void
*/
destroy: function () {
$(dashboardElement).remove();
dashboardElement = null;
var widgets = $('[widgetId]');
for (var i = 0; i < widgets.length; i++) {
$(widgets[i]).dashboardWidget('destroy');
}
},
/**
* Load dashboard with the given id
*
* @param {int} dashboardIdToLoad
*/
loadDashboard: function (dashboardIdToLoad) {
$(dashboardElement).empty();
dashboardName = '';
dashboardLayout = null;
dashboardId = dashboardIdToLoad;
piwikHelper.showAjaxLoading();
broadcast.updateHashOnly = true;
broadcast.propagateAjax('?idDashboard=' + dashboardIdToLoad);
fetchLayout(generateLayout);
buildMenu();
return this;
},
/**
* Change current column layout to the given one
*
* @param {String} newLayout
*/
setColumnLayout: function (newLayout) {
adjustDashboardColumns(newLayout);
},
/**
* Returns the current column layout
*
* @return {String}
*/
getColumnLayout: function () {
return dashboardLayout.config.layout;
},
/**
* Return the current dashboard name
*
* @return {String}
*/
getDashboardName: function () {
return dashboardName;
},
/**
* Return the current dashboard id
*
* @return {int}
*/
getDashboardId: function () {
return dashboardId;
},
/**
* Sets a new name for the current dashboard
*
* @param {String} newName
*/
setDashboardName: function (newName) {
dashboardName = newName;
dashboardChanged = true;
saveLayout();
},
/**
* Adds a new widget to the dashboard
*
* @param {String} uniqueId
* @param {int} columnNumber
* @param {object} widgetParameters
* @param {boolean} addWidgetOnTop
* @param {boolean} isHidden
*/
addWidget: function (uniqueId, columnNumber, widgetParameters, addWidgetOnTop, isHidden) {
addWidgetTemplate(uniqueId, columnNumber, widgetParameters, addWidgetOnTop, isHidden);
reloadWidget(uniqueId);
saveLayout();
},
/**
* Resets the current layout to the defaults
*/
resetLayout: function () {
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Dashboard',
action: 'resetLayout',
idDashboard: dashboardId
}, 'get');
ajaxRequest.setCallback(
function () {
methods.loadDashboard.apply(this, [dashboardId])
}
);
ajaxRequest.setLoadingElement();
ajaxRequest.setFormat('html');
ajaxRequest.send(true);
},
/**
* Removes the current dashboard
*/
removeDashboard: function () {
removeDashboard();
},
/**
* Saves the current layout aus new default widget layout
*/
saveLayoutAsDefaultWidgetLayout: function () {
saveLayout('saveLayoutAsDefault');
},
/**
* Returns if the current loaded dashboard is the default dashboard
*/
isDefaultDashboard: function () {
return (dashboardId == 1);
}
};
/**
* Generates the dashboard out of the given layout
*
* @param {object|string} layout
*/
function generateLayout(layout) {
dashboardLayout = parseLayout(layout);
piwikHelper.hideAjaxLoading();
adjustDashboardColumns(dashboardLayout.config.layout);
var dashboardContainsWidgets = false;
for (var column = 0; column < dashboardLayout.columns.length; column++) {
for (var i in dashboardLayout.columns[column]) {
if (typeof dashboardLayout.columns[column][i] != 'object') {
// Fix IE8 bug: the "i in" loop contains i="indexOf", which would yield type function.
// If we would continue with i="indexOf", an invalid widget would be created.
continue;
}
var widget = dashboardLayout.columns[column][i];
dashboardContainsWidgets = true;
addWidgetTemplate(widget.uniqueId, column + 1, widget.parameters, false, widget.isHidden)
}
}
if (!dashboardContainsWidgets) {
$(dashboardElement).trigger('dashboardempty');
}
makeWidgetsSortable();
}
/**
* Fetches the layout for the currently set dashboard id
* and passes the response to given callback function
*
* @param {function} callback
*/
function fetchLayout(callback) {
globalAjaxQueue.abort();
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Dashboard',
action: 'getDashboardLayout',
idDashboard: dashboardId
}, 'get');
ajaxRequest.setCallback(callback);
ajaxRequest.send(false);
}
/**
* Adjust the dashboard columns to fit the new layout
* removes or adds new columns if needed and sets the column sizes.
*
* @param {String} layout new layout in format xx-xx-xx
* @return {void}
*/
function adjustDashboardColumns(layout) {
var columnWidth = layout.split('-');
var columnCount = columnWidth.length;
var currentCount = $('.col', dashboardElement).length;
if (currentCount < columnCount) {
$('.menuClear', dashboardElement).remove();
for (var i = currentCount; i < columnCount; i++) {
if (dashboardLayout.columns.length < i) {
dashboardLayout.columns.push({});
}
$(dashboardElement).append('<div class="col"> </div>');
}
$(dashboardElement).append('<div class="menuClear"> </div>');
} else if (currentCount > columnCount) {
for (var i = columnCount; i < currentCount; i++) {
if (dashboardLayout.columns.length >= i) {
dashboardLayout.columns.pop();
}
// move widgets to other columns depending on columns height
$('[widgetId]', $('.col:last')).each(function (id, elem) {
var cols = $('.col').slice(0, columnCount);
var smallestColumn = $(cols[0]);
var smallestColumnHeight = null;
cols.each(function (colId, col) {
if (smallestColumnHeight == null || smallestColumnHeight > $(col).height()) {
smallestColumnHeight = $(col).height();
smallestColumn = $(col);
}
});
$(elem).appendTo(smallestColumn);
});
$('.col:last').remove();
}
}
for (var i = 0; i < columnCount; i++) {
$('.col', dashboardElement)[i].className = 'col width-' + columnWidth[i];
}
makeWidgetsSortable();
// if dashboard column count is changed (not on initial load)
if (currentCount > 0 && dashboardLayout.config.layout != layout) {
dashboardChanged = true;
dashboardLayout.config.layout = layout;
saveLayout();
}
// trigger resize event on all widgets
$('.widgetContent').each(function () {
$(this).trigger('widget:resize');
});
}
/**
* Returns the given layout as an layout object
* Used to parse old layout format into the new syntax
*
* @param {object} layout layout object or string
* @return {object}
*/
function parseLayout(layout) {
// Handle layout array used in piwik before 1.7
// column count was always 3, so use layout 33-33-33 as default
if ($.isArray(layout)) {
layout = {
config: {layout: '33-33-33'},
columns: layout
};
}
if (!layout.config.layout) {
layout.config.layout = '33-33-33';
}
return layout;
}
/**
* Reloads the widget with the given uniqueId
*
* @param {String} uniqueId
*/
function reloadWidget(uniqueId) {
$('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget('reload', false, true);
}
/**
* Adds an empty widget template to the dashboard in the given column
* @param {String} uniqueId
* @param {int} columnNumber
* @param {object} widgetParameters
* @param {boolean} addWidgetOnTop
* @param {boolean} isHidden
*/
function addWidgetTemplate(uniqueId, columnNumber, widgetParameters, addWidgetOnTop, isHidden) {
if (!columnNumber) {
columnNumber = 1;
}
// do not try to add widget if given column number is to high
if (columnNumber > $('.col', dashboardElement).length) {
return;
}
var widgetContent = '<div class="sortable" widgetId="' + uniqueId + '"></div>';
if (addWidgetOnTop) {
$('.col:nth-child(' + columnNumber + ')', dashboardElement).prepend(widgetContent);
} else {
$('.col:nth-child(' + columnNumber + ')', dashboardElement).append(widgetContent);
}
$('[widgetId=' + uniqueId + ']', dashboardElement).dashboardWidget({
uniqueId: uniqueId,
widgetParameters: widgetParameters,
onChange: function () {
saveLayout();
},
isHidden: isHidden
});
}
/**
* Make all widgets on the dashboard sortable
*/
function makeWidgetsSortable() {
function onStart(event, ui) {
if (!jQuery.support.noCloneEvent) {
$('object', this).hide();
}
}
function onStop(event, ui) {
$('object', this).show();
$('.widgetHover', this).removeClass('widgetHover');
$('.widgetTopHover', this).removeClass('widgetTopHover');
if ($('.widget:has(".piwik-graph")', ui.item).length) {
reloadWidget($('.widget', ui.item).attr('id'));
}
saveLayout();
}
//launch 'sortable' property on every dashboard widgets
$( "div.col:data('ui-sortable')", dashboardElement ).sortable('destroy');
$('div.col', dashboardElement)
.sortable({
items: 'div.sortable',
opacity: 0.6,
forceHelperSize: true,
forcePlaceholderSize: true,
placeholder: 'hover',
handle: '.widgetTop',
helper: 'clone',
start: onStart,
stop: onStop,
connectWith: 'div.col'
});
}
/**
* Handle clicks for menu items for choosing between available dashboards
*/
function buildMenu() {
var success = function (dashboards) {
var dashboardMenuList = $('#Dashboard').find('> ul');
var dashboardMenuListItems = dashboardMenuList.find('>li');
dashboardMenuListItems.filter(function () {
return $(this).attr('id').indexOf('Dashboard_embeddedIndex') == 0;
}).remove();
if (dashboards.length > 1
|| dashboardMenuListItems.length >= 1
) {
dashboardMenuList.show();
var items = [];
for (var i = 0; i < dashboards.length; i++) {
var $link = $('<a/>').attr('data-idDashboard', dashboards[i].iddashboard).text(dashboards[i].name);
var $li = $('<li/>').attr('id', 'Dashboard_embeddedIndex_' + dashboards[i].iddashboard)
.addClass('dashboardMenuItem').append($link);
items.push($li);
if (dashboards[i].iddashboard == dashboardId) {
dashboardName = dashboards[i].name;
$li.addClass('sfHover');
}
}
dashboardMenuList.prepend(items);
} else {
dashboardMenuList.hide();
}
dashboardMenuList.find('a[data-idDashboard]').click(function (e) {
e.preventDefault();
var idDashboard = $(this).attr('data-idDashboard');
if (typeof piwikMenu != 'undefined') {
piwikMenu.activateMenu('Dashboard', 'embeddedIndex');
}
$('#Dashboard ul li').removeClass('sfHover');
if ($(dashboardElement).length) {
$(dashboardElement).dashboard('loadDashboard', idDashboard);
} else {
broadcast.propagateAjax('module=Dashboard&action=embeddedIndex&idDashboard=' + idDashboard);
}
$(this).closest('li').addClass('sfHover');
});
};
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Dashboard',
action: 'getAllDashboards'
}, 'get');
ajaxRequest.setCallback(success);
ajaxRequest.send(false);
}
/**
* Save the current layout in database if it has changed
* @param {string} [action] action to perform (defaults to saveLayout)
*/
function saveLayout(action) {
var columns = [];
var columnNumber = 0;
$('.col').each(function () {
columns[columnNumber] = [];
var items = $('[widgetId]', this);
for (var j = 0; j < items.size(); j++) {
columns[columnNumber][j] = $(items[j]).dashboardWidget('getWidgetObject');
// Do not store segment in the dashboard layout
delete columns[columnNumber][j].parameters.segment;
}
columnNumber++;
});
if (JSON.stringify(dashboardLayout.columns) != JSON.stringify(columns) || dashboardChanged || action) {
dashboardLayout.columns = JSON.parse(JSON.stringify(columns));
columns = null;
if (!action) {
action = 'saveLayout';
}
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Dashboard',
action: action,
idDashboard: dashboardId
}, 'get');
ajaxRequest.addParams({
layout: JSON.stringify(dashboardLayout),
name: dashboardName
}, 'post');
ajaxRequest.setCallback(
function () {
if (dashboardChanged) {
dashboardChanged = false;
buildMenu();
}
}
);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
}
}
/**
* Removes the current dashboard
*/
function removeDashboard() {
if (dashboardId == 1) {
return; // dashboard with id 1 should never be deleted, as it is the default
}
var ajaxRequest = new ajaxHelper();
ajaxRequest.setLoadingElement();
ajaxRequest.addParams({
module: 'Dashboard',
action: 'removeDashboard',
idDashboard: dashboardId
}, 'get');
ajaxRequest.setCallback(
function () {
methods.loadDashboard.apply(this, [1]);
}
);
ajaxRequest.setFormat('html');
ajaxRequest.send(true);
}
/**
* Make plugin methods available
*/
$.fn.dashboard = function (method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
$.error('Method ' + method + ' does not exist on jQuery.dashboard');
}
}
})(jQuery);

View file

@ -0,0 +1,331 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
(function ($) {
$.widget('piwik.dashboardWidget', {
/**
* Boolean indicating wether the widget is currently maximised
* @type {Boolean}
*/
isMaximised: false,
/**
* Unique Id of the widget
* @type {String}
*/
uniqueId: null,
/**
* Object holding the widget parameters
* @type {Object}
*/
widgetParameters: {},
/**
* Options available for initialization
*/
options: {
uniqueId: null,
isHidden: false,
onChange: null,
widgetParameters: {},
title: null,
onRemove: null,
onRefresh: null,
onMaximise: null,
onMinimise: null,
autoMaximiseVisualizations: ['tableAllColumns', 'tableGoals']
},
/**
* creates a widget object
*/
_create: function () {
if (!this.options.uniqueId) {
piwikHelper.error('widgets can\'t be created without an uniqueId');
return;
} else {
this.uniqueId = this.options.uniqueId;
}
if (this.options.widgetParameters) {
this.widgetParameters = this.options.widgetParameters;
}
this._createDashboardWidget(this.uniqueId);
var self = this;
this.element.on('setParameters.dashboardWidget', function (e, params) { self.setParameters(params); });
this.reload(true, true);
},
/**
* Cleanup some events and dialog
* Called automatically upon removing the widgets domNode
*/
destroy: function () {
if (this.isMaximised) {
$('[widgetId=' + this.uniqueId + ']').dialog('destroy');
}
$('*', this.element).off('.dashboardWidget'); // unbind all events
$('.widgetContent', this.element).trigger('widget:destroy');
require('piwik/UI').UIControl.cleanupUnusedControls();
return this;
},
/**
* Returns the data currently set for the widget
* @return {object}
*/
getWidgetObject: function () {
return {
uniqueId: this.uniqueId,
parameters: this.widgetParameters,
isHidden: this.options.isHidden
};
},
/**
* Show the current widget in an ui.dialog
*/
maximise: function () {
this.isMaximised = true;
if (this.options.onMaximise) {
this.options.onMaximise(this.element);
} else {
this._maximiseImpl();
}
$('.widgetContent', this.element).trigger('widget:maximise');
return this;
},
/**
* Reloads the widgets content with the currently set parameters
*/
reload: function (hideLoading, notJQueryUI, overrideParams) {
if (!notJQueryUI) {
piwikHelper.log('widget.reload() was called by jquery.ui, ignoring', arguments.callee.caller);
return;
}
var self = this, currentWidget = this.element;
function onWidgetLoadedReplaceElementWithContent(loadedContent) {
$('.widgetContent', currentWidget).html(loadedContent);
$('.widgetContent', currentWidget).removeClass('loading');
$('.widgetContent', currentWidget).trigger('widget:create', [self]);
}
// Reading segment from hash tag (standard case) or from the URL (when embedding dashboard)
var segment = broadcast.getValueFromHash('segment') || broadcast.getValueFromUrl('segment');
if (segment.length) {
this.widgetParameters.segment = segment;
}
if (!hideLoading) {
$('.widgetContent', currentWidget).addClass('loading');
}
var params = $.extend(this.widgetParameters, overrideParams || {});
widgetsHelper.loadWidgetAjax(this.uniqueId, params, onWidgetLoadedReplaceElementWithContent);
return this;
},
/**
* Update widget parameters
*
* @param {object} parameters
*/
setParameters: function (parameters) {
if (!this.isMaximised
&& this.options.autoMaximiseVisualizations.indexOf(parameters.viewDataTable) !== -1
) {
this.maximise();
}
for (var name in parameters) {
this.widgetParameters[name] = parameters[name];
}
if (!this.isMaximised) {
this.options.onChange();
}
return this;
},
/**
* Get widget parameters
*
* @param {object} parameters
*/
getParameters: function () {
return $.extend({}, this.widgetParameters);
},
/**
* Creaates the widget markup for the given uniqueId
*
* @param {String} uniqueId
*/
_createDashboardWidget: function (uniqueId) {
var widgetName = widgetsHelper.getWidgetNameFromUniqueId(uniqueId);
if (!widgetName) {
widgetName = _pk_translate('Dashboard_WidgetNotFound');
}
var title = this.options.title === null ? $('<span/>').text(widgetName) : this.options.title;
var emptyWidgetContent = require('piwik/UI/Dashboard').WidgetFactory.make(uniqueId, title);
this.element.html(emptyWidgetContent);
var widgetElement = $('#' + uniqueId, this.element);
var self = this;
widgetElement
.on('mouseenter.dashboardWidget', function () {
if (!self.isMaximised) {
$(this).addClass('widgetHover');
$('.widgetTop', this).addClass('widgetTopHover');
}
})
.on('mouseleave.dashboardWidget', function () {
if (!self.isMaximised) {
$(this).removeClass('widgetHover');
$('.widgetTop', this).removeClass('widgetTopHover');
}
});
if (this.options.isHidden) {
$('.widgetContent', widgetElement).toggleClass('hidden').closest('.widget').toggleClass('hiddenContent');
}
$('.button#close', widgetElement)
.on('click.dashboardWidget', function (ev) {
piwikHelper.modalConfirm('#confirm', {yes: function () {
if (self.options.onRemove) {
self.options.onRemove(self.element);
} else {
self.element.remove();
self.options.onChange();
}
}});
});
$('.button#maximise', widgetElement)
.on('click.dashboardWidget', function (ev) {
if (self.options.onMaximise) {
self.options.onMaximise(self.element);
} else {
if ($('.widgetContent', $(this).parents('.widget')).hasClass('hidden')) {
self.showContent();
} else {
self.maximise();
}
}
});
$('.button#minimise', widgetElement)
.on('click.dashboardWidget', function (ev) {
if (self.options.onMinimise) {
self.options.onMinimise(self.element);
} else {
if (!self.isMaximised) {
self.hideContent();
} else {
self.element.dialog("close");
}
}
});
$('.button#refresh', widgetElement)
.on('click.dashboardWidget', function (ev) {
if (self.options.onRefresh) {
self.options.onRefresh(self.element);
} else {
self.reload(false, true);
}
});
},
/**
* Hide the widget content. Triggers the onChange event.
*/
hideContent: function () {
$('.widgetContent', this.element.find('.widget').addClass('hiddenContent')).addClass('hidden');
this.options.isHidden = true;
this.options.onChange();
},
/**
* Show the widget content. Triggers the onChange event.
*/
showContent: function () {
this.isMaximised = false;
this.options.isHidden = false;
this.element.find('.widget').removeClass('hiddenContent').find('.widgetContent').removeClass('hidden');
this.element.find('.widget').find('div.piwik-graph').trigger('resizeGraph');
this.options.onChange();
$('.widgetContent', this.element).trigger('widget:minimise');
},
/**
* Default maximise behavior. Will create a dialog that is 70% of the document's width,
* displaying the widget alone.
*/
_maximiseImpl: function () {
this.detachWidget();
var width = Math.floor($('body').width() * 0.7);
var self = this;
this.element.dialog({
title: '',
modal: true,
width: width,
position: ['center', 'center'],
resizable: true,
autoOpen: true,
close: function (event, ui) {
self.isMaximised = false;
$('body').off('.dashboardWidget');
$(this).dialog("destroy");
$('#' + self.uniqueId + '-placeholder').replaceWith(this);
$(this).removeAttr('style');
self.options.onChange();
$(this).find('div.piwik-graph').trigger('resizeGraph');
$('.widgetContent', self.element).trigger('widget:minimise');
}
});
this.element.find('div.piwik-graph').trigger('resizeGraph');
var currentWidget = this.element;
$('body').on('click.dashboardWidget', function (ev) {
if (/ui-widget-overlay/.test(ev.target.className)) {
$(currentWidget).dialog("close");
}
});
},
/**
* Detaches the widget from the DOM and replaces it with a placeholder element.
* The placeholder element will have the save dimensions as the widget and will have
* the widgetPlaceholder CSS class.
*
* @return {jQuery} the detached widget
*/
detachWidget: function () {
this.element.before('<div id="' + this.uniqueId + '-placeholder" class="widgetPlaceholder widget"> </div>');
$('#' + this.uniqueId + '-placeholder').height(this.element.height());
$('#' + this.uniqueId + '-placeholder').width(this.element.width() - 16);
return this.element.detach();
}
});
})(jQuery);

View file

@ -0,0 +1,421 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
function widgetsHelper() {
}
/**
* Returns the available widgets fetched via AJAX (if not already done)
*
* @return {object} object containing available widgets
*/
widgetsHelper.getAvailableWidgets = function () {
if (!widgetsHelper.availableWidgets) {
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams({
module: 'Dashboard',
action: 'getAvailableWidgets'
}, 'get');
ajaxRequest.setCallback(
function (data) {
widgetsHelper.availableWidgets = data;
}
);
ajaxRequest.send(true);
}
return widgetsHelper.availableWidgets;
};
/**
* Returns the complete widget object by its unique id
*
* @param {string} uniqueId
* @return {object} widget object
*/
widgetsHelper.getWidgetObjectFromUniqueId = function (uniqueId) {
var widgets = widgetsHelper.getAvailableWidgets();
for (var widgetCategory in widgets) {
var widgetInCategory = widgets[widgetCategory];
for (var i in widgetInCategory) {
if (widgetInCategory[i]["uniqueId"] == uniqueId) {
return widgetInCategory[i];
}
}
}
return false;
};
/**
* Returns the name of a widget by its unique id
*
* @param {string} uniqueId unique id of the widget
* @return {string}
*/
widgetsHelper.getWidgetNameFromUniqueId = function (uniqueId) {
var widget = this.getWidgetObjectFromUniqueId(uniqueId);
if (widget == false) {
return false;
}
return widget["name"];
};
/**
* Sends and ajax request to query for the widgets html
*
* @param {string} widgetUniqueId unique id of the widget
* @param {object} widgetParameters parameters to be used for loading the widget
* @param {function} onWidgetLoadedCallback callback to be executed after widget is loaded
* @return {object}
*/
widgetsHelper.loadWidgetAjax = function (widgetUniqueId, widgetParameters, onWidgetLoadedCallback) {
var disableLink = broadcast.getValueFromUrl('disableLink');
if (disableLink.length) {
widgetParameters['disableLink'] = disableLink;
}
widgetParameters['widget'] = 1;
var ajaxRequest = new ajaxHelper();
ajaxRequest.addParams(widgetParameters, 'get');
ajaxRequest.setCallback(onWidgetLoadedCallback);
ajaxRequest.setFormat('html');
ajaxRequest.send(false);
return ajaxRequest;
};
(function ($, require) {
var exports = require('piwik/UI/Dashboard');
/**
* Singleton instance that creates widget elements. Normally not needed even
* when embedding/re-using dashboard widgets, but it can be useful when creating
* elements with the same look and feel as dashboard widgets, but different
* behavior (such as the widget preview in the dashboard manager control).
*
* @constructor
*/
var WidgetFactory = function () {
// empty
};
/**
* Creates an HTML element for displaying a widget.
*
* @param {string} uniqueId unique id of the widget
* @param {string} widgetName name of the widget
* @return {Element} the empty widget
*/
WidgetFactory.prototype.make = function (uniqueId, widgetName) {
var $result = this.getWidgetTemplate().clone();
$result.attr('id', uniqueId).find('.widgetName').append(widgetName);
return $result;
};
/**
* Returns the base widget template element. The template is stored in the
* element with id == 'widgetTemplate'.
*
* @return {Element} the widget template
*/
WidgetFactory.prototype.getWidgetTemplate = function () {
if (!this.widgetTemplate) {
this.widgetTemplate = $('#widgetTemplate').find('>.widget').detach();
}
return this.widgetTemplate;
};
exports.WidgetFactory = new WidgetFactory();
})(jQuery, require);
/**
* widgetPreview jQuery Extension
*
* Converts an dom element to a widget preview
* Widget preview contains an categorylist, widgetlist and a preview
*/
(function ($) {
$.extend({
widgetPreview: new function () {
/**
* Default settings for widgetPreview
* @type {object}
*/
var defaultSettings = {
/**
* handler called after a widget preview is loaded in preview element
* @type {function}
*/
onPreviewLoaded: function () {},
/**
* handler called on click on element in widgetlist or widget header
* @type {function}
*/
onSelect: function () {},
/**
* callback used to determine if a widget is available or not
* unavailable widgets aren't chooseable in widgetlist
* @type {function}
*/
isWidgetAvailable: function (widgetUniqueId) { return true; },
/**
* should the lists and preview be reset on widget selection?
* @type {boolean}
*/
resetOnSelect: false,
/**
* css classes for various elements
* @type {string}
*/
baseClass: 'widgetpreview-base',
categorylistClass: 'widgetpreview-categorylist',
widgetlistClass: 'widgetpreview-widgetlist',
widgetpreviewClass: 'widgetpreview-preview',
choosenClass: 'widgetpreview-choosen',
unavailableClass: 'widgetpreview-unavailable'
};
/**
* Returns the div to show category list in
* - if element doesn't exist it will be created and added
* - if element already exist it's content will be removed
*
* @return {$} category list element
*/
function createWidgetCategoryList(widgetPreview, availableWidgets) {
var settings = widgetPreview.settings;
if (!$('.' + settings.categorylistClass, widgetPreview).length) {
$(widgetPreview).append('<ul class="' + settings.categorylistClass + '"></ul>');
} else {
$('.' + settings.categorylistClass, widgetPreview).empty();
}
for (var widgetCategory in availableWidgets) {
$('.' + settings.categorylistClass, widgetPreview).append('<li>' + widgetCategory + '</li>');
}
return $('.' + settings.categorylistClass, widgetPreview);
}
/**
* Returns the div to show widget list in
* - if element doesn't exist it will be created and added
* - if element already exist it's content will be removed
*
* @return {$} widget list element
*/
function createWidgetList(widgetPreview) {
var settings = widgetPreview.settings;
if (!$('.' + settings.widgetlistClass, widgetPreview).length) {
$(widgetPreview).append('<ul class="' + settings.widgetlistClass + '"></ul>');
} else {
$('.' + settings.widgetlistClass + ' li', widgetPreview).off('mouseover');
$('.' + settings.widgetlistClass + ' li', widgetPreview).off('click');
$('.' + settings.widgetlistClass, widgetPreview).empty();
}
if ($('.' + settings.categorylistClass + ' .' + settings.choosenClass, widgetPreview).length) {
var position = $('.' + settings.categorylistClass + ' .' + settings.choosenClass, widgetPreview).position().top -
$('.' + settings.categorylistClass, widgetPreview).position().top;
$('.' + settings.widgetlistClass, widgetPreview).css('top', position);
$('.' + settings.widgetlistClass, widgetPreview).css('marginBottom', position);
}
return $('.' + settings.widgetlistClass, widgetPreview);
}
/**
* Display the given widgets in a widget list
*
* @param {object} widgets widgets to be displayed
* @return {void}
*/
function showWidgetList(widgets, widgetPreview) {
var settings = widgetPreview.settings;
var widgetList = createWidgetList(widgetPreview),
widgetPreviewTimer;
for (var j = 0; j < widgets.length; j++) {
var widgetName = widgets[j]["name"];
var widgetUniqueId = widgets[j]["uniqueId"];
// var widgetParameters = widgets[j]["parameters"];
var widgetClass = '';
if (!settings.isWidgetAvailable(widgetUniqueId)) {
widgetClass += ' ' + settings.unavailableClass;
}
widgetList.append('<li class="' + widgetClass + '" uniqueid="' + widgetUniqueId + '">' + widgetName + '</li>');
}
// delay widget preview a few millisconds
$('li', widgetList).on('mouseenter', function () {
var that = this,
widgetUniqueId = $(this).attr('uniqueid');
clearTimeout(widgetPreview);
widgetPreviewTimer = setTimeout(function () {
$('li', widgetList).removeClass(settings.choosenClass);
$(that).addClass(settings.choosenClass);
showPreview(widgetUniqueId, widgetPreview);
}, 400);
});
// clear timeout after mouse has left
$('li:not(.' + settings.unavailableClass + ')', widgetList).on('mouseleave', function () {
clearTimeout(widgetPreview);
});
$('li:not(.' + settings.unavailableClass + ')', widgetList).on('click', function () {
if (!$('.widgetLoading', widgetPreview).length) {
settings.onSelect($(this).attr('uniqueid'));
if (settings.resetOnSelect) {
resetWidgetPreview(widgetPreview);
}
}
return false;
});
}
/**
* Returns the div to show widget preview in
* - if element doesn't exist it will be created and added
* - if element already exist it's content will be removed
*
* @return {$} preview element
*/
function createPreviewElement(widgetPreview) {
var settings = widgetPreview.settings;
if (!$('.' + settings.widgetpreviewClass, widgetPreview).length) {
$(widgetPreview).append('<div class="' + settings.widgetpreviewClass + '"></div>');
} else {
$('.' + settings.widgetpreviewClass + ' .widgetTop', widgetPreview).off('click');
$('.' + settings.widgetpreviewClass, widgetPreview).empty();
}
return $('.' + settings.widgetpreviewClass, widgetPreview);
}
/**
* Show widget with the given uniqueId in preview
*
* @param {string} widgetUniqueId unique id of widget to display
* @return {void}
*/
function showPreview(widgetUniqueId, widgetPreview) {
// do not reload id widget already displayed
if ($('#' + widgetUniqueId, widgetPreview).length) return;
var settings = widgetPreview.settings;
var previewElement = createPreviewElement(widgetPreview);
var widget = widgetsHelper.getWidgetObjectFromUniqueId(widgetUniqueId);
var widgetParameters = widget['parameters'];
var emptyWidgetHtml = require('piwik/UI/Dashboard').WidgetFactory.make(
widgetUniqueId,
$('<div/>')
.attr('title', _pk_translate("Dashboard_AddPreviewedWidget"))
.text(_pk_translate('Dashboard_WidgetPreview'))
);
previewElement.html(emptyWidgetHtml);
var onWidgetLoadedCallback = function (response) {
var widgetElement = $('#' + widgetUniqueId);
$('.widgetContent', widgetElement).html($(response));
$('.widgetContent', widgetElement).trigger('widget:create');
settings.onPreviewLoaded(widgetUniqueId, widgetElement);
$('.' + settings.widgetpreviewClass + ' .widgetTop', widgetPreview).on('click', function () {
settings.onSelect(widgetUniqueId);
if (settings.resetOnSelect) {
resetWidgetPreview(widgetPreview);
}
return false;
});
};
// abort previous sent request
if (widgetPreview.widgetAjaxRequest) {
widgetPreview.widgetAjaxRequest.abort();
}
widgetPreview.widgetAjaxRequest = widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParameters, onWidgetLoadedCallback);
}
/**
* Reset function
*
* @return {void}
*/
function resetWidgetPreview(widgetPreview) {
var settings = widgetPreview.settings;
$('.' + settings.categorylistClass + ' li', widgetPreview).removeClass(settings.choosenClass);
createWidgetList(widgetPreview);
createPreviewElement(widgetPreview);
}
/**
* Constructor
*
* @param {object} userSettings Settings to be used
* @return {void}
*/
this.construct = function (userSettings) {
if (userSettings == 'reset') {
resetWidgetPreview(this);
return;
}
this.widgetAjaxRequest = null;
$(this).addClass('widgetpreview-base');
this.settings = jQuery.extend({}, defaultSettings, userSettings);
// set onSelect callback
if (typeof this.settings.onSelect == 'function') {
this.onSelect = this.settings.onSelect;
}
// set onPreviewLoaded callback
if (typeof this.settings.onPreviewLoaded == 'function') {
this.onPreviewLoaded = this.settings.onPreviewLoaded;
}
availableWidgets = widgetsHelper.getAvailableWidgets();
var categoryList = createWidgetCategoryList(this, availableWidgets);
var self = this;
$('li', categoryList).on('mouseover', function () {
var category = $(this).text();
var widgets = availableWidgets[category];
$('li', categoryList).removeClass(self.settings.choosenClass);
$(this).addClass(self.settings.choosenClass);
showWidgetList(widgets, self);
createPreviewElement(self); // empty preview
});
};
}
});
/**
* Makes widgetPreview available with $().widgetPreview()
*/
$.fn.extend({
widgetPreview: $.widgetPreview.construct
})
})(jQuery);

View file

@ -0,0 +1,540 @@
#dashboard {
margin: 0 -7px;
}
#root>.top_controls {
margin-left:15px;
margin-right:15px;
}
.top_controls {
position: relative;
height: 32px;
clear: left;
}
@media all and (max-width: 749px) {
.top_controls {
height: auto;
}
.top_controls #periodString,
.top_controls .dashboardSettings,
.top_controls #segmentEditorPanel {
position: static;
margin: 0 0 10px;
float: none;
}
}
#dashboardWidgetsArea {
padding-bottom: 100px;
}
.col {
float: left;
min-height: 100px;
}
.col.width-100 {
width: 100%;
}
.col.width-75 {
width: 75%;
}
.col.width-67 {
width: 66.67%;
}
.col.width-50 {
width: 50%;
}
.col.width-40 {
width: 40%;
}
.col.width-33 {
width: 33.33%;
}
.col.width-30 {
width: 30%;
}
.col.width-25 {
width: 25%;
}
.hover {
border: 2px dashed #E3E3E3;
}
.widget {
background: #fff;
border: 1px solid #bbb6ad;
margin: 10px 7px;
border-radius: 4px;
overflow: hidden;
font-size: 14px;
z-index: 1;
}
.widgetHover {
border: 1px solid #aba494;
}
.widget .entityContainer {
width: 100%;
}
.widget .sparkline {
margin-left: 5px;
}
.widgetContent.hidden {
position: absolute;
top: -5000px;
}
.widgetContent.loading {
opacity: 0.5;
background: url(plugins/Zeitgeist/images/loading-blue.gif) no-repeat top right;
}
.widget h2 {
font-size: 1.2em;
margin-left: 10px;
font-weight: bold;
}
.widget p {
margin-left: 10px;
}
.widgetTop {
background: #b5b0a7 url(plugins/Zeitgeist/images/dashboard_h_bg.png) repeat-x 0 0;
cursor: move;
font-size: 10pt;
font-weight: bold;
padding-bottom: 4px;
}
.widgetTopHover {
background: #C4BBAD url(plugins/Zeitgeist/images/dashboard_h_bg_hover.png) repeat-x 0 0;
}
.widgetName {
font-size: 18px;
padding: 2px 0 0 10px;
font-weight: normal;
color: #fff;
text-shadow: 1px 1px 2px #7e7363;
}
// Overriding some dataTable css for better dashboard display
.widget .dataTableWrapper {
width: 100% !important;
}
.widgetTop .button {
cursor: pointer;
float: right;
margin: 6px 6px 0 0;
}
.ui-confirm {
display: none;
width: 630px;
background: #fff;
color: #444;
cursor: default;
font-size: 12px !important;
font-family: Arial, Verdana, Arial, Helvetica, sans-serif;
border-radius: 4px;
padding: 20px 10px;
border-radius: 4px;
min-height: 0 !important;
}
.ui-confirm p {
margin-top:10px;
}
.ui-confirm h2 {
text-align: center;
font-weight: bold;
padding: 0;
}
.ui-dialog .ui-dialog-buttonpane {
text-align: center;
border: none;
}
.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
float: none;
}
.ui-dialog-buttonset input[type=button], .ui-dialog-buttonset button {
background: #B5B0A7 url(plugins/Zeitgeist/images/dashboard_h_bg_hover.png) repeat-x 0 0 !important;
color: #fff !important;
border: 0 !important;
font-size: 12px !important;
padding: 5px 20px !important;
border-radius: 3px;
cursor: pointer;
display: inline-block;
margin: 0 8px 3px 8px !important;
}
.ui-dialog .ui-button-text {
padding: 0 !important;
}
.ui-widget-overlay {
opacity: 0.6;
background: none #000;
position: fixed;
z-index: 1000;
}
.ui-dialog {
z-index: 1001;
}
.menu {
display: none;
}
.widgetLoading {
cursor: wait;
padding: 10px;
text-align: center;
font-size: 10pt;
}
#closeMenuIcon {
float: right;
margin: 3px;
cursor: pointer;
}
.menuClear {
clear: both;
height: 30px;
}
.dashboardSettings {
position: absolute;
z-index: 120;
background: #f7f7f7;
border: 1px solid #e4e5e4;
padding: 5px 10px 6px 10px;
border-radius: 4px;
color: #444;
font-size: 14px;
cursor: pointer;
overflow: hidden;
}
.dashboardSettings:hover {
background: #f1f0eb;
border-color: #a9a399;
}
.dashboardSettings.visible {
z-index: 1020; /* More than .jqplot-seriespicker-popover (1010) */
}
.dashboardSettings > span {
background: url(plugins/Zeitgeist/images/sort_subtable_desc.png) right center no-repeat;
padding-right: 20px;
display: block;
}
.dashboardSettings ul.submenu {
padding-top: 5px;
display: none;
float: left;
}
.dashboardSettings.visible ul.submenu {
display: block;
list-style: square outside none;
margin-left: 15px;
}
.dashboardSettings > ul.submenu > li {
padding: 5px 0;
clear: both;
}
.dashboardSettings > ul.submenu > li:hover {
color: #e87500;
}
#changeDashboardLayout h2 {
margin-bottom: 20px;
}
#columnPreview {
clear: both;
width: 400px;
margin: auto;
}
#columnPreview > div {
margin: 5px;
float: left;
opacity: 0.4;
cursor: pointer;
filter: Alpha(opacity=40);
}
#columnPreview > div:hover, #columnPreview > div.choosen {
opacity: 1;
filter: Alpha(opacity=100);
}
#columnPreview div div {
height: 120px;
float: left;
}
#columnPreview div div span {
background-color: #ddd;
width: 100%;
height: 100%;
display: block;
border: 2px dotted #555;
margin: 0 1px;
}
#columnPreview div.choosen div span, #columnPreview div:hover div span {
border-style: solid;
}
#columnPreview .width-100 {
width: 120px;
}
#columnPreview .width-75 {
width: 90px;
}
#columnPreview .width-67 {
width: 80.4px;
}
#columnPreview .width-50 {
width: 60px;
}
#columnPreview .width-40 {
width: 48px;
}
#columnPreview .width-33 {
width: 40px;
}
#columnPreview .width-30 {
width: 36px;
}
#columnPreview .width-25 {
width: 30px;
}
/**
* Layout for widget previews
*/
.widgetpreview-base {
clear: both;
min-height: 600px;
-height: 600px;
}
.addWidget, .manageDashboard {
cursor: default;
}
ul.widgetpreview-widgetlist,
ul.widgetpreview-categorylist {
color: #5d5342;
list-style: none;
font-size: 11px;
line-height: 20px;
float: left;
margin-right: 20px;
}
ul.widgetpreview-categorylist {
cursor: default;
}
ul.widgetpreview-categorylist li,
ul.widgetpreview-widgetlist li {
line-height: 20px;
padding: 0 25px 0 5px;
border-radius: 2px;
}
.widgetpreview-base li.widgetpreview-choosen {
background: #e4e2d7 url(plugins/Zeitgeist/images/arr_r.png) no-repeat right 6px;
color: #255792;
font-weight: bold;
}
.widgetpreview-categorylist li.widgetpreview-choosen {
color: #000;
}
.widgetpreview-base li.widgetpreview-unavailable {
color: #D3D3D3;
cursor: default;
}
ul.widgetpreview-widgetlist {
cursor: pointer;
position: relative;
top: 0;
}
div.widgetpreview-preview {
float: left;
width: 500px;
}
.dashboardSettings {
min-height: 0;
height: auto;
margin-right: 10px;
}
.dashboardSettings .submenu {
font-weight: bold;
color: #255792;
}
.dashboardSettings .submenu ul {
float: none;
font-weight: normal;
padding-top: 10px;
margin-left: 10px;
color: #5D5342;
list-style: none;
font-size: 11px;
line-height: 20px;
margin-right: 20px;
}
.dashboardSettings .submenu ul li {
line-height: 20px;
padding: 0 25px 0 5px;
color: #444;
}
.dashboardSettings .submenu ul li:hover {
color: #e87500;
}
.dashboardSettings .widgetpreview-widgetlist {
width: 228px;
font-weight: normal;
}
.dashboardSettings .widgetTop {
cursor: pointer;
}
.dashboardSettings .widgetpreview-widgetlist,
.dashboardSettings .widgetpreview-preview {
display: none;
}
.dashboardSettings.visible .widgetpreview-widgetlist,
.dashboardSettings.visible .widgetpreview-preview {
display: block;
}
.widgetPlaceholder {
border: 1px dashed #bbb6ad;
}
#newDashboardName, #createDashboardName {
width: 200px;
}
#newDashboardNameInput, #createDashboardNameInput {
margin: 20px 0 0 100px;
text-align: left;
}
#createDashboardNameInput input {
margin-bottom: 10px;
}
.popoverSubMessage {
text-align: center;
padding: 10px 0 5px 0;
}
#copyDashboardToUserConfirm .inputs {
width: 375px;
margin: 10px auto 0;
}
#copyDashboardToUserConfirm .inputs select,
#copyDashboardToUserConfirm .inputs input {
width: 150px;
float: left;
}
#copyDashboardToUserConfirm .inputs label {
width: 200px;
float: left;
}
@media all and (max-width: 749px) {
#dashboardWidgetsArea {
padding-right: 7px;
}
.col.width-75,
.col.width-67,
.col.width-50,
.col.width-40,
.col.width-33,
.col.width-30,
.col.width-25 {
width: 100%;
.widget {
margin-right: 0;
}
}
}
.widgetTop .button {
display:none;
}
.widgetTop.widgetTopHover .button {
display:block;
}
.widget.hiddenContent .widgetTop.widgetTopHover {
.button#minimise,.button#refresh {
display:none;
}
}
.ui-dialog .widget {
.button#close,.button#maximise {
display:none;
}
}

View file

@ -0,0 +1,72 @@
body {
padding-left: 7px;
}
#dashboard {
margin: 30px -6px 0 -12px;
width: 100%;
padding-top: 8px;
}
#Dashboard {
z-index: 5;
font-size: 14px;
cursor: pointer;
float: left;
position: relative;
}
#Dashboard > ul {
list-style: square inside none;
background: #f7f7f7;
border: 1px solid #e4e5e4;
padding: 5px 10px 6px 10px;
border-radius: 4px;
color: #444;
height: 18px;
}
#Dashboard:hover ul {
background: #f1f0eb;
border-color: #a9a399;
}
#Dashboard > ul > li {
float: left;
margin-right: 15px;
}
#Dashboard a {
color: #444;
text-decoration: none;
font-weight: normal;
display: inline-block;
}
#Dashboard > ul > li:hover,
#Dashboard > ul > li:hover a,
#Dashboard > ul > li.sfHover,
#Dashboard > ul > li.sfHover a {
color: #e87500;
}
#Dashboard > ul > li.sfHover,
#Dashboard > ul > li.sfHover a {
font-weight: bold;
}
.top_controls > #Dashboard,
.top_controls > #periodString,
.top_controls > .dashboardSettings {
margin-left: 0;
margin-right: 10px;
}
.jqplot-seriespicker-popover {
top: 0;
}
#ajaxLoading {
margin: 40px 0 -30px 0;
}

View file

@ -0,0 +1,22 @@
<span>{{ 'Dashboard_WidgetsAndDashboard'|translate }}</span>
<ul class="submenu">
<li>
<div class="addWidget">{{ 'Dashboard_AddAWidget'|translate }} &darr;</div>
<ul class="widgetpreview-categorylist"></ul>
</li>
{% if dashboardActions|length > 0 %}
<li>
<div class="manageDashboard">{{ 'Dashboard_ManageDashboard'|translate }} &darr;</div>
<ul>
{% for action, title in dashboardActions %}
<li data-action="{{ action }}">{{ title|translate }}</li>
{% endfor %}
</ul>
</li>
{% endif %}
{% for action, title in generalActions %}
<li data-action="{{ action }}">{{ title|translate }}</li>
{% endfor %}
</ul>
<ul class="widgetpreview-widgetlist"></ul>
<div class="widgetpreview-preview"></div>

View file

@ -0,0 +1,16 @@
{# This header is for loading the dashboard in stand alone mode #}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{ 'Dashboard_Dashboard'|translate }} - {{ 'CoreHome_WebAnalyticsReports'|translate }}</title>
<!--[if lt IE 9]>
<script language="javascript" type="text/javascript" src="libs/jqplot/excanvas.min.js"></script>
<![endif]-->
<link rel="stylesheet" type="text/css" href="plugins/Dashboard/stylesheets/standalone.css" />
{% include "_jsGlobalVariables.twig" %}
{% include "_jsCssIncludes.twig" %}
</head>
<body id="standalone">

View file

@ -0,0 +1,22 @@
<div id="widgetTemplate" style="display:none;">
<div class="widget">
<div class="widgetTop">
<div class="button" id="close">
<img src="plugins/Zeitgeist/images/close.png" title="{{ 'General_Close'|translate }}" />
</div>
<div class="button" id="maximise">
<img src="plugins/Zeitgeist/images/maximise.png" title="{{ 'Dashboard_Maximise'|translate }}" />
</div>
<div class="button" id="minimise">
<img src="plugins/Zeitgeist/images/minimise.png" title="{{ 'Dashboard_Minimise'|translate }}" />
</div>
<div class="button" id="refresh">
<img src="plugins/Zeitgeist/images/refresh.png" title="{{ 'General_Refresh'|translate }}" />
</div>
<div class="widgetName">{% if widgetName is defined %}{{ widgetName }}{% endif %}</div>
</div>
<div class="widgetContent">
<div class="widgetLoading">{{ 'Dashboard_LoadingWidget'|translate }}</div>
</div>
</div>
</div>

View file

@ -0,0 +1,98 @@
<script type="text/javascript">
widgetsHelper.availableWidgets = {{ availableWidgets|raw }};
$(function() {
initDashboard({{ dashboardId }}, {{ dashboardLayout|raw }});
});
</script>
<div id="dashboard">
<div class="ui-confirm" id="confirm">
<h2>{{ 'Dashboard_DeleteWidgetConfirm'|translate }}</h2>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
<div class="ui-confirm" id="setAsDefaultWidgetsConfirm">
<h2>{{ 'Dashboard_SetAsDefaultWidgetsConfirm'|translate }}</h2>
{% set resetDashboard %}{{ 'Dashboard_ResetDashboard'|translate }}{% endset %}
<div class="popoverSubMessage">{{ 'Dashboard_SetAsDefaultWidgetsConfirmHelp'|translate(resetDashboard) }}</div>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
<div class="ui-confirm" id="resetDashboardConfirm">
<h2>{{ 'Dashboard_ResetDashboardConfirm'|translate }}</h2>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
<div class="ui-confirm" id="dashboardEmptyNotification">
<h2>{{ 'Dashboard_DashboardEmptyNotification'|translate }}</h2>
<input role="addWidget" type="button" value="{{ 'Dashboard_AddAWidget'|translate }}"/>
<input role="resetDashboard" type="button" value="{{ 'Dashboard_ResetDashboard'|translate }}"/>
</div>
<div class="ui-confirm" id="changeDashboardLayout">
<h2>{{ 'Dashboard_SelectDashboardLayout'|translate }}</h2>
<div id="columnPreview">
{% for layout in availableLayouts %}
<div>
{% for column in layout %}
<div class="width-{{ column }}"><span></span></div>
{% endfor %}
</div>
{% endfor %}
</div>
<input role="yes" type="button" value="{{ 'General_Save'|translate }}"/>
</div>
<div class="ui-confirm" id="renameDashboardConfirm">
<h2>{{ 'Dashboard_RenameDashboard'|translate }}</h2>
<div id="newDashboardNameInput"><label for="newDashboardName">{{ 'Dashboard_DashboardName'|translate }} </label>
<input type="input" name="newDashboardName" id="newDashboardName" value=""/>
</div>
<input role="yes" type="button" value="{{ 'General_Save'|translate }}"/>
<input role="cancel" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
{% if isSuperUser %}
<div class="ui-confirm" id="copyDashboardToUserConfirm">
<h2>{{ 'Dashboard_CopyDashboardToUser'|translate }}</h2>
<div class="inputs">
<label for="copyDashboardName">{{ 'Dashboard_DashboardName'|translate }} </label>
<input type="input" name="copyDashboardName" id="copyDashboardName" value=""/>
<label for="copyDashboardUser">{{ 'General_Username'|translate }} </label>
<select name="copyDashboardUser" id="copyDashboardUser"></select>
</div>
<input role="yes" type="button" value="{{ 'General_Ok'|translate }}"/>
<input role="cancel" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
{% endif %}
<div class="ui-confirm" id="createDashboardConfirm">
<h2>{{ 'Dashboard_CreateNewDashboard'|translate }}</h2>
<div id="createDashboardNameInput">
<label>{{ 'Dashboard_DashboardName'|translate }} <input type="input" name="newDashboardName" id="createDashboardName" value=""/></label><br/>
<label><input type="radio" checked="checked" name="type" value="default" id="dashboard_type_default">{{ 'Dashboard_DefaultDashboard'|translate }}
</label><br/>
<label><input type="radio" name="type" value="empty" id="dashboard_type_empty">{{ 'Dashboard_EmptyDashboard'|translate }}</label>
</div>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
<div class="ui-confirm" id="removeDashboardConfirm">
<h2>{{ 'Dashboard_RemoveDashboardConfirm'|translate('<span></span>')|raw }}</h2>
<div class="popoverSubMessage">{{ 'Dashboard_NotUndo'|translate(resetDashboard) }}</div>
<input role="yes" type="button" value="{{ 'General_Yes'|translate }}"/>
<input role="no" type="button" value="{{ 'General_Cancel'|translate }}"/>
</div>
{% include "@Dashboard/_widgetFactoryTemplate.twig" %}
<div id="dashboardWidgetsArea"></div>
</div>

View file

@ -0,0 +1,20 @@
{% include "@Dashboard/_header.twig" %}
<div class="top_controls">
{% include "@CoreHome/_periodSelect.twig" %}
{{ postEvent("Template.nextToCalendar") }}
{% render dashboardSettingsControl %}
<div id="Dashboard" class="piwikTopControl">
<ul>
{% for dashboard in dashboards %}
<li class="dashboardMenuItem" id="Dashboard_embeddedIndex_{{ dashboard.iddashboard }}">
<a href="javascript:$('#dashboardWidgetsArea').dashboard('loadDashboard', {{ dashboard.iddashboard }});">{{ dashboard.name|escape }}</a>
</li>
{% endfor %}
</ul>
</div>
</div>
{% import 'ajaxMacros.twig' as ajax %}
{{ ajax.loadingDiv }}
{% include "@Dashboard/embeddedIndex.twig" %}
</body>
</html>