[ADD] web_edit_user_filter

[FIX] Readme

[FIX] Lint

[ADD] Tests

[IMP] UI/UX

[IMP] Hide popover when other is opened

[IMP] Add item to roadmap
pull/1193/head
tarteo 2019-02-08 13:28:23 +01:00 committed by Dennis Sluijk
parent 2f31985f14
commit ae45dd9edc
16 changed files with 517 additions and 0 deletions

View File

@ -0,0 +1,100 @@
=================
Edit User Filters
=================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/12.0/web_edit_user_filter
:alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-12-0/web-12-0-web_edit_user_filter
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/162/12.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
In standard Odoo you can edit user filters via the debug module.
The problem is that normal users often don't have access to this menu therefore can't adjust filters once they're saved.
This module makes this feature available for normal users with a user friendly interface.
It also adds the ability to adjust facets (a single part of the filter).
**Table of contents**
.. contents::
:local:
Usage
=====
Edit a favourite filter:
#. Go to a list or kanban view;
#. open the advanced search options;
#. open the 'Favorites' menu;
#. click on the pencil icon to start editing the filter.
Edit a facet:
#. Click on the facet;
#. a menu is now shown which allows you to remove values from the facet;
#. to cancel removal you can click outside the popover.
.. image:: /web_edit_user_filter/static/description/edit_facet.png
:alt: Edit Facet
Known issues / Roadmap
======================
* Make individual values in facets editable.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/web/issues/new?body=module:%20web_edit_user_filter%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* Onestein
Contributors
~~~~~~~~~~~~
* Dennis Sluijk <d.sluijk@onestein.nl>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/web <https://github.com/OCA/web/tree/12.0/web_edit_user_filter>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,22 @@
# Copyright 2019 Onestein
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
'name': 'Edit User Filters',
'category': 'Extra Tools',
'version': '12.0.1.0.0',
'development_status': 'Production/Stable',
'author': 'Onestein,Odoo Community Association (OCA)',
'license': 'AGPL-3',
'website': 'https://github.com/OCA/web',
'depends': [
'web'
],
'data': [
'templates/assets.xml'
],
'qweb': [
'static/src/xml/backend.xml'
],
'installable': True,
}

View File

@ -0,0 +1 @@
from . import ir_filters

View File

@ -0,0 +1,23 @@
# Copyright 2019 Onestein
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import api, fields, models
class IrFilters(models.Model):
_inherit = 'ir.filters'
facet = fields.Text()
@api.model
def get_filters(self, model, action_id=None):
res = super().get_filters(model, action_id)
ids = map(lambda f: f['id'], res)
# Browse filters that are in res
filters = self.browse(ids)
for i, res_filter in enumerate(res):
# Add the field 'facet' to the result
res[i]['facet'] = filters.filtered(
lambda f: f.id == res_filter['id']
).facet
return res

View File

@ -0,0 +1 @@
* Dennis Sluijk <d.sluijk@onestein.nl>

View File

@ -0,0 +1,4 @@
In standard Odoo you can edit user filters via the debug module.
The problem is that normal users often don't have access to this menu therefore can't adjust filters once they're saved.
This module makes this feature available for normal users with a user friendly interface.
It also adds the ability to adjust facets (a single part of the filter).

View File

@ -0,0 +1,2 @@
* Make individual values in facets editable.
* Make saved filters easy overwritable.

View File

