diff options
| author | Parth Gajjar <pga@openerp.com> | 2015-06-17 17:49:17 +0530 |
|---|---|---|
| committer | David Monjoie <dmo@odoo.com> | 2015-09-04 13:03:21 +0200 |
| commit | c6a0af45efb6b30fc67af6645a2d71d58e8f4065 (patch) | |
| tree | 37d091b674cfcafb5671e9cbf9ee4b06e442496a | |
| parent | bd0c6a2b242107be49df6b220da5218eceaf91e3 (diff) | |
[ADD] web_settings_dashboard: Settings Dashboard
11 files changed, 600 insertions, 0 deletions
diff --git a/addons/web_settings_dashboard/__init__.py b/addons/web_settings_dashboard/__init__.py new file mode 100644 index 00000000000..bde83af3aea --- /dev/null +++ b/addons/web_settings_dashboard/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +import controllers +import models diff --git a/addons/web_settings_dashboard/__openerp__.py b/addons/web_settings_dashboard/__openerp__.py new file mode 100644 index 00000000000..7de71e122e7 --- /dev/null +++ b/addons/web_settings_dashboard/__openerp__.py @@ -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, +} diff --git a/addons/web_settings_dashboard/controllers/__init__.py b/addons/web_settings_dashboard/controllers/__init__.py new file mode 100644 index 00000000000..a84d81a791b --- /dev/null +++ b/addons/web_settings_dashboard/controllers/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +import main diff --git a/addons/web_settings_dashboard/controllers/main.py b/addons/web_settings_dashboard/controllers/main.py new file mode 100644 index 00000000000..35db119ae14 --- /dev/null +++ b/addons/web_settings_dashboard/controllers/main.py @@ -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"), + }, + } diff --git a/addons/web_settings_dashboard/models/__init__.py b/addons/web_settings_dashboard/models/__init__.py new file mode 100644 index 00000000000..b0b7a73232a --- /dev/null +++ b/addons/web_settings_dashboard/models/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +import res_users diff --git a/addons/web_settings_dashboard/models/res_users.py b/addons/web_settings_dashboard/models/res_users.py new file mode 100644 index 00000000000..f42aceba94c --- /dev/null +++ b/addons/web_settings_dashboard/models/res_users.py @@ -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 diff --git a/addons/web_settings_dashboard/static/src/js/dashboard.js b/addons/web_settings_dashboard/static/src/js/dashboard.js new file mode 100644 index 00000000000..48f98c52417 --- /dev/null +++ b/addons/web_settings_dashboard/static/src/js/dashboard.js @@ -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, +}; + +}); diff --git a/addons/web_settings_dashboard/static/src/less/dashboard.less b/addons/web_settings_dashboard/static/src/less/dashboard.less new file mode 100644 index 00000000000..78016febbf5 --- /dev/null +++ b/addons/web_settings_dashboard/static/src/less/dashboard.less @@ -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 + } + } + } + } +} diff --git a/addons/web_settings_dashboard/static/src/xml/dashboard.xml b/addons/web_settings_dashboard/static/src/xml/dashboard.xml new file mode 100644 index 00000000000..ebec855e3c1 --- /dev/null +++ b/addons/web_settings_dashboard/static/src/xml/dashboard.xml @@ -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> diff --git a/addons/web_settings_dashboard/views/dashboard_templates.xml b/addons/web_settings_dashboard/views/dashboard_templates.xml new file mode 100644 index 00000000000..ff4f74b1063 --- /dev/null +++ b/addons/web_settings_dashboard/views/dashboard_templates.xml @@ -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> diff --git a/addons/web_settings_dashboard/views/dashboard_views.xml b/addons/web_settings_dashboard/views/dashboard_views.xml new file mode 100644 index 00000000000..3bf85772f4b --- /dev/null +++ b/addons/web_settings_dashboard/views/dashboard_views.xml @@ -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> |
