update Piwik to version 2.16 (fixes #91)

This commit is contained in:
oliver 2016-04-10 18:55:57 +02:00
commit d885a4baa9
5833 changed files with 418860 additions and 226988 deletions

View file

@ -1,34 +0,0 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
angular.module('piwikApp').controller('MultiSitesDashboardController', function($scope, piwik, multisitesDashboardModel){
$scope.model = multisitesDashboardModel;
$scope.reverse = true;
$scope.predicate = 'nb_visits';
$scope.evolutionSelector = 'visits_evolution';
$scope.hasSuperUserAccess = piwik.hasSuperUserAccess;
$scope.date = piwik.broadcast.getValueFromUrl('date');
$scope.url = piwik.piwik_url;
$scope.period = piwik.period;
$scope.sortBy = function (metric) {
var reverse = $scope.reverse;
if ($scope.predicate == metric) {
reverse = !reverse;
}
$scope.predicate = metric;
$scope.reverse = reverse;
};
this.refresh = function (interval) {
multisitesDashboardModel.fetchAllSites(interval);
};
});

View file

@ -1,40 +0,0 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
/**
* Renders the multisites dashboard
* Example usage:
*
* <div piwik-multisites-dashboard
* display-revenue-column="true"
* show-sparklines="true"
* date-sparkline="true"
* page-size="50"
* auto-refresh-today-report="500" // or 0 to disable
* ></div>
*/
angular.module('piwikApp').directive('piwikMultisitesDashboard', function($document, piwik, $filter){
return {
restrict: 'A',
scope: {
displayRevenueColumn: '@',
showSparklines: '@',
dateSparkline: '@'
},
templateUrl: 'plugins/MultiSites/angularjs/dashboard/dashboard.html?cb=' + piwik.cacheBuster,
controller: 'MultiSitesDashboardController',
link: function (scope, element, attrs, controller) {
if (attrs.pageSize) {
scope.model.pageSize = attrs.pageSize;
}
controller.refresh(attrs.autoRefreshTodayReport);
}
};
});

View file

@ -1,63 +0,0 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
/**
* Filters a given list of websites and groups and makes sure only the websites within a given offset and limit are
* displayed. It also makes sure sites are displayed under the groups. That means it flattens a structure like this:
*
* - website1
* - website2
* - website3.sites // this is a group
* - website4
* - website5
* - website6
*
* to the following structure
* - website1
* - website2
* - website3.sites // this is a group
* - website4
* - website5
* - website6
*/
angular.module('piwikApp').filter('multiSitesGroupFilter', function() {
return function(websites, from, to) {
var offsetEnd = parseInt(from, 10) + parseInt(to, 10);
var groups = {};
var sites = [];
for (var index = 0; index < websites.length; index++) {
var website = websites[index];
sites.push(website);
if (website.sites && website.sites.length) {
groups[website.label] = website;
for (var innerIndex = 0; innerIndex < website.sites.length; innerIndex++) {
sites.push(website.sites[innerIndex]);
}
}
if (sites.length >= offsetEnd) {
break;
}
}
// if the first site is a website having a group, then try to find the related group and prepend it to the list
// of sites to make sure we always display the name of the group that belongs to a website.
var filteredSites = sites.slice(from, offsetEnd);
if (filteredSites.length && filteredSites[0] && filteredSites[0].group) {
var groupName = filteredSites[0].group;
if (groups[groupName]) {
filteredSites.unshift(groups[groupName]);
}
}
return filteredSites;
};
});

View file

@ -1,273 +0,0 @@
/**
* Model for Multisites Dashboard aka All Websites Dashboard.
*
*/
angular.module('piwikApp').factory('multisitesDashboardModel', function (piwikApi, $filter, $timeout) {
/**
*
* this is the list of all available sites. For performance reason this list is different to model.sites. ngRepeat
* won't operate on the whole list this way. The allSites array contains websites and groups in the following
* structure
*
* - website1
* - website2
* - website3.sites = [ // this is a group
* - website4
* - website5
* ]
* - website6
*
* This structure allows us to display the sites within a group directly under the group without big logic and also
* allows us to calculate the summary for each group easily
*/
var allSitesByGroup = [];
var model = {};
// those sites are going to be displayed
model.sites = [];
model.isLoading = false;
model.pageSize = 5;
model.currentPage = 0;
model.totalVisits = '?';
model.totalActions = '?';
model.totalRevenue = '?';
model.searchTerm = '';
model.lastVisits = '?';
model.lastVisitsDate = '?';
fetchPreviousSummary();
// create a new group object which has similar structure than a website
function createGroup(name){
return {
label: name,
sites: [],
nb_visits: 0,
nb_pageviews: 0,
revenue: 0,
isGroup: true
};
}
// create a new group with empty site to make sure we do not change the original group in $allSites
function copyGroup(group)
{
return {
label: group.label,
sites: [],
nb_visits: group.nb_visits,
nb_pageviews: group.nb_pageviews,
revenue: group.revenue,
isGroup: true
};
}
function onError () {
model.errorLoadingSites = true;
model.sites = [];
allSitesByGroup = [];
}
function calculateMetricsForEachGroup(groups)
{
angular.forEach(groups, function (group) {
angular.forEach(group.sites, function (site) {
var revenue = (site.revenue+'').match(/(\d+\.?\d*)/); // convert $ 0.00 to 0.00 or 5€ to 5
group.nb_visits += parseInt(site.nb_visits, 10);
group.nb_pageviews += parseInt(site.nb_pageviews, 10);
if (revenue.length) {
group.revenue += parseInt(revenue[0], 10);
}
});
});
}
function createGroupsAndMoveSitesIntoRelatedGroup(allSitesUnordered, reportMetadata)
{
var sitesByGroup = [];
var groups = {};
// we do 3 things (complete site information, create groups, move sites into group) in one step for
// performance reason, there can be > 20k sites
angular.forEach(allSitesUnordered, function (site, index) {
site.idsite = reportMetadata[index].idsite;
site.group = reportMetadata[index].group;
site.main_url = reportMetadata[index].main_url;
if (site.group) {
if (!groups[site.group]) {
var group = createGroup(site.group);
groups[site.group] = group;
sitesByGroup.push(group);
}
groups[site.group].sites.push(site);
} else {
sitesByGroup.push(site);
}
});
calculateMetricsForEachGroup(groups);
return sitesByGroup;
}
model.updateWebsitesList = function (processedReport) {
if (!processedReport) {
onError();
return;
}
var allSitesUnordered = processedReport.reportData;
model.totalVisits = processedReport.reportTotal.nb_visits;
model.totalActions = processedReport.reportTotal.nb_actions;
model.totalRevenue = processedReport.reportTotal.revenue;
allSitesByGroup = createGroupsAndMoveSitesIntoRelatedGroup(allSitesUnordered, processedReport.reportMetadata);
if (!allSitesByGroup.length) {
return;
}
if (model.searchTerm) {
model.searchSite(model.searchTerm);
} else {
model.sites = allSitesByGroup;
}
};
model.getNumberOfFilteredSites = function () {
var numSites = model.sites.length;
var groupNames = {};
for (var index = 0; index < model.sites.length; index++) {
var site = model.sites[index];
if (site && site.isGroup) {
numSites += site.sites.length;
}
}
return numSites;
};
model.getNumberOfPages = function () {
return Math.ceil(model.getNumberOfFilteredSites() / model.pageSize - 1);
};
model.getCurrentPagingOffsetStart = function() {
return Math.ceil(model.currentPage * model.pageSize);
};
model.getCurrentPagingOffsetEnd = function() {
var end = model.getCurrentPagingOffsetStart() + parseInt(model.pageSize, 10);
var max = model.getNumberOfFilteredSites();
if (end > max) {
end = max;
}
return parseInt(end, 10);
};
model.previousPage = function () {
model.currentPage = model.currentPage - 1;
};
model.nextPage = function () {
model.currentPage = model.currentPage + 1;
};
function nestedSearch(sitesByGroup, term)
{
var filteredSites = [];
for (var index in sitesByGroup) {
var site = sitesByGroup[index];
if (site.isGroup) {
var matchingSites = nestedSearch(site.sites, term);
if (matchingSites.length || (''+site.label).toLowerCase().indexOf(term) > -1) {
var clonedGroup = copyGroup(site);
clonedGroup.sites = matchingSites;
filteredSites.push(clonedGroup);
}
} else if ((''+site.label).toLowerCase().indexOf(term) > -1) {
filteredSites.push(site);
} else if (site.group && (''+site.group).toLowerCase().indexOf(term) > -1) {
filteredSites.push(site);
}
}
return filteredSites;
}
model.searchSite = function (term) {
model.searchTerm = term;
model.currentPage = 0;
model.sites = nestedSearch(allSitesByGroup, term);
};
function fetchPreviousSummary () {
piwikApi.fetch({
method: 'API.getLastDate'
}).then(function (response) {
if (response && response.value) {
return response.value;
}
}).then(function (lastDate) {
if (!lastDate) {
return;
}
model.lastVisitsDate = lastDate;
return piwikApi.fetch({
method: 'API.getProcessedReport',
apiModule: 'MultiSites',
apiAction: 'getAll',
hideMetricsDoc: '1',
filter_limit: '0',
showColumns: 'label,nb_visits',
enhanced: 1,
date: lastDate
});
}).then(function (response) {
if (response && response.reportTotal) {
model.lastVisits = response.reportTotal.nb_visits;
}
});
}
model.fetchAllSites = function (refreshInterval) {
if (model.isLoading) {
piwikApi.abort();
}
model.isLoading = true;
model.errorLoadingSites = false;
return piwikApi.fetch({
method: 'API.getProcessedReport',
apiModule: 'MultiSites',
apiAction: 'getAll',
hideMetricsDoc: '1',
filter_limit: '-1',
showColumns: 'label,nb_visits,nb_pageviews,visits_evolution,pageviews_evolution,revenue_evolution,nb_actions,revenue',
enhanced: 1
}).then(function (response) {
model.updateWebsitesList(response);
}, onError)['finally'](function () {
model.isLoading = false;
if (refreshInterval && refreshInterval > 0) {
$timeout(function () {
model.fetchAllSites(refreshInterval);
}, refreshInterval * 1000);
}
});
};
return model;
});

View file

@ -0,0 +1,179 @@
/**
* Model for Multisites Dashboard aka All Websites Dashboard.
*/
(function () {
angular.module('piwikApp').factory('multisitesDashboardModel', multisitesDashboardModel);
multisitesDashboardModel.$inject = ['piwikApi', '$filter', '$timeout'];
function multisitesDashboardModel(piwikApi, $filter, $timeout) {
var refreshPromise = null;
// those sites are going to be displayed
var model = {
sites : [],
isLoading : false,
pageSize : 25,
currentPage : 0,
totalVisits : '?',
totalPageviews : '?',
totalActions : '?',
totalRevenue : '?',
searchTerm : '',
lastVisits : '?',
lastVisitsDate : '?',
numberOfSites : 0,
updateWebsitesList: updateWebsitesList,
getNumberOfFilteredSites: getNumberOfFilteredSites,
getNumberOfPages: getNumberOfPages,
getCurrentPagingOffsetStart: getCurrentPagingOffsetStart,
getCurrentPagingOffsetEnd: getCurrentPagingOffsetEnd,
previousPage: previousPage,
nextPage: nextPage,
searchSite: searchSite,
sortBy: sortBy,
reverse: true,
sortColumn: 'nb_visits',
fetchAllSites: fetchAllSites,
refreshInterval: 0
};
return model;
function cancelRefereshInterval()
{
if (refreshPromise) {
$timeout.cancel(refreshPromise);
refreshPromise = null;
};
}
function onError () {
model.errorLoadingSites = true;
model.sites = [];
}
function updateWebsitesList(report) {
if (!report) {
onError();
return;
}
var allSites = report.sites;
angular.forEach(allSites, function (site, index) {
site.visits_evolution = parseInt(site.visits_evolution, 10);
site.pageviews_evolution = parseInt(site.pageviews_evolution, 10);
site.revenue_evolution = parseInt(site.revenue_evolution, 10);
});
model.totalVisits = report.totals.nb_visits;
model.totalPageviews = report.totals.nb_pageviews;
model.totalActions = report.totals.nb_actions;
model.totalRevenue = report.totals.revenue;
model.lastVisits = report.totals.nb_visits_lastdate;
model.sites = allSites;
model.numberOfSites = report.numSites;
model.lastVisitsDate = report.lastDate;
}
function getNumberOfFilteredSites () {
return model.numberOfSites;
}
function getNumberOfPages() {
return Math.ceil(getNumberOfFilteredSites() / model.pageSize - 1);
}
function getCurrentPagingOffsetStart() {
return Math.ceil(model.currentPage * model.pageSize);
}
function getCurrentPagingOffsetEnd() {
var end = getCurrentPagingOffsetStart() + parseInt(model.pageSize, 10);
var max = getNumberOfFilteredSites();
if (end > max) {
end = max;
}
return parseInt(end, 10);
}
function previousPage() {
model.currentPage = model.currentPage - 1;
fetchAllSites();
}
function sortBy(metric) {
if (model.sortColumn == metric) {
model.reverse = !model.reverse;
}
model.sortColumn = metric;
fetchAllSites();
};
function previousPage() {
model.currentPage = model.currentPage - 1;
fetchAllSites();
}
function nextPage() {
model.currentPage = model.currentPage + 1;
fetchAllSites();
}
function searchSite (term) {
model.searchTerm = term;
model.currentPage = 0;
fetchAllSites();
}
function fetchAllSites() {
if (model.isLoading) {
piwikApi.abort();
cancelRefereshInterval();
}
model.isLoading = true;
model.errorLoadingSites = false;
var params = {
module: 'MultiSites',
action: 'getAllWithGroups',
hideMetricsDoc: '1',
filter_sort_order: 'asc',
filter_limit: model.pageSize,
filter_offset: getCurrentPagingOffsetStart(),
showColumns: 'label,nb_visits,nb_pageviews,visits_evolution,pageviews_evolution,revenue_evolution,nb_actions,revenue'
};
if (model.searchTerm) {
params.pattern = model.searchTerm;
}
if (model.sortColumn) {
params.filter_sort_column = model.sortColumn;
}
if (model.reverse) {
params.filter_sort_order = 'desc';
}
return piwikApi.fetch(params).then(function (response) {
updateWebsitesList(response);
}, onError)['finally'](function () {
model.isLoading = false;
if (model.refreshInterval && model.refreshInterval > 0) {
cancelRefereshInterval();
refreshPromise = $timeout(function () {
refreshPromise = null;
fetchAllSites(model.refreshInterval);
}, model.refreshInterval * 1000);
}
});
}
}
})();

View file

@ -0,0 +1,29 @@
/*!
* 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('MultiSitesDashboardController', MultiSitesDashboardController);
MultiSitesDashboardController.$inject = ['$scope', 'piwik', 'multisitesDashboardModel'];
function MultiSitesDashboardController($scope, piwik, multisitesDashboardModel){
$scope.model = multisitesDashboardModel;
$scope.evolutionSelector = 'visits_evolution';
$scope.hasSuperUserAccess = piwik.hasSuperUserAccess;
$scope.date = piwik.broadcast.getValueFromUrl('date');
$scope.idSite = piwik.broadcast.getValueFromUrl('idSite');
$scope.url = piwik.piwik_url;
$scope.period = piwik.period;
$scope.arePiwikProAdsEnabled = piwik.config && piwik.config.are_ads_enabled;
this.refresh = function (interval) {
multisitesDashboardModel.refreshInterval = interval;
multisitesDashboardModel.fetchAllSites();
};
}
})();

View file

@ -5,37 +5,37 @@
{{ 'General_AllWebsitesDashboard'|translate }}
<span class='smallTitle'
title="{{ 'General_EvolutionSummaryGeneric'|translate:('General_NVisits'|translate:model.totalVisits):date:model.lastVisits:model.lastVisitsDate:(model.totalVisits|evolution:model.lastVisits)}}"
ng-bind-html="'General_TotalVisitsPageviewsRevenue' | translate:('<strong>'+model.totalVisits+'</strong>'):('<strong>'+model.totalActions+'</strong>'):('<strong>' + model.totalRevenue + '</strong>')">
ng-bind-html="'General_TotalVisitsPageviewsActionsRevenue' | translate:('<strong>'+model.totalVisits+'</strong>'):('<strong>'+model.totalPageviews+'</strong>'):('<strong>'+model.totalActions+'</strong>'):('<strong>' + model.totalRevenue + '</strong>')">
</span>
</h2>
<table id="mt" class="dataTable" cellspacing="0">
<thead>
<tr>
<th id="names" class="label" ng-click="sortBy('label')" ng-class="{columnSorted: 'label' == predicate}">
<th id="names" class="label" ng-click="model.sortBy('label')" ng-class="{columnSorted: 'label' == model.sortColumn}">
<span class="heading">{{ 'General_Website'|translate }}</span>
<span ng-class="{multisites_asc: !reverse && 'label' == predicate, multisites_desc: reverse && 'label' == predicate}" class="arrow"></span>
<span ng-class="{multisites_asc: !model.reverse && 'label' == model.sortColumn, multisites_desc: model.reverse && 'label' == model.sortColumn}" class="arrow"></span>
</th>
<th id="visits" class="multisites-column" ng-click="sortBy('nb_visits')" ng-class="{columnSorted: 'nb_visits' == predicate}">
<th id="visits" class="multisites-column" ng-click="model.sortBy('nb_visits')" ng-class="{columnSorted: 'nb_visits' == model.sortColumn}">
<span ng-class="{multisites_asc: !model.reverse && 'nb_visits' == model.sortColumn, multisites_desc: model.reverse && 'nb_visits' == model.sortColumn}" class="arrow"></span>
<span class="heading">{{ 'General_ColumnNbVisits'|translate }}</span>
<span ng-class="{multisites_asc: !reverse && 'nb_visits' == predicate, multisites_desc: reverse && 'nb_visits' == predicate}" class="arrow"></span>
</th>
<th id="pageviews" class="multisites-column" ng-click="sortBy('nb_pageviews')" ng-class="{columnSorted: 'nb_pageviews' == predicate}">
<th id="pageviews" class="multisites-column" ng-click="model.sortBy('nb_pageviews')" ng-class="{columnSorted: 'nb_pageviews' == model.sortColumn}">
<span ng-class="{multisites_asc: !model.reverse && 'nb_pageviews' == model.sortColumn, multisites_desc: model.reverse && 'nb_pageviews' == model.sortColumn}" class="arrow"></span>
<span class="heading">{{ 'General_ColumnPageviews'|translate }}</span>
<span ng-class="{multisites_asc: !reverse && 'nb_pageviews' == predicate, multisites_desc: reverse && 'nb_pageviews' == predicate}" class="arrow"></span>
</th>
<th ng-if="displayRevenueColumn" id="revenue" class="multisites-column" ng-click="sortBy('revenue')" ng-class="{columnSorted: 'revenue' == predicate}">
<th ng-if="displayRevenueColumn" id="revenue" class="multisites-column" ng-click="model.sortBy('revenue')" ng-class="{columnSorted: 'revenue' == model.sortColumn}">
<span ng-class="{multisites_asc: !model.reverse && 'revenue' == model.sortColumn, multisites_desc: model.reverse && 'revenue' == model.sortColumn}" class="arrow"></span>
<span class="heading">{{ 'General_ColumnRevenue'|translate }}</span>
<span ng-class="{multisites_asc: !reverse && 'revenue' == predicate, multisites_desc: reverse && 'revenue' == predicate}" class="arrow"></span>
</th>
<th id="evolution" colspan="{{ showSparklines ? 2 : 1 }}" ng-class="{columnSorted: evolutionSelector == predicate}">
<span class="arrow" ng-class="{multisites_asc: !reverse && evolutionSelector == predicate, multisites_desc: reverse && evolutionSelector == predicate}"></span>
<th id="evolution" colspan="{{ showSparklines ? 2 : 1 }}" ng-class="{columnSorted: evolutionSelector == model.sortColumn}">
<span class="arrow" ng-class="{multisites_asc: !model.reverse && evolutionSelector == model.sortColumn, multisites_desc: model.reverse && evolutionSelector == model.sortColumn}"></span>
<span class="evolution"
ng-click="sortBy(evolutionSelector)"> {{ 'MultiSites_Evolution'|translate }}</span>
<select class="selector" id="evolution_selector" ng-model="evolutionSelector"
ng-change="predicate = evolutionSelector">
ng-click="model.sortBy(evolutionSelector)"> {{ 'MultiSites_Evolution'|translate }}</span>
<select class="selector native-select" id="evolution_selector" ng-model="evolutionSelector"
ng-change="model.sortBy(evolutionSelector)">
<option value="visits_evolution">{{ 'General_ColumnNbVisits'|translate }}</option>
<option value="pageviews_evolution">{{ 'General_ColumnPageviews'|translate }}</option>
<option ng-if="displayRevenueColumn" value="revenue_evolution">{{ 'General_ColumnRevenue'|translate }}</option>
@ -58,7 +58,16 @@
<tr ng-if="model.errorLoadingSites">
<td colspan="7">
<div class="notification system notification-error">
{{ 'General_ErrorRequest'|translate }}
{{ 'General_ErrorRequest'|translate:(''):('') }}
<br /><br />
{{ 'General_NeedMoreHelp'|translate }}
<a rel="noreferrer" target="_blank" href="https://piwik.org/faq/troubleshooting/faq_19489/">{{ 'General_Faq'|translate }}</a>
<a rel="noreferrer" target="_blank" href="http://forum.piwik.org/">{{ 'Feedback_CommunityHelp'|translate }}</a>
<span ng-show="arePiwikProAdsEnabled"> </span>
<a ng-show="arePiwikProAdsEnabled" rel="noreferrer" target="_blank" href="https://piwik.pro/?pk_campaign=Help&pk_medium=AjaxError&pk_content=MultiSites&pk_source=Piwik_App">{{ 'Feedback_ProfessionalHelp'|translate }}</a>.
</div>
</td>
</tr>
@ -68,10 +77,9 @@
piwik-multisites-site
date-sparkline="dateSparkline"
show-sparklines="showSparklines"
metric="predicate"
ng-class-odd="'columnodd'"
metric="model.sortColumn"
display-revenue-column="displayRevenueColumn"
ng-repeat="website in model.sites | orderBy:predicate:reverse | multiSitesGroupFilter:model.getCurrentPagingOffsetStart():model.pageSize">
ng-repeat="website in model.sites">
</tr>
</tbody>
@ -79,43 +87,25 @@
<tr ng-if="hasSuperUserAccess">
<td colspan="8" class="add_new_site">
<a href="{{ url }}?module=SitesManager&action=index&showaddsite=1&period={{ period }}&date={{ date }}">
<img src='plugins/UsersManager/images/add.png' alt=""/> {{ 'SitesManager_AddSite'|translate }}
<a href="{{ url }}?module=SitesManager&action=index&showaddsite=1&period={{ period }}&date={{ date }}&idSite={{ idSite }}">
<img src='plugins/Morpheus/images/add.png' alt=""/> {{ 'SitesManager_AddSite'|translate }}
</a>
</td>
</tr>
<tr ng-if="!hasSuperUserAccess">
<td colspan="8">
<td colspan="8" class="empty-row">
<br/>
</td>
</tr>
<tr>
<td colspan="8" class="site_search">
<input type="text"
ng-change="model.searchSite(searchTerm)"
ng-model="searchTerm"
placeholder="{{ 'Actions_SubmenuSitesearch' | translate }}">
<img title="Search"
ng-show="!searchTerm"
class="search_ico"
src="plugins/Zeitgeist/images/search_ico.png"/>
<img title="Clear"
ng-show="searchTerm"
ng-click="searchTerm='';model.searchSite('')"
class="reset"
src="plugins/CoreHome/images/reset_search.png"/>
</td>
</tr>
<tr row_id="last">
<td colspan="8" class="paging" ng-hide="model.numberOfPages() <= 1">
<span id="prev" class="previous" ng-hide="model.currentPage == 0" ng-click="model.previousPage()">
<span style="cursor:pointer;">&#171; {{ 'General_Previous'|translate }}</span>
</span>
<span class="dataTablePages">
<span id="counter">
{{ model.getCurrentPagingOffsetStart() }} - {{ model.getCurrentPagingOffsetEnd() }} of {{ model.getNumberOfFilteredSites() }}
{{ 'General_Pagination'|translate:model.getCurrentPagingOffsetStart():model.getCurrentPagingOffsetEnd():model.getNumberOfFilteredSites() }}
</span>
</span>
<span id="next" class="next" ng-hide="model.currentPage >= model.getNumberOfPages()" ng-click="model.nextPage()">
@ -123,6 +113,19 @@
</span>
</td>
</tr>
<tr row_id="last">
<td colspan="8" class="site_search">
<input type="text"
ng-model="searchTerm"
piwik-onenter="model.searchSite(searchTerm)"
placeholder="{{ 'Actions_SubmenuSitesearch' | translate }}">
<img title="{{ 'General_ClickToSearch' | translate }}"
ng-click="model.searchSite(searchTerm)"
class="search_ico"
src="plugins/Morpheus/images/search_ico.png"/>
</td>
</tr>
</tfoot>
</table>
</div>
</div>

View file

@ -0,0 +1,46 @@
/*!
* Piwik - free/libre analytics platform
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
/**
* Renders the multisites dashboard
* Example usage:
*
* <div piwik-multisites-dashboard
* display-revenue-column="true"
* show-sparklines="true"
* date-sparkline="true"
* page-size="50"
* auto-refresh-today-report="500" // or 0 to disable
* ></div>
*/
(function () {
angular.module('piwikApp').directive('piwikMultisitesDashboard', piwikMultisitesDashboard);
piwikMultisitesDashboard.$inject = ['piwik'];
function piwikMultisitesDashboard(piwik){
return {
restrict: 'A',
scope: {
displayRevenueColumn: '@',
showSparklines: '@',
dateSparkline: '@'
},
templateUrl: 'plugins/MultiSites/angularjs/dashboard/dashboard.directive.html?cb=' + piwik.cacheBuster,
controller: 'MultiSitesDashboardController',
link: function (scope, element, attrs, controller) {
if (attrs.pageSize) {
scope.model.pageSize = attrs.pageSize;
}
controller.refresh(attrs.autoRefreshTodayReport);
}
};
}
})();

View file

@ -3,13 +3,27 @@
font-size: 15px;
}
.widget {
#multisites {
padding: 15px;
}
}
#multisites {
border: 0;
padding: 0 15px;
font-size: 14px;
h2 {
border-bottom: 0px;
font-size: 24px;
}
.notification-error {
margin-top: 15px;
a {
text-decoration: underline !important;
}
}
.add_new_site,
@ -30,15 +44,32 @@
border: 0 !important;
}
.multisites-evolution,
.multisites-column {
text-align: right;
}
.multisites-evolution {
width:170px;
}
td, tr, .sparkline {
text-align: center;
vertical-align: middle;
padding: 1px;
padding: 2px 6px 2px 12px;
margin: 0;
}
th {
padding: 12px 6px 12px 12px;
}
td.empty-row {
border-bottom: none !important;
}
.indicator {
background: url(plugins/MultiSites/images/loading-blue.gif) no-repeat center;
background: url(plugins/Morpheus/images/loading-blue.gif) no-repeat center;
height: 20px;
width: 60px;
margin: auto;
@ -46,7 +77,9 @@
}
.paging {
padding: 20px;
padding: 5px;
font-size: 10px;
border-bottom: 0px !important;
.previous {
padding-right: 20px;
@ -56,23 +89,37 @@
}
}
.top_controls {
height: 10px;
th:first-child {
text-align:left;
}
th {
cursor: pointer;
border-left: 0px;
text-align: right;
border-bottom: 0px;
&#evolution {
text-align: center;
}
&.columnSorted {
font-weight: normal !important;
}
}
.site_search input {
margin-right: 0px;
margin-left: 25px;
padding-right: 25px;
}
.search_ico {
position: relative;
left: -25px;
margin-right: 0px;
margin-top: -1px;
cursor: pointer;
}
.reset {
position: relative;
@ -81,14 +128,6 @@
margin-right: 0px;
}
tr.columnodd:hover td, tr.columnodd td {
background: #F2F2F2 !important;
}
tr:hover td {
background: #FFF !important;
}
tr.group {
font-weight: bold;
height: 30px;
@ -97,7 +136,6 @@
padding-left: 50px;
}
td.multisites-label {
padding-left: 15px;
text-align: left;
width: 250px;
}
@ -141,28 +179,68 @@
}
.allWebsitesLoadingIndicator {
background: url(plugins/Zeitgeist/images/loading-blue.gif) no-repeat right 3px;
background: url(plugins/Morpheus/images/loading-blue.gif) no-repeat right 3px;
display: inline-block;
width: 16px;
height: 16px;
}
.heading {
display: inline-block;
display: inline;
margin-top: 4px;
}
#evolution_selector {
margin: -6px 0 0 5px;
}
.label .arrow {
margin-left: 2px;
}
.multisites_asc,
.multisites_desc {
width: 16px;
height: 13px;
float: none;
display: inline-block;
background-image: url(plugins/Zeitgeist/images/sortdesc.png);
vertical-align: top;
margin: 0px;
margin-left: 6px;
margin-top: -1px;
}
#evolution {
.multisites_asc,
.multisites_desc {
margin-right: 6px;
margin-left: 0px;
}
.evolution {
vertical-align: top;
}
}
.multisites_asc {
width: 16px;
height: 13px;
display: inline-block;
background-image: url(plugins/Zeitgeist/images/sortasc.png);
margin-top: -7px;
vertical-align: top;
}
.multisites_desc:after {
content: " \25BC";
font-size: 1px;
color: @theme-color-brand;
border-left: 4px solid rgba(0, 0, 0, 0);
border-right: 4px solid rgba(0, 0, 0, 0);
border-top: 5px solid @theme-color-brand;
}
.multisites_asc:after {
content: " \25BC";
font-size: 1px;
color: @theme-color-brand;
border-left: 4px solid rgba(0, 0, 0, 0);
border-right: 4px solid rgba(0, 0, 0, 0);
border-bottom: 5px solid @theme-color-brand;
}
div.sparkline {
float:none;
}
@ -182,4 +260,4 @@
#mt thead *:last-child {
border-top-right-radius: 7px;
}
}