@ -0,0 +1,15 @@
Edit a favourite filter:
#. Go to a list or kanban view;
#. open the advanced search options;
#. open the 'Favorites' menu;
#. click on the pencil icon to start editing the filter.
Edit a facet:
#. Click on the facet;
#. a menu is now shown which allows you to remove values from the facet;
#. to cancel removal you can click outside the popover.
.. image:: /web_edit_user_filter/static/description/edit_facet.png
:alt: Edit Facet

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,242 @@
/* Copyright 2019 Onestein
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define('web_edit_user_filter', function (require) {
"use strict";
var FavoriteMenu = require('web.FavoriteMenu'),
core = require('web.core'),
SearchView = require('web.SearchView');
var qweb = core.qweb;
var _t = core._t;
FavoriteMenu.include({
/**
* Adds the facets data to the filter.
*
* @override
* @private
*/
_createFilter: function (filter) {
var facets = [];
this.query.each(function (facet) {
var json_facet = facet.attributes;
json_facet.values = facet.get('values');
_.each(json_facet.values, function (value, i) {
if (typeof value.value === 'object' &&
'attrs' in value.value) {
json_facet.values[i] = {
attrs: value.value.attrs,
};
}
});
if ('field' in json_facet) {
json_facet.field = {
attrs: json_facet.field.attrs,
};
}
facets.push(json_facet);
});
filter.facet = JSON.stringify(facets);
return this._super(filter);
},
/**
* Adds the edit button to the favourite filter menu item.
*
* @override
* @private
*/
append_filter: function (filter) {
var self = this;
var res = this._super(filter);
var key = this.key_for(filter);
this.$filters[key].append($('<span>', {
class: 'fa fa-pencil o-edit-user-filter',
on: {
click: function (event) {
event.stopImmediatePropagation();
self._unpackFilter(filter);
},
},
}));
return res;
},
/**
* Unpacks a saved filter and updates the search view's facets.
*
* @private
*/
_unpackFilter: function (filter) {
var self = this;
var facets = JSON.parse(filter.facet);
var new_facets = [];
this.query.reset([]);
_.each(facets, function (segment) {
if (segment.cat === 'groupByCategory') {
_.each(segment.values, function (value) {
var groupBy = _.find(
self.searchview.groupbysMapping,
function (mapping) {
return mapping.groupby.attrs.context === value.attrs.context;
}
);
var eventData = {
category: 'groupByCategory',
itemId: groupBy.groupbyId,
isActive: true,
groupId: groupBy.groupId,
};
self.trigger_up('menu_item_toggled', eventData);
});
} else if (segment.cat === 'filterCategory') {
_.each(segment.values, function (value) {
var filterDomain = _.find(
self.searchview.filtersMapping,
function (mapping) {
return mapping.filter.attrs.domain === value.attrs.domain;
}
);
var eventData = {
category: 'filterCategory',
itemId: filterDomain.filterId,
isActive: true,
groupId: filterDomain.groupId,
};
self.trigger_up('menu_item_toggled', eventData);
});
} else {
var search_widget = _.find(
self.searchview.search_fields, function (f) {
return f.attrs.name === segment.field.attrs.name;
}
);
new_facets.push({
category: segment.category,
field: search_widget,
values: segment.values,
});
}
});
this.query.add(new_facets);
},
});
SearchView.include({
/**
* Removes a value from a facet.
*
* @private
* @param {Backbone.Model} model
* @param {Integer|Object} value The value to remove
*/
_removeValue: function (model, value) {
var toRemove = model.values.filter(function (v) {
if (typeof v.attributes.value === 'object') {
return v.attributes.value.attrs.name === value;
}
return v.attributes.value.toString() === value;
});
model.values.remove(toRemove);
},
/**
* Renders a popover for a facet.
*
* @private
* @param {jQuery} $facet Element of the facet
* @param {Backbone.Model} model
*/
_renderPopover: function ($facet, model) {
var self = this;
var $content = $(qweb.render('web_edit_user_filter.Popover', {
values: model.get('values'),
}));
// Cannot use Widget.events here because renderFacets is
// triggered apart from renderElement
$content.find('.list-group-item').click(function () {
self._removeValue(model, $(this).attr('data-value'));
});
$facet.popover({
title: _t('Edit Facet'),
template: qweb.render('web_edit_user_filter.PopoverTemplate'),
content: $content,
container: this.$el,
html: true,
trigger: 'manual',
placement: 'bottom',
animation: false,
});
},
/**
* Hides all popovers.
*
* @private
*/
_hidePopovers: function () {
this.$el.find('.popover').popover('hide');
},
/**
* @override
*/
renderFacets: function () {
var self = this;
var res = this._super.apply(this, arguments);
this.$el.find('.o-edit-user-filter-popover').remove();
_.each(this.input_subviews, function (input_subview) {
if (!input_subview.model ||
input_subview.model.attributes.is_custom_filter) {
return;
}
input_subview.$el.addClass('o-edit-user-filter-editable');
self._renderPopover(input_subview.$el, input_subview.model);
input_subview.$el.click(function () {
self._hidePopovers();
input_subview.$el.popover('show');
});
});
return res;
},
/**
* @override
*/
start: function () {
var self = this;
var res = this._super.apply(this, arguments);
this._proxyHidePopovers = this.proxy('_hidePopovers');
$(document).click(this._proxyHidePopovers);
return res;
},
/**
* @override
*/
destroy: function () {
var res = this._super.apply(this, arguments);
$(document).unbind('click', this._proxyHidePopovers);
return res;
}
});
});

View File

@ -0,0 +1,27 @@
/* Copyright 2019 Onestein
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
.o_favorites_menu {
.o-edit-user-filter {
@include o-position-absolute(50%, 30px);
margin-top: -6px;
cursor: pointer;
}
}
.o-edit-user-filter-popover {
a {
cursor: pointer;
padding: 0.3rem 0.75rem;
font-size: 1.2rem;
.fa-close {
padding-left: 13px;
font-size: 1.2rem;
}
}
}
.o-edit-user-filter-editable {
cursor: pointer;
}

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 Onestein
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<template>
<div t-name="web_edit_user_filter.Popover">
<div class="list-group">
<t t-foreach="values" t-as="value">
<a class="list-group-item list-group-item-action"
t-attf-data-value="#{typeof value.value === 'object' ? value.value.attrs.name : value.value}">
<span>
<t t-esc="value.label"/>
</span>
<button class="btn btn-sm btn-link pull-right">
<span class="fa fa-close" />
</button>
</a>
</t>
</div>
</div>
<t t-name="web_edit_user_filter.PopoverTemplate">
<div class="popover o-edit-user-filter-popover" role="tooltip">
<h3 class="popover-header"></h3>
<div class="arrow"></div>
<div class="popover-body"></div>
</div>
</t>
</template>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2019 Onestein
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="assets_backend" inherit_id="web.assets_backend">
<xpath expr=".">
<script src="/web_edit_user_filter/static/src/js/backend.js"></script>
</xpath>
<xpath expr="//link[last()]">
<link href="/web_edit_user_filter/static/src/scss/backend.scss"
rel="stylesheet"
type="text/scss"/>
</xpath>
</template>
</odoo>

View File

@ -0,0 +1 @@
from . import test_edit_user_filter

View File

@ -0,0 +1,32 @@
# Copyright 2019 Onestein
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo.tests.common import SingleTransactionCase, post_install
@post_install(True)
class TestEditUserFilter(SingleTransactionCase):
def test_filter_facet_inclusion(self):
self.env['ir.filters'].create({
'name': 'any2',
'model_id': 'ir.filters',
'domain': '[]',
'facet': 'test2'
})
new_filter = self.env['ir.filters'].create({
'name': 'any',
'model_id': 'ir.filters',
'domain': '[]',
'facet': 'test'
})
res = self.env['ir.filters'].get_filters('ir.filters')
self.assertTrue('facet' in res[0])
self.assertEqual(
list(
filter(
lambda f: f['id'] == new_filter.id,
res
)
)[0]['facet'],
'test'
)