[ADD] Advance Menu Search and Menu Bookmark Facility (web)

This commit is contained in:
Chintan Ambaliya 2018-02-28 14:19:17 +05:30 committed by Siddharth Bhalgami
parent 87f3b80eba
commit 4cf45b1c78
13 changed files with 531 additions and 145 deletions

View File

@ -16,6 +16,8 @@ This module provides the core of the Odoo Web Client.
'depends': ['base'],
'auto_install': True,
'data': [
'security/ir.model.access.csv',
'views/res_users_views.xml',
'views/webclient_templates.xml',
'views/report_templates.xml',
],

View File

@ -4,3 +4,4 @@
from . import ir_qweb
from . import ir_http
from . import models
from . import bookmark

View File

@ -0,0 +1,75 @@
# -*- coding: utf-8 -*-
from flectra import api, fields, models
class Users(models.Model):
_inherit = "res.users"
bookmark_ids = fields.One2many('menu.bookmark', 'user_id', string="Bookmark Records")
class MenuBookmark(models.Model):
_name = "menu.bookmark"
menu_id = fields.Many2one('ir.ui.menu', 'Menu Id', help='Bookmark Menu Id', required=True)
user_id = fields.Many2one('res.users', 'User Id', help='Bookmark User ID', required=True)
@api.multi
def bookmark(self, action_id):
if action_id:
menu = self.env['ir.ui.menu'].search([('action', 'like', '%,' + str(action_id))], limit=1)
if not menu:
action = self.env['ir.actions.actions'].browse(int(action_id))
menu = self.env['ir.ui.menu'].search([('name', '=', action.name), ('action', '!=', '')], limit=1)
rec = self.sudo().search(
[('menu_id', '=', menu.id),
('user_id', '=', self.env.user.id)])
if (rec):
if (rec.sudo().unlink()):
return {
'bookmark': False
}
else:
if (menu and menu.action):
if (self.sudo().create({
'menu_id': menu.id,
'user_id': self.env.user.id,
})):
return {
'bookmark': True
}
return {}
@api.multi
def is_bookmark(self, menu_id):
menu = self.env['ir.ui.menu'].browse(int(menu_id))
if (menu and menu.action):
rec = self.sudo().search(
[('menu_id', '=', menu_id),
('user_id', '=', self.env.user.id)])
if (rec):
return True
return False
@api.multi
def get_bookmark_data(self, fields=[]):
bookmark_menu_ids = [rec.menu_id.id for rec in self.sudo().search([('user_id', '=', self.env.user.id)])]
menu_ids = self.env['ir.ui.menu'].browse(bookmark_menu_ids)
return menu_ids.read(fields)
@api.multi
def remove_bookmark(self, menu_id):
if menu_id:
rec = self.sudo().search(
[('menu_id', '=', menu_id),
('user_id', '=', self.env.user.id)])
if rec:
json = {
'id': rec.id,
'action_id': rec.menu_id.action.id,
'menu_id': rec.menu_id.id,
}
rec.sudo().unlink()
return json
return False

View File

@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_menu_bookmark,access_menu_bookmark,model_menu_bookmark,,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_menu_bookmark access_menu_bookmark model_menu_bookmark 1 0 0 0

View File

