summaryrefslogtreecommitdiff
path: root/addons/web_tip
diff options
context:
space:
mode:
authorFrederic van der Essen <fva@openerp.com / fvdessen+o@gmail.com>2014-08-25 16:42:57 +0200
committerFrederic van der Essen <fva@openerp.com / fvdessen+o@gmail.com>2014-08-25 16:42:57 +0200
commitd7f93defb899b2b48a14d679e5dffffdb4a00eff (patch)
treef8572183b480477622917a1f504cf49a8e202f6e /addons/web_tip
parent2b67528fe9e366829d44d8aba756bc6e359b4c61 (diff)
parent36146b3fadc47543f4b696ab75c31b3459afb15b (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__.py1
-rw-r--r--addons/web_tip/__openerp__.py37
-rw-r--r--addons/web_tip/security/ir.model.access.csv2
-rw-r--r--addons/web_tip/static/src/css/tip.css56
-rw-r--r--addons/web_tip/static/src/js/tip.js254
-rw-r--r--addons/web_tip/views/tip.xml11
-rw-r--r--addons/web_tip/web_tip.py53
-rw-r--r--addons/web_tip/web_tip_view.xml66
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">&times;</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>