[ADD] web_settings_dashboard: Settings Dashboard

This commit is contained in:
Parth Gajjar
2015-09-04 13:03:21 +02:00
committed by David Monjoie
parent bd0c6a2b24
commit c6a0af45ef
11 changed files with 600 additions and 0 deletions
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
import controllers
import models
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
{
'name': 'Odoo Settings Dashboard',
'version': '1.0',
'summary': 'Quick actions for installing new app, adding users, completing planners, etc.',
'category': 'Tools',
'description':
"""
Odoo dashboard
==============
* Quick access to install apps
* Quick users add
* Access all planners at one place
* Quick access to the `App Store` and `Theme Store`
""",
'data': [
'views/dashboard_views.xml',
'views/dashboard_templates.xml',
],
'depends': ['web_planner'],
'qweb': ['static/src/xml/dashboard.xml'],
'auto_install': True,
}
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
import main
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from openerp import http
from openerp.http import request
class WebSettingsDashboard(http.Controller):
@http.route('/web_settings_dashboard/data', type='json', auth='user')
def web_settings_dashboard_data(self, **kw):
installed_apps = request.env['ir.module.module'].search_count([
('application', '=', True),
('state', 'in', ['installed', 'to upgrade', 'to remove'])
])
active_users = request.env['res.users'].search_count([('active', '=', True), ('login_date', '!=', False)])
pending_users = request.env['res.users'].search([('login_date', '=', False)], order="create_date desc")
return {
'apps': {
'installed_apps': installed_apps
},
'users_info': {
'active_users': active_users,
'pending_users': zip(pending_users.mapped('id'), pending_users.mapped('login')),
'user_form_view_id': request.env['ir.model.data'].xmlid_to_res_id("base.view_users_form"),
},
}
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
import res_users
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from openerp import models, api, _
from openerp.exceptions import RedirectWarning
from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT
class Users(models.Model):
_inherit = 'res.users'
@api.model
def web_dashboard_create_users(self, emails):
# Reactivate already existing users if needed
deactivated_users = self.with_context(active_test=False).search([('active', '=', False), '|', ('login', 'in', emails), ('email', 'in', emails)])
for user in deactivated_users:
user.active = True
new_emails = set(emails) - set(deactivated_users.mapped('email'))
# Process new email addresses : create new users
for email in new_emails:
default_values = {'login': email, 'name': email.split('@')[0], 'email': email, 'active': True}
user = self.create(default_values)
return True
@@ -0,0 +1,266 @@
odoo.define('web_settings_dashboard', function (require) {
"use strict";
var core = require('web.core');
var Widget = require('web.Widget');
var Model = require('web.Model');
var session = require('web.session');
var PlannerCommon = require('web.planner.common');
var framework = require('web.framework');
var webclient = require('web.web_client');
var PlannerDialog = PlannerCommon.PlannerDialog;
var QWeb = core.qweb;
var _t = core._t;
var Dashboard = Widget.extend({
template: 'DashboardMain',
events: {
'click .o_browse_apps': 'on_new_apps',
},
start: function(){
return this.load(['apps', 'invitations', 'planner', 'share'])
},
load: function(dashboards){
var self = this;
var loading_done = new $.Deferred();
session.rpc("/web_settings_dashboard/data", {}).then(function (data) {
// Load each dashboard
var all_dashboards_defs = []
_.each(dashboards, function(dashboard) {
var dashboard_def = self['load_' + dashboard](data);
if (dashboard_def) {
all_dashboards_defs.push(dashboard_def);
}
});
// Resolve loading_done when all dashboards defs are resolved
$.when.apply($, all_dashboards_defs).then(function() {
loading_done.resolve();
});
});
return loading_done;
},
load_apps: function(data){
this.$('.o_web_settings_dashboard_apps').append(QWeb.render("DashboardApps", data['apps']));
},
load_share: function(data){
return new DashboardShare(this, {}).replace(this.$('.o_web_settings_dashboard_share'));
},
load_invitations: function(data){
return new DashboardInvitations(this, data['users_info']).replace(this.$('.o_web_settings_dashboard_invitations'));
},
load_planner: function(data){
return new DashboardPlanner(this, data['planner']).replace(this.$('.o_web_settings_dashboard_planner'));
},
on_new_apps: function(){
this.do_action('base.open_module_tree');
}
});
var DashboardInvitations = Widget.extend({
template: 'DashboardInvitations',
events: {
'click .o_web_settings_dashboard_invitations': 'send_invitations',
'click .o_web_settings_dashboard_access_rights': 'on_access_rights_clicked',
'click .o_web_settings_dashboard_user': 'on_user_clicked',
},
init: function(parent, data){
this.data = data;
this.parent = parent;
return this._super.apply(this, arguments);
},
send_invitations: function(e){
var self = this;
var $target = $(e.currentTarget);
var user_emails = _.filter(this.$('#user_emails').val().split(/[\n, ]/), function(email){
return email != "";
});
var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
var is_valid_emails = _.every(user_emails, function(email) {
return re.test(email);
});
if (is_valid_emails) {
// Disable button
$target.prop('disabled', true);
$target.find('i.fa-cog').removeClass('hidden');
// Try to create user accountst
new Model("res.users")
.call("web_dashboard_create_users", [user_emails])
.then(function() {
self.reload();
})
.always(function() {
// Re-enable button
self.$('.o_web_settings_dashboard_invitations').prop('disabled', false);
self.$('i.fa-cog').addClass('hidden');
});
}
else {
this.do_warn(_t("Please provide valid email addresses"), "");
}
},
on_access_rights_clicked: function (e) {
var self = this;
e.preventDefault();
this.do_action('base.action_res_users', {
on_reverse_breadcrumb: function(){ return self.reload();}
});
},
on_user_clicked: function (e) {
var self = this;
e.preventDefault();
var user_id = $(e.currentTarget).data('user-id');
var action = {
type: 'ir.actions.act_window',
view_type: 'form',
view_mode: 'form',
res_model: 'res.users',
views: [[this.data.user_form_view_id, 'form']],
res_id: user_id,
}
this.do_action(action,{
on_reverse_breadcrumb: function(){ return self.reload();}
});
},
reload:function(){
return this.parent.load(['invitations']);
},
});
var DashboardPlanner = Widget.extend({
template: 'DashboardPlanner',
events: {
'click .o_web_settings_dashboard_progress_title,.progress': 'on_planner_clicked',
},
init: function(parent, data){
this.data = data;
this.parent = parent;
this.planner_by_menu = {};
this._super.apply(this, arguments);
},
willStart:function(){
var self = this;
return new Model('web.planner').query().all().then(function(res) {
self.planners = res;
_.each(self.planners, function(planner) {
self.planner_by_menu[planner.menu_id[0]] = planner;
self.planner_by_menu[planner.menu_id[0]].data = $.parseJSON(planner.data) || {};
});
self.set_overall_progress();
});
},
update_planner_progress: function(){
this.set_overall_progress();
this.$('.o_web_settings_dashboard_planners_list').replaceWith(
QWeb.render("DashboardPlanner.PlannersList", {'planners': this.planners})
);
},
set_overall_progress: function(){
var self = this;
this.sort_planners_list();
var average = _.reduce(self.planners, function(memo, planner) {
return planner.progress + memo;
}, 0) / (self.planners.length || 1);
self.overall_progress = Math.floor(average);
self.$('.o_web_settings_dashboard_planner_overall_progress').text(self.overall_progress);
},
sort_planners_list: function(){
// sort planners alphabetically but with fully completed planners at the end:
this.planners = _.sortBy(this.planners, function(planner){return (planner.progress == 100) + planner.name});
},
on_planner_clicked: function(e){
var menu_id = $(e.currentTarget).attr('data-menu-id');
// Setup the planner if we didn't do it yet
if (this.planner && this.planner.menu_id[0] == menu_id) {
this.dialog.$el.modal('show');
}
else {
this.setup_planner(menu_id);
}
},
setup_planner: function(menu_id){
var self = this;
this.planner = self.planner_by_menu[menu_id];
if (this.dialog) {
this.dialog.destroy()
}
this.dialog = new PlannerDialog(this, this.planner);
this.dialog.on("planner_progress_changed", this, function(percent) {
self.planner.progress = percent;
self.update_planner_progress();
});
this.dialog.appendTo(webclient.$el).then(function() {
self.dialog.$el.modal('show');
});
}
});
var DashboardShare = Widget.extend({
template: 'DashboardShare',
events: {
'click .tw_share': 'share_twitter',
'click .fb_share': 'share_facebook',
'click .li_share': 'share_linkedin',
},
init: function(parent, data){
this.data = data;
this.parent = parent;
this.share_url = 'http://www.odoo.com/';
this.share_text = encodeURIComponent("#IamUsingOdoo, an Open-Source Web App that manages my Sales, Projects, Accounting, Website, Warehouse, Shop and more");
},
share_twitter: function(){
var popup_url = _.str.sprintf( 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=%s %s',this.share_text,this.share_url);
this.sharer(popup_url);
},
share_facebook: function(){
var popup_url = _.str.sprintf('https://www.facebook.com/sharer/sharer.php?u=%s', encodeURIComponent(this.share_url));
this.sharer(popup_url);
},
share_linkedin: function(){
var popup_url = _.str.sprintf('http://www.linkedin.com/shareArticle?mini=true&url=%s&title=I am using odoo&summary=%s&source=www.odoo.com', encodeURIComponent(this.share_url), this.share_text);
this.sharer(popup_url);
},
sharer: function(popup_url){
window.open(
popup_url,
'Share Dialog',
'width=600,height=400'); // We have to add a size otherwise the window pops in a new tab
}
});
core.action_registry.add('web_settings_dashboard.main', Dashboard);
return {
Dashboard: Dashboard,
DashboardInvitations: DashboardInvitations,
DashboardPlanner: DashboardPlanner,
DashboardShare: DashboardShare,
};
});
@@ -0,0 +1,97 @@
.o_web_settings_dashboard {
@bg-dashboard-col: #FFF;
.o_web_settings_dashboard_card() {
box-shadow: 0 1px 2px 0 rgba(0,0,0,.14);
background: @bg-dashboard-col;
border-radius: 2px;
}
background: #F7F7F7;
height: 100%;
.o_web_settings_dashboard_col {
padding: 10px 8px;
> div > hr {
margin: 12px -18px 15px -18px;
border-color: #CBCBCB;
}
> div:first-child {
.o_web_settings_dashboard_card();
padding: 25px 18px 18px 18px;
.o_web_settings_dashboard_header {
font-size: 22px;
margin-bottom: 8px;
}
}
.o_web_settings_dashboard_pills {
margin-top: 10px;
> li > a {
.o_web_settings_dashboard_card();
padding: 10px;
> i.fa {
vertical-align: middle;
font-size: 1.5em;
}
}
@media (max-width: 1200px) {
> li {
width: 100%;
margin-bottom: 5px;
}
}
}
.btn.o_browse_apps, .btn.o_web_settings_dashboard_invitations {
margin-top: 12px;
}
.o_web_settings_dashboard_compact_subtitle {
line-height: 12px;
display: block;
}
.o_web_settings_dashboard_invitations {
.o_web_settings_dashboard_invitation_form {
text-align: left;
> textarea {
resize: vertical;
width: 100%;
}
}
a.o_web_settings_dashboard_user {
line-height: 24px;
}
}
.o_web_settings_dashboard_planner {
.progress {
height: 10px;
background-color: darken(@bg-dashboard-col, 15%);
cursor: pointer;
margin-bottom: 30px;
}
.o_web_settings_dashboard_planner_progress_text {
font-size: 18px;
padding-right: 0px;
padding-top: 3px;
}
.o_web_settings_dashboard_planner_progress_bar {
padding-left: 10px;
}
.o_web_settings_dashboard_progress_title {
font-weight: bold;
cursor: pointer;
color: @odoo-brand-optional;
}
.o_web_settings_dashboard_progress_100 {
-webkit-filter: grayscale(100%);
filter: grayscale(100%);
&.o_web_settings_dashboard_planner_progress_text {
font-size: 15px
}
}
}
}
}
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-name="DashboardMain">
<div class="container-fluid o_web_settings_dashboard">
<div class="row">
<div class="col-md-3 col-sm-6 col-xs-12 o_web_settings_dashboard_col"> <div class="text-center o_web_settings_dashboard_apps"></div>
<ul class="nav nav-pills o_web_settings_dashboard_pills">
<li role="presentation" class="text-center"><a href="https://www.odoo.com/apps/modules" target="_blank"><i class="fa fa-rocket fa-2x text-muted"/> App store </a></li>
<li role="presentation" class="text-center pull-right"><a href="https://www.odoo.com/apps/themes" target="_blank"><i class="fa fa-picture-o fa-2x text-muted"/> Theme store</a></li>
</ul>
</div>
<div class="col-md-3 col-sm-6 col-xs-12 o_web_settings_dashboard_col">
<div class="text-center o_web_settings_dashboard_invitations"></div>
</div>
<div class="col-md-3 col-sm-6 col-xs-12 o_web_settings_dashboard_col">
<div class="text-center o_web_settings_dashboard_planner"></div>
</div>
<div class="col-md-3 col-sm-6 col-xs-12 o_web_settings_dashboard_col">
<div class="text-center o_web_settings_dashboard_share"></div>
</div>
</div>
</div>
</t>
<t t-name="DashboardApps">
<i class="fa fa-cog fa-4x text-muted"></i>
<div class="o_web_settings_dashboard_header">
<t t-if="installed_apps">
<t t-esc="installed_apps"></t>
<t t-if="installed_apps == 1">Installed App</t>
<t t-if="installed_apps > 1">Installed Apps</t>
</t>
<t t-if="! installed_apps">
No app installed
</t>
</div>
<div>
<a class="btn btn-primary btn-block o_browse_apps" role="button"><strong>Browse Apps</strong></a>
</div>
</t>
<t t-name="DashboardInvitations">
<div class="text-center o_web_settings_dashboard_invitations">
<i class="fa fa-users fa-4x text-muted"></i>
<div class="o_web_settings_dashboard_header">
<t t-set="active_users" t-value="widget.data.active_users"></t>
<t t-set="pending_users" t-value="widget.data.pending_users"></t>
<t t-if="active_users">
<t t-esc="active_users"></t> Active User<t t-if="active_users > 1">s</t>
</t>
</div>
<div class="text-center">
<a href="#" class="o_web_settings_dashboard_access_rights"> Manage access rights</a>
</div>
<hr/>
<div class="o_web_settings_dashboard_invitation_form">
<strong>Add new users</strong> <small class="text-muted">(with default rights)</small>
<textarea rows="3" id="user_emails" placeholder="Enter e-mail addresses"></textarea>
<button class="btn btn-primary btn-block o_web_settings_dashboard_invitations" role="button"> <strong><i class="fa fa-cog fa-spin hidden"/> Add Users</strong></button>
</div>
<div>
<small class="o_web_settings_dashboard_pending_users text-muted text-center">
<t t-if="pending_users.length">
<br/>
<strong>Pending users:</strong><br/>
<t t-foreach="pending_users" t-as="pending">
<a href="#"><div class="o_web_settings_dashboard_user" t-att-data-user-id="pending[0]"> <t t-esc="pending[1]"/></div></a>
</t>
</t>
</small>
</div>
</div>
</t>
<t t-name="DashboardPlanner">
<div class="text-center o_web_settings_dashboard_planner">
<i class="fa fa-info-circle fa-4x text-muted"></i>
<div class="o_web_settings_dashboard_header">
<span class="o_web_settings_dashboard_planner_overall_progress"><t t-esc="widget.overall_progress"></t></span>%
Implementation
</div>
<div>
<small class="text-muted text-center o_web_settings_dashboard_compact_subtitle">
Follow these Implementation Guides to get the maximum value for your business.
</small>
</div>
<hr/>
<t t-set="planners" t-value="widget.planners"/>
<t t-call="DashboardPlanner.PlannersList"/>
</div>
</t>
<t t-name="DashboardPlanner.PlannersList">
<div class="row o_web_settings_dashboard_planners_list">
<t t-if="!planners.length">
<div>You need to install some apps first.</div>
</t>
<t t-if="planners.length" t-foreach="planners" t-as="p">
<div t-attf-class="col-xs-2 col-md-3 col-lg-2 o_web_settings_dashboard_planner_progress_text o_web_settings_dashboard_progress_#{p.progress}">
<t t-esc="p.progress"/>%
</div>
<div t-attf-class="col-xs-10 col-md-9 col-lg-10 o_web_settings_dashboard_planner_progress_bar o_web_settings_dashboard_progress_#{p.progress}">
<div class="o_web_settings_dashboard_progress_title text-left" t-att-data-menu-id="p.menu_id[0]">
<t t-esc="p.menu_id[1]"></t> <i class="fa fa-arrow-right pull-right"/>
</div>
<div class="progress" t-att-data-menu-id="p.menu_id[0]">
<div class="progress-bar" role="progressbar" t-attf-style="width:#{p.progress}%">
</div>
</div>
</div>
</t>
</div>
</t>
<t t-name="DashboardShare">
<div class="text-center o_web_settings_dashboard_share">
<i class="fa fa-share-alt fa-4x text-muted"/>
<div class="o_web_settings_dashboard_header">Social Share</div>
<div>
<small class="text-muted text-center o_web_settings_dashboard_compact_subtitle">
Help us spread the word: Share Odoo's awesomeness with your friends!
</small>
</div>
<hr/>
<div class="row mt16">
<div class="col-xs-4"><a><i class="fa fa-twitter-square fa-4x tw_share"/></a></div>
<div class="col-xs-4"><a><i class="fa fa-facebook-square fa-4x fb_share"/></a></div>
<div class="col-xs-4"><a><i class="fa fa-linkedin-square fa-4x li_share"/></a></div>
</div>
</div>
</t>
</templates>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_backend" name="Web Settings Dashboard Assets" inherit_id="web.assets_backend" priority="17">
<xpath expr="." position="inside">
<link href="/web_settings_dashboard/static/src/less/dashboard.less" rel="stylesheet" type="text/less"/>
<script type="text/javascript" src="/web_settings_dashboard/static/src/js/dashboard.js"></script>
</xpath>
</template>
</odoo>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="web_settings_dashboard_action" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">web_settings_dashboard.main</field>
</record>
<menuitem name="Dashboard" id="web_dashboard_menu" action="web_settings_dashboard_action" parent="base.menu_administration" sequence="-1" />
</odoo>