@ -1,95 +0,0 @@
flectra.define('web.AppsLauncher', function (require) {
"use strict";
var core = require('web.core');
var session = require('web.session');
var Widget = require('web.Widget');
var rpc = require('web.rpc');
var QWeb = core.qweb;
var Apps = Widget.extend({
template: 'AppsLauncher',
events: {
'click #f_clear_apps_search': '_clearSearch',
'input .f_apps_search_input': '_appsSearch',
'click a[data-menu]': '_o_app_click',
},
init: function (parent) {
this._super.apply(this, arguments);
this.parent = parent;
},
start: function () {
var self = this;
this._super.apply(this, arguments);
var company = session.company_id;
var img = session.url('/web/binary/company_logo' + '?db=' + session.db + (company ? '&company=' + company : ''));
$(document).keyup(function (e) {
if (e.keyCode == 27) {
$('.o_web_client').removeClass('launcher_opened');
self.$el.parents().find('#f_apps_search').find('i').toggleClass('fa-search fa-times');
}
});
this._createAppDashboardWidget().then(function (apps) {
self.$el.find('.f_apps_content').html(QWeb.render('AppsLauncher.Menus', {
dashboard_apps: apps.children
}));
self.apps = apps.children;
});
this.$('.f_app_footer img').attr('src',img);
},
_getAction: function (menu) {
if (!menu.action && menu.children.length) {
if (menu.children[0].action) {
return menu.children[0].action;
} else {
return this._getAction(menu.children[0]);
}
}
},
_createAppDashboardWidget: function () {
var self = this;
return rpc.query({
model: 'ir.ui.menu',
method: 'load_menus',
args: [core.debug],
kwargs: {context: session.user_context},
}).then(function (menus) {
menus.children = _.each(menus.children, function (menu) {
if (!menu.action && menu.children.length) {
menu.action = self._getAction(menu);
}
return menu;
});
return menus;
});
},
_clearSearch: function (ev) {
this.$el.find('.f_apps_search_input').val('');
this.$el.find('.f_apps_content').html(QWeb.render('AppsLauncher.Menus', {
dashboard_apps: this.apps
}));
},
_appsSearch: function (ev) {
var search_str = $(ev.currentTarget).val().trim().toLowerCase();
var apps = [];
_.each(this.apps, function (app) {
if (app.name.trim().toLowerCase().indexOf(search_str) !== -1) {
apps.push(app);
}
});
this.$el.find('.f_apps_content').html(QWeb.render('AppsLauncher.Menus', {
dashboard_apps: apps
}));
},
_o_app_click: function (ev) {
ev.preventDefault();
this.parent._isMainMenuClick = true;
this.parent.menu_click($(ev.currentTarget).data('menu'));
}
});
return Apps;
});

View File