View file

@ -1,55 +0,0 @@
/*!
* Piwik - Web Analytics
*
* @link http://piwik.org
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
/**
* Renders a single website row, for instance to be used within the MultiSites Dashboard.
*
* Usage:
* <div piwik-multisites-site>
* website="{label: 'Name', main_url: 'http://...', idsite: '...'}"
* evolution-metric="visits_evolution"
* show-sparklines="true"
* date-sparkline="2014-01-01,2014-02-02"
* display-revenue-column="true"
* </div>
*/
angular.module('piwikApp').directive('piwikMultisitesSite', function($document, piwik, $filter){
return {
restrict: 'AC',
replace: true,
scope: {
website: '=',
evolutionMetric: '=',
showSparklines: '=',
dateSparkline: '=',
displayRevenueColumn: '=',
metric: '='
},
templateUrl: 'plugins/MultiSites/angularjs/site/site.html?cb=' + piwik.cacheBuster,
controller: function ($scope) {
$scope.period = piwik.period;
$scope.date = piwik.broadcast.getValueFromUrl('date');
$scope.parseInt = parseInt;
this.getWebsite = function () {
return $scope.website;
};
$scope.sparklineImage = function(website){
var append = '';
var token_auth = piwik.broadcast.getValueFromUrl('token_auth');
if (token_auth.length) {
append = '&token_auth=' + token_auth;
}
return piwik.piwik_url + '?module=MultiSites&action=getEvolutionGraph&period=' + $scope.period + '&date=' + $scope.dateSparkline + '&evolutionBy=' +$scope.metric + '&columns=' + $scope.metric + '&idSite=' + website.idsite + '&idsite=' + website.idsite + '&viewDataTable=sparkline' + append + '&colors=' + encodeURIComponent(JSON.stringify(piwik.getSparklineColors()));
};
}
};
});

