diff options
| author | Frederic van der Essen <fva@openerp.com / fvdessen+o@gmail.com> | 2014-08-25 16:42:57 +0200 |
|---|---|---|
| committer | Frederic van der Essen <fva@openerp.com / fvdessen+o@gmail.com> | 2014-08-25 16:42:57 +0200 |
| commit | d7f93defb899b2b48a14d679e5dffffdb4a00eff (patch) | |
| tree | f8572183b480477622917a1f504cf49a8e202f6e /addons/web_tip | |
| parent | 2b67528fe9e366829d44d8aba756bc6e359b4c61 (diff) | |
| parent | 36146b3fadc47543f4b696ab75c31b3459afb15b (diff) | |
Merge branch 'master' into master-pos-new-payment-screen-fva
Conflicts:
addons/point_of_sale/point_of_sale.py
addons/point_of_sale/point_of_sale_view.xml
Diffstat (limited to 'addons/web_tip')
| -rw-r--r-- | addons/web_tip/__init__.py | 1 | ||||
| -rw-r--r-- | addons/web_tip/__openerp__.py | 37 | ||||
| -rw-r--r-- | addons/web_tip/security/ir.model.access.csv | 2 | ||||
| -rw-r--r-- | addons/web_tip/static/src/css/tip.css | 56 | ||||
| -rw-r--r-- | addons/web_tip/static/src/js/tip.js | 254 | ||||
| -rw-r--r-- | addons/web_tip/views/tip.xml | 11 | ||||
| -rw-r--r-- | addons/web_tip/web_tip.py | 53 | ||||
| -rw-r--r-- | addons/web_tip/web_tip_view.xml | 66 |
8 files changed, 480 insertions, 0 deletions
diff --git a/addons/web_tip/__init__.py b/addons/web_tip/__init__.py new file mode 100644 index 00000000000..f8f6f9dbb55 --- /dev/null +++ b/addons/web_tip/__init__.py @@ -0,0 +1 @@ +import web_tip diff --git a/addons/web_tip/__openerp__.py b/addons/web_tip/__openerp__.py new file mode 100644 index 00000000000..4ccc8e6b3b9 --- /dev/null +++ b/addons/web_tip/__openerp__.py @@ -0,0 +1,37 @@ +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013 OpenERP SA (<http://openerp.com>). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## +{ + 'name': 'Tips', + 'category': 'Usability', + 'description': """ +OpenERP Web tips. +======================== + +""", + 'version': '0.1', + 'author': 'OpenERP SA', + 'depends': ['web'], + 'data': [ + 'security/ir.model.access.csv', + 'views/tip.xml', + 'web_tip_view.xml' + ], + 'auto_install': True +} diff --git a/addons/web_tip/security/ir.model.access.csv b/addons/web_tip/security/ir.model.access.csv new file mode 100644 index 00000000000..3408fe5a52d --- /dev/null +++ b/addons/web_tip/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_web_tip,access_web_tip,model_web_tip,,1,1,0,0 diff --git a/addons/web_tip/static/src/css/tip.css b/addons/web_tip/static/src/css/tip.css new file mode 100644 index 00000000000..089b7133be7 --- /dev/null +++ b/addons/web_tip/static/src/css/tip.css @@ -0,0 +1,56 @@ +.oe_tip_overlay { + top: 0; + bottom: 0; + left: 0; + right: 0; + position: absolute; + opacity: 0.8; + z-index: 1001; + background-color: #000; + background: -moz-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -webkit-gradient(radial,center center,0px,center center,100%,color-stop(0%,rgba(0,0,0,0.4)),color-stop(100%,rgba(0,0,0,0.9))); + background: -webkit-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -o-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: -ms-radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + background: radial-gradient(center,ellipse cover,rgba(0,0,0,0.4) 0,rgba(0,0,0,0.9) 100%); + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#66000000',endColorstr='#e6000000',GradientType=1); + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; + filter: alpha(opacity=50); + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.oe_tip_helper { + position: absolute; + z-index: 1008; + background-color: #FFF; + background-color: rgba(255,255,255,.9); + border: 1px solid #777; + border: 1px solid rgba(0,0,0,.5); + border-radius: 4px; + box-shadow: 0 2px 15px rgba(0,0,0,.4); + -webkit-transition: all 0.3s ease-out; + -moz-transition: all 0.3s ease-out; + -ms-transition: all 0.3s ease-out; + -o-transition: all 0.3s ease-out; + transition: all 0.3s ease-out; +} + +.oe_tip_close { + padding: 5px 5px 10px 10px !important; + margin-top: -15px; + margin-right: -15px; +} + +.oe_tip_fix_parent { + z-index: auto !important; + opacity: 1.0 !important; +} + +.oe_tip_show_element { + z-index: 1011 !important; + position: relative; +} diff --git a/addons/web_tip/static/src/js/tip.js b/addons/web_tip/static/src/js/tip.js new file mode 100644 index 00000000000..a07eb533039 --- /dev/null +++ b/addons/web_tip/static/src/js/tip.js @@ -0,0 +1,254 @@ +(function() { + + var instance = openerp; + + instance.web.Tip = instance.web.Class.extend({ + init: function() { + var self = this; + self.tips = []; + self.tip_mutex = new $.Mutex(); + self.$overlay = null; + self.$element = null; + + var Tips = new instance.web.Model('web.tip'); + Tips.query(['title', 'description', 'action_id', 'model', 'type', 'mode', 'trigger_selector', + 'highlight_selector', 'end_selector', 'end_event', 'placement', 'is_consumed']) + .all().then(function(tips) { + self.tips = tips; + }) + ; + + instance.web.bus.on('action', this, function(action) { + self.on_action(action); + }); + + instance.web.bus.on('view_shown', this, function(view) { + if (_.keys(view.fields_view).length === 0) { + view.on('view_loaded', this, function(fields_view) { + self.on_view(view); + }); + } else { + self.on_view(view); + } + + view.on('switch_mode', this, function() { + }); + }); + + instance.web.bus.on('view_switch_mode', this, function(viewManager, mode) { + self.on_switch(viewManager, mode); + }); + + instance.web.bus.on('form_view_shown', this, function(formView) { + self.on_form_view(formView); + }); + + instance.web.bus.on('form_view_saved', this, function(formView) { + self.on_form_view(formView); + }); + }, + + // stub + on_action: function(action) { + var self = this; + var action_id = action.id; + var model = action.res_model; + }, + + on_view: function(view) { + var self = this; + var fields_view = view.fields_view; + var action_id = view.ViewManager.action ? view.ViewManager.action.id : null; + var model = fields_view.model; + + // kanban + if(fields_view.type === 'kanban') { + var dataset_def = $.Deferred(); + var groups_def = $.Deferred(); + view.on("kanban_dataset_processed", self, function() { + var length = view.dataset.ids.length; + dataset_def.resolve(length); + }); + view.on('kanban_groups_processed', self, function() { + groups_def.resolve(); + }); + dataset_def.done(function(length) { + self.eval_tip(action_id, model, fields_view.type); + }); + groups_def.done(function() { + self.eval_tip(action_id, model, fields_view.type); + }); + } else if (fields_view.type === 'tree') { + view.on('view_list_rendered', self, function() { + self.eval_tip(action_id, model, fields_view.type); + }); + } + }, + + on_form_view: function(formView) { + var self = this; + var model = formView.model; + var type = formView.datarecord.type ? formView.datarecord.type : null; + var mode = 'form'; + self.eval_tip(null, model, mode, type); + }, + + // stub + on_switch: function (viewManager, mode) { + var self = this; + var action = viewManager.action; + var action_id = action.id; + var model = action.res_model; + }, + + eval_tip: function(action_id, model, mode, type) { + var self = this; + var filter = {}; + var valid_tips = []; + var tips = []; + if (action_id) { + valid_tips = _.filter(self.tips, function (tip) { + return tip.action_id[0] === action_id; + }); + } + + filter.model = model; + filter.mode = mode; + tips = _.where(self.tips, filter); + if (type) { + tips = _.filter(tips, function(tip) { + if (!tip.type) { + return true; + } + return tip.type === type; + }); + } + + valid_tips = _.uniq(valid_tips.concat(tips)); + _.each(valid_tips, function(tip) { + if (!tip.is_consumed) { + self.add_tip(tip); + } + }); + }, + + + add_tip: function(tip) { + var self = this; + self.tip_mutex.exec(function() { + return $.when(self.do_tip(tip)); + }); + }, + + do_tip: function (tip) { + var self = this; + var def = $.Deferred(); + var Tips = new instance.web.Model('web.tip'); + var highlight_selector = tip.highlight_selector; + var triggers = tip.trigger_selector ? tip.trigger_selector.split(',') : []; + var trigger_tip = true; + + if(!$(highlight_selector).length > 0) { + return def.reject(); + } + for (var i = 0; i < triggers.length; i++) { + if(!$(triggers[i]).length > 0) { + trigger_tip = false; + } + } + + if (trigger_tip) { + self.$element = $(highlight_selector).first(); + var _top = self.$element.offset().top -5; + var _left = self.$element.offset().left -5; + var _width = self.$element.outerWidth() + 10; + var _height = self.$element.outerHeight() + 10; + + self.$helper = $("<div>", { class: 'oe_tip_helper' }); + self.$element.after(self.$helper); + self.$helper.offset({top: _top , left: _left}); + self.$helper.width(_width); + self.$helper.height(_height); + + self.$overlay = $("<div>", { class: 'oe_tip_overlay' }); + $('body').append(self.$overlay); + self.$element.addClass('oe_tip_show_element'); + + // fix the stacking context problem + _.each(self.$element.parentsUntil('body'), function(el) { + var zIndex = $(el).css('z-index'); + var opacity = parseFloat($(el).css('opacity')); + + if (/[0-9]+/.test(zIndex) || opacity < 1) { + $(el).addClass('oe_tip_fix_parent'); + } + }); + + self.$element.popover({ + placement: tip.placement, + title: tip.title, + content: tip.description, + html: true, + container: 'body', + }).popover("show"); + + var $cross = $('<button type="button" class="close">×</button>'); + $cross.addClass('oe_tip_close'); + + if (tip.title) { + $('.popover-title').prepend($cross); + } else { + $('.popover-content').prepend($cross); + } + + // consume tip + tip.end_selector = tip.end_selector ? tip.end_selector : tip.highlight_selector; + $(tip.end_selector).one(tip.end_event, function($ev) { + self.end_tip(tip); + def.resolve(); + }); + + // dismiss tip + $cross.on('click', function($ev) { + self.end_tip(tip); + def.resolve(); + }); + self.$overlay.on('click', function($ev) { + self.end_tip(tip); + def.resolve(); + }); + $(document).on('keyup.web_tip', function($ev) { + if ($ev.which === 27) { // esc + self.end_tip(tip); + def.resolve(); + } + }); + } else { + def.reject(); + } + return def; + }, + + end_tip: function(tip) { + var self = this; + var Tips = new instance.web.Model('web.tip'); + self.$element.popover('destroy'); + self.$overlay.remove(); + self.$helper.remove(); + self.$element.removeClass('oe_tip_show_element'); + _.each($('.oe_tip_fix_parent'), function(el) { + $(el).removeClass('oe_tip_fix_parent'); + }); + $(document).off('keyup.web_tip'); + Tips.call('consume', [tip.id], {}); + tip.is_consumed = true; + } + }); + + instance.web.WebClient = instance.web.WebClient.extend({ + show_application: function() { + this._super(); + this.tip_handler = new instance.web.Tip(); + } + }); +})(); diff --git a/addons/web_tip/views/tip.xml b/addons/web_tip/views/tip.xml new file mode 100644 index 00000000000..49159b395a4 --- /dev/null +++ b/addons/web_tip/views/tip.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <template id="assets_backend" name="tip assets" inherit_id="web.assets_backend"> + <xpath expr="." position="inside"> + <link rel="stylesheet" href="/web_tip/static/src/css/tip.css"/> + <script type="text/javascript" src="/web_tip/static/src/js/tip.js"></script> + </xpath> + </template> + </data> +</openerp> diff --git a/addons/web_tip/web_tip.py b/addons/web_tip/web_tip.py new file mode 100644 index 00000000000..e3a074160c2 --- /dev/null +++ b/addons/web_tip/web_tip.py @@ -0,0 +1,53 @@ +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2009-Today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/> +# +############################################################################## + +from openerp import models, fields, api + + +class tip(models.Model): + _name = 'web.tip' + _description = 'Tips' + + @api.one + @api.depends('user_ids') + def _is_consumed(self): + if self.env.uid in self.user_ids: + self.is_consumed = True + else: + self.is_consumed = False + + title = fields.Char('Tip title') + description = fields.Html('Tip Description', required=True) + action_id = fields.Many2one('ir.actions.act_window', string="Action", + help="The action that will trigger the tip") + model = fields.Char("Model", help="Model name on which to trigger the tip, e.g. 'res.partner'.") + type = fields.Char("Type", help="Model type, e.g. lead or opportunity for crm.lead") + mode = fields.Char("Mode", help="Mode, e.g. kanban, form") + trigger_selector = fields.Char('Trigger selector', help='CSS selectors used to trigger the tip, separated by a comma (ANDed).') + highlight_selector = fields.Char('Highlight selector', help='CSS selector for the element to highlight') + end_selector = fields.Char('End selector', help='CSS selector used to end the tip') + end_event = fields.Char('End event', help='Event to end the tip', default='click') + placement = fields.Char('Placement', help='Popover placement, bottom, top, left or right', default='auto') + user_ids = fields.Many2many('res.users', string='Consumed by') + is_consumed = fields.Boolean(string='Tip consumed', compute='_is_consumed') + + @api.multi + def consume(self): + self.write({'user_ids': [(4, self.env.uid)]}) diff --git a/addons/web_tip/web_tip_view.xml b/addons/web_tip/web_tip_view.xml new file mode 100644 index 00000000000..246116f9566 --- /dev/null +++ b/addons/web_tip/web_tip_view.xml @@ -0,0 +1,66 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + <record id="edit_tip_form" model="ir.ui.view"> + <field name="model">web.tip</field> + <field name="arch" type="xml"> + <form string="Menu"> + <sheet> + <group> + <group> + <field name="title"/> + <field name="description"/> + <field name="action_id"/> + </group> + <group> + <field name="model"/> + <field name="type"/> + <field name="mode"/> + </group> + </group> + <group> + <field name="trigger_selector"/> + <field name="highlight_selector"/> + <field name="end_selector"/> + <field name="end_event"/> + <field name="placement"/> + </group> + <notebook> + <page string="Consumed by"> + <field name="user_ids"/> + </page> + </notebook> + </sheet> + </form> + </field> + </record> + <record id="edit_tip_list" model="ir.ui.view"> + <field name="model">web.tip</field> + <field eval="8" name="priority"/> + <field name="arch" type="xml"> + <tree string="Menu"> + <field name="model"/> + <field name="mode"/> + </tree> + </field> + </record> + <record id="edit_tip_search" model="ir.ui.view"> + <field name="name">web.tip.search</field> + <field name="model">web.tip</field> + <field name="arch" type="xml"> + <search string="Tip"> + <field name="model"/> + <field name="mode"/> + </search> + </field> + </record> + <record id="edit_tip_action" model="ir.actions.act_window"> + <field name="name">Tips</field> + <field name="res_model">web.tip</field> + <field name="view_type">form</field> + <field name="view_id" ref="edit_tip_list"/> + <field name="search_view_id" ref="edit_tip_search"/> + </record> + <menuitem action="edit_tip_action" id="menu_tip_action" parent="base.next_id_2" sequence="5"/> + </data> +</openerp> |