@ -0,0 +1,211 @@
flectra.define('web.AppsLauncher', function (require) {
"use strict";
var core = require('web.core');
var session = require('web.session');
var Widget = require('web.Widget');
var rpc = require('web.rpc');
var QWeb = core.qweb;
var Apps = Widget.extend({
template: 'AppsLauncher',
events: {
'click .f_remove_bookmark i': '_removeBookmark',
'click #f_clear_apps_search': '_clearSearch',
'input .f_apps_search_input': '_appsSearch',
'click a[data-menu]': '_o_app_click',
},
init: function (parent) {
this._super.apply(this, arguments);
this.parent = parent;
},
start: function () {
var self = this;
this._super.apply(this, arguments);
var company = session.company_id;
var img = session.url('/web/binary/company_logo' + '?db=' + session.db + (company ? '&company=' + company : ''));
$(document).keyup(function (e) {
if ($('.launcher_opened').length && e.keyCode == 27) {
$('.f_search_launcher').removeClass('launcher_opened');
self.$el.parents().find('#f_apps_search').find('i').toggleClass('fa-search fa-times');
}
});
this.render();
this.$('.f_app_footer img').attr('src',img);
},
render: function () {
var self = this;
this._loadAllMenuData().then(function () {
var app_contents = self.$el.find('.f_apps_contents');
if(!app_contents.length){
app_contents = $('.f_search_launcher .f_apps_contents');
$('.f_search_launcher .f_apps_search_input').val('');
}
app_contents.html(QWeb.render('FlectraMenuContent', {
bookmark_menus: self.bookmarkMenus,
dashboard_apps: self.allApps.main_menu,
}));
});
},
_removeBookmark: function (e) {
e.stopPropagation();
var self = this;
var id = parseInt($(e.currentTarget).data('id'));
if(id !== NaN){
rpc.query({
model: 'menu.bookmark',
method: 'remove_bookmark',
args: ['', id]
}).then(function (res) {
if(res.id){
self.render();
if($.bbq.getState(true).action == res.action_id){
self.$el.parents().find('.o_user_bookmark_menu > a').removeClass('active');
}
}
});
}
return false;
},
_getAction: function (menu) {
if (!menu.action && menu.children.length) {
if (menu.children[0].action) {
return menu.children[0].action;
} else {
return this._getAction(menu.children[0]);
}
}
},
_loadAllMenuData: function () {
var self = this;
var _allApps = rpc.query({
model: 'ir.ui.menu',
method: 'load_menus',
args: [core.debug],
kwargs: {context: session.user_context},
}).then(function (menus) {
menus.children = _.each(menus.children, function (menu) {
if (!menu.action && menu.children.length) {
menu.action = self._getAction(menu);
}
return menu;
});
self.allApps = self._simplifyMenuData(menus);
});
var _bookmarkMenus = rpc.query({
model: 'menu.bookmark',
method: 'get_bookmark_data',
args: ['', ['name', 'action', 'display_name']],
kwargs: {context: session.user_context},
}).then(function (menus) {
menus = _.each(menus, function (menu) {
menu.short_name = self._createShortName(menu.name);
return menu;
});
self.bookmarkMenus = menus;
});
return $.when(_allApps, _bookmarkMenus);
},
_createShortName: function (menu_name) {
var s_name = menu_name.split(' ');
if(s_name.length > 1){
return (s_name[0].substr(0, 1) + s_name[1].substr(0, 1)).toUpperCase();
}else{
return menu_name.substr(0, 2).toUpperCase();
}
},
_clearSearch: function (ev) {
this.$el.find('.f_apps_search_input').val('');
this.$el.find('.f_apps_contents').html(QWeb.render('FlectraMenuContent', {
bookmark_menus: this.bookmarkMenus,
dashboard_apps: this.allApps.main_menu
}));
},
_appsSearch: function (ev) {
var search_str = $(ev.currentTarget).val().trim().toLowerCase();
var bookmarks = [];
var main_menus = [];
var sub_menus = [];
_.each(this.bookmarkMenus, function (bookmark) {
if (bookmark.name.trim().toLowerCase().indexOf(search_str) !== -1) {
bookmarks.push(bookmark);
}
});
_.each(this.allApps.main_menu, function (main_menu) {
if (main_menu.label.trim().toLowerCase().indexOf(search_str) !== -1) {
main_menus.push(main_menu);
}
});
_.each(this.allApps.sub_menu, function (sub_menu) {
if (sub_menu.label.trim().toLowerCase().indexOf(search_str) !== -1) {
sub_menus.push(sub_menu);
}
});
this.$el.find('.f_apps_contents').html(QWeb.render('FlectraMenuContent', {
bookmark_menus: bookmarks,
dashboard_apps: main_menus,
sub_menus : search_str == ''? undefined : sub_menus
}));
},
_o_app_click: function (ev) {
ev.preventDefault();
this.parent._isMainMenuClick = false;
this.$el.removeClass('launcher_opened');
this.$el.parents().find('#f_apps_search').find('i').addClass('fa-search').removeClass('fa-times');
this.parent.menu_click($(ev.currentTarget).data('menu'));
},
_simplifyMenuData: function (app, result, menu_id) {
result = result || {
main_menu:[],
sub_menu:[]
};
menu_id = menu_id || false;
var item = {
label: (app.parent_id && app.parent_id.length) ? [app.parent_id[1], app.name].join('/').replace(/\//g, ' / ') : app.name,
id: app.id,
xmlid: app.xmlid,
action: app.action ? app.action.split(',')[1] : '',
is_app: !app.parent_id,
web_icon: app.web_icon
};
if (!app.parent_id) {
if (app.web_icon_data) {
item.web_icon_data = 'data:image/png;base64,' + app.web_icon_data;
} else {
item.web_icon_data = '/base/static/description/icon.png';
}
} else {
item.menu_id = menu_id;
}
if (item.action !== "") {
if(item.is_app){
result.main_menu.push(item);
}else if(!item.is_app){
result.sub_menu.push(item);
}
}
if (app.children && app.children.length) {
for (var i in app.children) {
if (app.children[i].parent_id === false) {
menu_id = app.children[i].id;
}
this._simplifyMenuData(app.children[i], result, menu_id);
}
}
return result;
}
});
return Apps;
});

View File

@ -0,0 +1,48 @@
flectra.define('web.Bookmark', function (require) {
"use strict";
var SystrayMenu = require('web.SystrayMenu');
var Widget = require('web.Widget');
var rpc = require('web.rpc');
var AppsLauncher = require('web.AppsLauncher');
var Bookmark = Widget.extend({
template: "Bookmark.Menu",
events: {
"click": "on_click",
},
init: function () {
this._super.apply(this, arguments);
},
start: function () {
this.AppsLauncher = new AppsLauncher(this);
return this._super();
},
getMenuRecord: function () {
var action_id = $.bbq.getState().action;
return rpc.query({
model: 'menu.bookmark',
method: 'bookmark',
args: ['', action_id]
});
},
on_click: function (ev) {
ev.preventDefault();
var self = this;
this.getMenuRecord().then(function (rec) {
if (rec.bookmark) {
self.$el.find('> a').addClass('active');
} else {
self.$el.find('> a').removeClass('active');
}
self.AppsLauncher.render();
});
}
});
SystrayMenu.Items.push(Bookmark);
return {
Bookmark: Bookmark,
};
});

View File

@ -6,6 +6,7 @@ var session = require('web.session');
var Widget = require('web.Widget');
var config = require('web.config');
var Apps = require('web.AppsLauncher');
var rpc = require('web.rpc');
var Menu = Widget.extend({
init: function() {
@ -168,6 +169,7 @@ var Menu = Widget.extend({
* @param {Number} id database id of the terminal menu to select
*/
open_menu: function (id) {
var self = this;
this.current_menu = id;
session.active_id = id;
var $clicked_menu, $sub_menu, $main_menu;
@ -241,6 +243,19 @@ var Menu = Widget.extend({
this.$secondary_menus.find('.oe_secondary_submenu li a span').each(function() {
$(this).tooltip(this.scrollWidth > this.clientWidth ? {title: $(this).text().trim(), placement: 'right'} :'destroy');
});
if(this._isActionId || this.isLoadflag) {
rpc.query({
model: 'menu.bookmark',
method: 'is_bookmark',
args: ['', id]
}).then(function (rec) {
if (rec) {
self.$el.parents().find('.o_user_bookmark_menu > a').addClass('active');
} else {
self.$el.parents().find('.o_user_bookmark_menu > a').removeClass('active');
}
});
}
this.isLoadflag = false;
},
/**

View File

@ -247,6 +247,7 @@
.f_search_launcher_content {
position: relative;
width: 100%;
height: calc(~"100% - 118px");
.f_apps_search_box {
align-items: center;
display: flex;
@ -299,52 +300,99 @@
.o-align-items(center);
.o-flex(1, 0, auto);
width: 100%;
overflow-y: auto;
.f_apps_content {
.o-flex(0, 0, auto);
.f_apps_contents {
width: 100%;
.clearfix();
margin: 10px 0;
.f_app {
width: 25%;
text-align: center;
padding: 20px;
float: left;
@media (min-width: @screen-xs-min) {
height: 100%;
overflow-y: auto;
> h5 {
background-color: @flectra-brand-primary;
padding: 5px;
color: #ffffff;
}
.f_apps_content {
.o-flex(0, 0, auto);
width: 100%;
.clearfix();
margin: 10px 0;
.f_app {
width: 25%;
}
@media (max-width: @screen-xs-max) {
padding: 10px;
text-align: center;
padding: 20px;
float: left;
@media (min-width: @screen-xs-min) {
width: 25%;
}
@media (max-width: @screen-xs-max) {
padding: 10px;
.f_app_label {
font-size: small;
}
}
@media (min-width: 882px) {
width: 20%;
}
@media (min-width: @screen-md-min) {
width: 16.66%;
}
@media (min-width: @screen-lg-min) {
width: 12.5%;
}
@media (min-width: 1440px) {
width: 10%;
}
.f_app_icon {
max-width: 45px;
max-height: 100%;
width: 100%;
}
.f_app_label {
font-size: small;
color: #000000;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-top: 5px;
}
&:hover {
background-color: fade(@brand-primary, 60%);
}
}
@media (min-width: 882px) {
width: 20%;
}
@media (min-width: @screen-md-min) {
width: 16.66%;
}
@media (min-width: @screen-lg-min) {
width: 12.5%;
}
@media (min-width: 1440px) {
width: 10%;
}
.f_app_icon {
max-width: 45px;
max-height: 100%;
.f_sub_menu {
width: 100%;
text-align: left;
.f_sub_menu_label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 5px 20px;
font-weight: bold;
color: @flectra-main-text-color;
&:hover {
background-color: fade(@brand-primary, 60%);
}
}
}
.f_app_label {
color: #000000;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-top: 5px;
}
}
.bookmark_menus {
.f_app {
padding-top: 0 !important;
.f_remove_bookmark {
text-align: right;
margin-right: -16px;
font-size: 20px;
@media (max-width: @screen-xs-max) {
margin-right: -5px;
}
}
&:hover {
background-color: fade(@brand-primary, 60%);
.f_bm_icon {
height: 45px;
width: 45px;
margin: auto;
background: @flectra-brand-primary;
border-radius: 22px;
line-height: 45px;
font-size: 20px;
color: #ffffff;
}
}
}
@ -362,9 +410,9 @@
&.launcher_opened {
border: 1px solid @launcher-menu-border;
border-top: 0;
height: 356px;
height: 100%;
.f_apps_container {
height: 300px;
height: 100%;
}
@media (max-width: @screen-xs-max) {
height: 100%;

View File

@ -68,6 +68,11 @@ nav#oe_main_menu_navbar {
&:hover, &:focus {
background-color: lighten(@flectra-main-text-color, 65%);
}
&.active {
.fa-bookmark {
color: @brand-success;
}
}
}
> ul {
left: auto;

View File

@ -9,8 +9,8 @@
<span><i class="fa fa-times-circle f_apps_search_icon" id="f_clear_apps_search"/></span>
</div>
<div class="f_apps_container">
<div class="f_apps_content">
<t t-call="AppsLauncher.Menus"/>
<div class="f_apps_contents">
<t t-call="FlectraMenuContent"/>
</div>
<div class="f_app_footer">
<img src="/web/static/src/img/logo.png"/>
@ -19,21 +19,64 @@
</div>
</div>
<t t-name="FlectraMenuContent">
<h5>Bookmark</h5>
<div class="f_apps_content bookmark_menus">
<t t-call="Bookmark.Menus"/>
</div>
<h5>All Apps</h5>
<div class="f_apps_content all_apps">
<t t-call="AppsLauncher.Menus"/>
</div>
<h5 t-if="sub_menus">Menu</h5>
<div class="f_apps_content sub_menu">
<t t-call="AppsLauncher.SubMenus"/>
</div>
</t>
<t t-name="Bookmark.Menus">
<t t-if="bookmark_menus">
<t t-foreach="bookmark_menus" t-as="bookmark_menu">
<a class="f_app"
t-att-href="bookmark_menu.href ? bookmark_menu.href : ('#menu_id=' + bookmark_menu.id + '&amp;action=' + (bookmark_menu.action != '' ? bookmark_menu.action.split(',')[1] : ''))"
t-att-data-menu="bookmark_menu.id"
t-att-title="bookmark_menu.name"
t-att-data-action-id="bookmark_menu.action">
<div class="f_remove_bookmark"><i t-att-data-id="bookmark_menu.id" class="fa fa-times-circle"/></div>
<div class="f_bm_icon"><t t-esc="bookmark_menu.short_name"/></div>
<div class="f_app_label"><t t-esc="bookmark_menu.name"/></div>
</a>
</t>
</t>
</t>
<t t-name="AppsLauncher.Menus">
<t t-if="dashboard_apps">
<t t-foreach="dashboard_apps" t-as="dashboard_app">
<a class="f_app"
t-att-href="dashboard_app.href ? dashboard_app.href : ('#menu_id=' + dashboard_app.id + '&amp;action=' + dashboard_app.action.split(',')[1])"
t-att-href="dashboard_app.href ? dashboard_app.href : ('#menu_id=' + dashboard_app.id + '&amp;action=' + dashboard_app.action)"
t-att-data-menu="dashboard_app.id"
t-att-title="dashboard_app.label"
t-att-data-action-id="dashboard_app.action">
<img class="f_app_icon" t-att-src="dashboard_app.web_icon_data ? 'data:image/png;base64,' + dashboard_app.web_icon_data : '/base/static/description/icon.png'"/>
<div class="f_app_label"><t t-esc="dashboard_app.name"/></div>
<img class="f_app_icon" t-att-src="dashboard_app.web_icon_data"/>
<div class="f_app_label"><t t-esc="dashboard_app.label"/></div>
</a>
</t>
</t>
</t>
<t t-name="AppsLauncher.SubMenus">
<t t-if="sub_menus">
<t t-foreach="sub_menus" t-as="sub_menu">
<a class="f_sub_menu"
t-att-href="sub_menu.href ? sub_menu.href : ('#menu_id=' + sub_menu.id + '&amp;action=' + sub_menu.action)"
t-att-data-menu="sub_menu.id"
t-att-title="sub_menu.label"
t-att-data-action-id="sub_menu.action">
<div class="f_sub_menu_label"><t t-esc="sub_menu.label"/></div>
</a>
</t>
</t>
<div class="text-center" t-if="dashboard_apps == ''">
<div class="mt8" style="font-size: 22px;">Sorry, unable to find your content!</div>
</div>
</t>
<t t-name="DragAndDropAttachment">
@ -47,4 +90,10 @@
</t>
</div>
</t>
<t t-name="Bookmark.Menu">
<li class="o_user_bookmark_menu" ><a href="#" title="Bookmark"><i class="fa fa-bookmark"/></a></li>
</t>
</templates>

View File

@ -0,0 +1,24 @@
<?xml version="1.0"?>
<flectra>
<record id="view_users_form" model="ir.ui.view">
<field name="name">res.users.form.inherit</field>
<field name="model">res.users</field>
<field name="type">form</field>
<field name="inherit_id" ref="base.view_users_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook[last()]" position="inside">
<page string="Bookmark Menu">
<group>
<field name="bookmark_ids" nolabel="1">
<tree>
<field name="menu_id" string="Menus"/>
</tree>
</field>
</group>
</page>
</xpath>
</field>
</record>
</flectra>

View File

@ -176,7 +176,7 @@
<script type="text/javascript" src="/web/static/src/js/widgets/switch_company_menu.js"></script>
<script type="text/javascript" src="/web/static/src/js/chrome/user_menu.js"></script>
<script type="text/javascript" src="/web/static/src/js/chrome/menu.js"></script>
<script type="text/javascript" src="/web/static/src/js/chrome/apps.js"></script>
<script type="text/javascript" src="/web/static/src/js/chrome/apps_launcher.js"></script>
<script type="text/javascript" src="/web/static/src/js/chrome/search_view.js"></script>
<script type="text/javascript" src="/web/static/src/js/chrome/search_filters.js"></script>
<script type="text/javascript" src="/web/static/src/js/chrome/search_inputs.js"></script>
@ -268,6 +268,7 @@
<script type="text/javascript" src="/web/static/src/js/backend_theme_customizer/backend_theme_customizer.js"/>
<script type="text/javascript" src="/web/static/src/js/backend_theme_customizer/customize_switcher.js"/>
<script type="text/javascript" src="/web/static/src/js/chrome/bookmark.js"/>
</template>
@ -802,7 +803,7 @@
<li id="f_user_toggle">
<a href="#"><i class="fa fa-user"/></a>
</li>
<li id="f_apps_search" class="hidden-xs">
<li id="f_apps_search">
<a href="#"><i class="fa fa-search"/></a>
</li>
</ul>