View file

@ -0,0 +1,47 @@
/*!
* 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('MultiSitesSiteController', MultiSitesSiteController);
MultiSitesSiteController.$inject = ['$scope', 'piwik'];
function MultiSitesSiteController($scope, piwik){
$scope.period = piwik.period;
$scope.date = piwik.broadcast.getValueFromUrl('date');
$scope.sparklineImage = sparklineImage;
$scope.website.label = piwik.helper.htmlDecode($scope.website.label);
this.getWebsite = function () {
return $scope.website;
};
function sparklineImage(website){
var append = '';
var token_auth = piwik.broadcast.getValueFromUrl('token_auth');
if (token_auth.length) {
append = '&token_auth=' + token_auth;
}
var metric = $scope.metric;
switch ($scope.evolutionMetric) {
case 'visits_evolution':
metric = 'nb_visits';
break;
case 'pageviews_evolution':
metric = 'nb_pageviews';
break;
case 'revenue_evolution':
metric = 'revenue';
break;
}
return piwik.piwik_url + '?module=MultiSites&action=getEvolutionGraph&period=' + $scope.period + '&date=' + $scope.dateSparkline + '&evolutionBy=' + metric + '&columns=' + metric + '&idSite=' + website.idsite + '&idsite=' + website.idsite + '&viewDataTable=sparkline' + append + '&colors=' + encodeURIComponent(JSON.stringify(piwik.getSparklineColors()));
}
}
})();

View file

@ -3,8 +3,8 @@
<a title="View reports" ng-href="index.php?module=CoreHome&action=index&date={{ date }}&period={{ period }}&idSite={{ website.idsite }}">{{ website.label }}</a>
<span style="width: 10px; margin-left:3px;">
<a target="_blank" title="{{ 'General_GoTo'|translate:website.main_url }}" ng-href="{{ website.main_url }}">
<img src="plugins/MultiSites/images/link.gif"/></a>
<a rel="noreferrer" target="_blank" title="{{ 'General_GoTo'|translate:website.main_url }}" ng-href="{{ website.main_url }}">
<img src="plugins/Morpheus/images/link.gif"/></a>
</span>
</td>
<td ng-if="website.isGroup" class="multisites-label label">
@ -20,20 +20,20 @@
{{ website.revenue }}
</td>
<td ng-if="period != 'range'" style="width:170px;">
<td ng-if="period != 'range'" class="multisites-evolution">
<div class="visits" ng-if="!website.isGroup">
<span ng-show="parseInt(website[evolutionMetric]) > 0"><img src="plugins/MultiSites/images/arrow_up.png" alt="" /> <span style="color: green;">{{ website[evolutionMetric] }}&nbsp;</span></span>
<span ng-show="parseInt(website[evolutionMetric]) == 0"><img src="plugins/MultiSites/images/stop.png" alt="" /> <span>{{ website[evolutionMetric] }}</span></span>
<span ng-show="parseInt(website[evolutionMetric]) < 0"><img src="plugins/MultiSites/images/arrow_down.png" alt="" /> <span style="color: red;">{{ website[evolutionMetric] }}&nbsp;</span></span>
<span ng-show="website[evolutionMetric] > 0"><img src="plugins/MultiSites/images/arrow_up.png" alt="" /> <span style="color: green;">{{ website[evolutionMetric] }}%</span></span>
<span ng-show="website[evolutionMetric] == 0"><img src="plugins/MultiSites/images/stop.png" alt="" /> <span>{{ website[evolutionMetric] }}%</span></span>
<span ng-show="website[evolutionMetric] < 0"><img src="plugins/MultiSites/images/arrow_down.png" alt="" /> <span style="color: red;">{{ website[evolutionMetric] }}%</span></span>
</div>
</td>
<td ng-if="showSparklines" style="width:180px;">
<div ng-if="!website.isGroup" class="sparkline" style="width: 100px; margin: auto;">
<a target="_blank" ng-href="index.php?module=CoreHome&action=index&date={{ date }}&period={{ period }}&idSite={{ website.idsite }}"
<a rel="noreferrer" target="_blank" ng-href="index.php?module=CoreHome&action=index&date={{ date }}&period={{ period }}&idSite={{ website.idsite }}"
title="{{ 'General_GoTo'|translate:('Dashboard_DashboardOf'|translate:website.label) }}">
<img alt="" ng-src="{{ sparklineImage(website) }}" width="100" height="25" />
</a>
</div>
</td>
</tr>
</tr>

View file

@ -0,0 +1,42 @@
/*!
* 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 single website row, for instance to be used within the MultiSites Dashboard.
*
* Usage:
* <div piwik-multisites-site>
* website="{label: 'Name', main_url: 'http://...', idsite: '...'}"
* evolution-metric="visits_evolution"
* show-sparklines="true"
* date-sparkline="2014-01-01,2014-02-02"
* display-revenue-column="true"
* </div>
*/
(function () {
angular.module('piwikApp').directive('piwikMultisitesSite', piwikMultisitesSite);
piwikMultisitesSite.$inject = ['piwik'];
function piwikMultisitesSite(piwik){
return {
restrict: 'AC',
replace: true,
scope: {
website: '=',
evolutionMetric: '=',
showSparklines: '=',
dateSparkline: '=',
displayRevenueColumn: '=',
metric: '='
},
templateUrl: 'plugins/MultiSites/angularjs/site/site.directive.html?cb=' + piwik.cacheBuster,
controller: 'MultiSitesSiteController'
};
}
})();