diff --git a/addons/base_calendar/base_calendar_view.xml b/addons/base_calendar/base_calendar_view.xml
index 89066ce74bb..0ea2be4f37f 100644
--- a/addons/base_calendar/base_calendar_view.xml
+++ b/addons/base_calendar/base_calendar_view.xml
@@ -418,7 +418,7 @@
ir.actions.act_window
calendar.event
form
- tree,form,calendar
+ calendar,tree,form
diff --git a/addons/base_setup/res_partner_view.xml b/addons/base_setup/res_partner_view.xml
index 5e54c0b8629..15b80151831 100644
--- a/addons/base_setup/res_partner_view.xml
+++ b/addons/base_setup/res_partner_view.xml
@@ -9,14 +9,11 @@
-
-
-
-
+
+
-
diff --git a/addons/board/static/src/css/dashboard.css b/addons/board/static/src/css/dashboard.css
index ed66e338c81..f2bbcd07071 100644
--- a/addons/board/static/src/css/dashboard.css
+++ b/addons/board/static/src/css/dashboard.css
@@ -1,3 +1,36 @@
+.openerp .oe_dashboard_layout_1 .oe_dashboard_column.index_0 {
+ width: 100%;
+}
+.openerp .oe_dashboard_layout_1 .oe_dashboard_column.index_1, .openerp .oe_dashboard_layout_1 .oe_dashboard_column.index_2 {
+ display: none;
+}
+.openerp .oe_dashboard_layout_1-1 .oe_dashboard_column {
+ width: 50%;
+}
+.openerp .oe_dashboard_layout_1-1 .oe_dashboard_column.index_2 {
+ display: none;
+}
+.openerp .oe_dashboard_layout_1-1-1 .oe_dashboard_column {
+ width: 33%;
+}
+.openerp .oe_dashboard_layout_2-1 .oe_dashboard_column.index_0 {
+ width: 70%;
+}
+.openerp .oe_dashboard_layout_2-1 .oe_dashboard_column.index_1 {
+ width: 30%;
+}
+.openerp .oe_dashboard_layout_2-1 .oe_dashboard_column.index_2 {
+ display: none;
+}
+.openerp .oe_dashboard_layout_1-2 .oe_dashboard_column.index_0 {
+ width: 30%;
+}
+.openerp .oe_dashboard_layout_1-2 .oe_dashboard_column.index_1 {
+ width: 70%;
+}
+.openerp .oe_dashboard_layout_1-2 .oe_dashboard_column.index_2 {
+ display: none;
+}
.openerp .oe_dashboard_layout_selector ul {
white-space: nowrap;
}
diff --git a/addons/board/static/src/css/dashboard.sass b/addons/board/static/src/css/dashboard.sass
index 32f0e3b4f3a..122b6dc232c 100644
--- a/addons/board/static/src/css/dashboard.sass
+++ b/addons/board/static/src/css/dashboard.sass
@@ -9,6 +9,31 @@
box-shadow: $bsval
.openerp
+ .oe_dashboard_layout_1 .oe_dashboard_column
+ &.index_0
+ width: 100%
+ &.index_1, &.index_2
+ display: none
+ .oe_dashboard_layout_1-1 .oe_dashboard_column
+ width: 50%
+ &.index_2
+ display: none
+ .oe_dashboard_layout_1-1-1 .oe_dashboard_column
+ width: 33%
+ .oe_dashboard_layout_2-1 .oe_dashboard_column
+ &.index_0
+ width: 70%
+ &.index_1
+ width: 30%
+ &.index_2
+ display: none
+ .oe_dashboard_layout_1-2 .oe_dashboard_column
+ &.index_0
+ width: 30%
+ &.index_1
+ width: 70%
+ &.index_2
+ display: none
.oe_dashboard_layout_selector
ul
white-space: nowrap
@@ -98,3 +123,4 @@
> tbody
tr:nth-child(odd)
background: transparent
+
diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js
index 9c37924144d..43d7ca76d39 100644
--- a/addons/board/static/src/js/dashboard.js
+++ b/addons/board/static/src/js/dashboard.js
@@ -24,6 +24,10 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
scroll: false
}).bind('sortstop', self.do_save_dashboard);
+ var old_title = this.__parentedParent.get('title');
+ this.__parentedParent.on_record_loaded.add_last(function(){
+ self.__parentedParent.set({ 'title' : old_title});
+ });
// Events
this.$el.find('.oe_dashboard_link_reset').click(this.on_reset);
this.$el.find('.oe_dashboard_link_change_layout').click(this.on_change_layout);
@@ -165,8 +169,6 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({
this.rpc('/web/view/add_custom', {
view_id: this.view.fields_view.view_id,
arch: arch
- }, function() {
- self.$el.find('.oe_dashboard_link_reset').show();
});
},
on_load_action: function(result, index, action_attrs) {
diff --git a/addons/board/static/src/xml/board.xml b/addons/board/static/src/xml/board.xml
index e13fa707776..03ede793036 100644
--- a/addons/board/static/src/xml/board.xml
+++ b/addons/board/static/src/xml/board.xml
@@ -1,7 +1,7 @@
-
diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py
index da517ee17ad..b384613d174 100644
--- a/addons/mail/__init__.py
+++ b/addons/mail/__init__.py
@@ -25,6 +25,7 @@ import mail_message
import mail_mail
import mail_thread
import mail_group
+import mail_vote
import res_partner
import res_users
import report
diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py
index 44ce76b94b0..1d5d85e36e0 100644
--- a/addons/mail/mail_message.py
+++ b/addons/mail/mail_message.py
@@ -130,6 +130,8 @@ class mail_message(osv.Model):
'unread': fields.function(_get_unread, fnct_search=_search_unread,
type='boolean', string='Unread',
help='Functional field to search for unread messages linked to uid'),
+ 'vote_user_ids': fields.many2many('res.users', 'mail_vote', 'message_id', 'user_id', string='Votes',
+ help='Users that voted for this message'),
}
def _needaction_domain_get(self, cr, uid, context=None):
@@ -147,12 +149,35 @@ class mail_message(osv.Model):
'body': '',
}
+ #------------------------------------------------------
+ # Vote/Like
+ #------------------------------------------------------
+
+ def vote_toggle(self, cr, uid, ids, user_ids=None, context=None):
+ ''' Toggles voting '''
+ if not user_ids:
+ user_ids = [uid]
+ for message in self.read(cr, uid, ids, ['vote_user_ids'], context=context):
+ for user_id in user_ids:
+ has_voted = user_id in message.get('vote_user_ids')
+ if not has_voted:
+ self.write(cr, uid, message.get('id'), {'vote_user_ids': [(4, user_id)]}, context=context)
+ else:
+ self.write(cr, uid, message.get('id'), {'vote_user_ids': [(3, user_id)]}, context=context)
+ return True
+
#------------------------------------------------------
# Message loading for web interface
#------------------------------------------------------
def _message_dict_get(self, cr, uid, msg, context=None):
""" Return a dict representation of the message browse record. """
+ has_voted = False
+ vote_ids = self.pool.get('res.users').name_get(cr, uid, [user.id for user in msg.vote_user_ids], context=context)
+ for vote in vote_ids:
+ if vote[0] == uid:
+ has_voted = True
+ break
attachment_ids = [{'id': attach[0], 'name': attach[1]} for attach in self.pool.get('ir.attachment').name_get(cr, uid, [x.id for x in msg.attachment_ids], context=context)]
author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0]
author_user_id = self.pool.get('res.users').name_get(cr, uid, [msg.author_id.user_ids[0].id], context=context)[0]
@@ -171,6 +196,8 @@ class mail_message(osv.Model):
'author_user_id': author_user_id,
'partner_ids': partner_ids,
'child_ids': [],
+ 'vote_user_ids': vote_ids,
+ 'has_voted': has_voted
}
def message_read_tree_flatten(self, cr, uid, messages, current_level, level, context=None):
diff --git a/addons/mail/mail_vote.py b/addons/mail/mail_vote.py
new file mode 100644
index 00000000000..1e439fe0aeb
--- /dev/null
+++ b/addons/mail/mail_vote.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Management Solution
+# Copyright (C) 2012-Today OpenERP SA ().
+#
+# 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
+#
+##############################################################################
+
+from osv import osv, fields
+
+
+class mail_vote(osv.Model):
+ ''' Mail vote feature allow users to like and unlike messages attached
+ to a document. This allows for example to build a ranking-based
+ displaying of messages, for FAQ. '''
+
+ _name = 'mail.vote'
+ _description = 'Mail Vote'
+ _columns = {
+ 'message_id': fields.many2one('mail.message', 'Message', select=1,
+ ondelete='cascade', required=True),
+ 'user_id': fields.many2one('res.users', 'User', select=1,
+ ondelete='cascade', required=True),
+ }
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/mail/security/ir.model.access.csv b/addons/mail/security/ir.model.access.csv
index dbc84c86ded..2ec74086a34 100644
--- a/addons/mail/security/ir.model.access.csv
+++ b/addons/mail/security/ir.model.access.csv
@@ -10,3 +10,4 @@ access_mail_alias_user,mail.alias,model_mail_alias,base.group_user,1,1,1,0
access_mail_alias_system,mail.alias,model_mail_alias,base.group_system,1,1,1,1
access_mail_mail_user,mail.mail,model_mail_mail,base.group_user,1,1,1,0
access_mail_mail_manager,mail.mail,model_mail_mail,group_mail_manager,1,1,1,1
+access_mail_vote_all,mail.vote.all,model_mail_vote,,1,1,1,1
diff --git a/addons/mail/static/src/css/mail.css b/addons/mail/static/src/css/mail.css
index 48a178732b0..2f9b35f1c95 100644
--- a/addons/mail/static/src/css/mail.css
+++ b/addons/mail/static/src/css/mail.css
@@ -260,6 +260,31 @@
display: none;
}
+/*--------------------------------------------------------------*/
+/* mail.vote
+/*--------------------------------------------------------------*/
+
+.openerp .oe_mail_msg_content button.oe_mail_msg_vote {
+ height:21px;
+ width: 30px;
+ padding: 1px;
+ background: #8A89BA;
+ margin-top: -4px;
+}
+
+.openerp .oe_mail_msg_content button.oe_mail_msg_vote_true {
+ background:#DC5F59;
+}
+
+.openerp .oe_mail_msg_content button.oe_mail_msg_vote span {
+ color: white;
+}
+
+.openerp .oe_mail_msg_content span.oe_mail_vote_count{
+ color: #807FB4;
+}
+
+
/* ------------------------------------------------------------ */
/* mail.compose.message form view & OpenERP hacks
/* ------------------------------------------------------------ */
diff --git a/addons/mail/static/src/js/mail.js b/addons/mail/static/src/js/mail.js
index 71665bc69fb..57c777441e7 100644
--- a/addons/mail/static/src/js/mail.js
+++ b/addons/mail/static/src/js/mail.js
@@ -291,6 +291,7 @@ openerp.mail = function(session) {
truncate_limit: options.truncate_limit || 250,
}
// datasets and internal vars
+ this.records = {};
this.ds_thread = new session.web.DataSetSearch(this, this.context.default_model);
this.ds_notification = new session.web.DataSetSearch(this, 'mail.notification');
this.ds_message = new session.web.DataSetSearch(this, 'mail.message');
@@ -355,6 +356,8 @@ openerp.mail = function(session) {
'default_parent_id': parseInt(msg_id),
'default_content_subtype': 'html'} );
});
+ // event: click on 'Vote' button
+ this.$el.on('click', 'button.oe_mail_msg_vote', this.on_vote);
},
on_message_delete: function (event) {
@@ -373,6 +376,16 @@ openerp.mail = function(session) {
return this.ds_notification.call('set_message_read', [parseInt(msg_id)]);
},
+ on_vote: function (event) {
+ event.stopPropagation();
+ var self = this;
+ var message_id = $(event.srcElement).parent().data().msg_id;
+ var vote_node = $(event.srcElement).parents('li').eq(0);
+ if (! message_id) { return false; }
+ return this.ds_message.call('vote_toggle', [[parseInt(message_id)]]).pipe(
+ self.toggle_vote(message_id, vote_node));
+ },
+
/**
* Override-hack of do_action: automatically reload the chatter.
* Normally it should be called only when clicking on 'Post/Send'
@@ -492,6 +505,8 @@ openerp.mail = function(session) {
attach['url'] = mail.ChatterUtils.get_attachment_url(this.session, attach);
}
record.is_author = mail.ChatterUtils.is_author(this, record.author_user_id[0]);
+ // add to internal storage
+ this.records[record.id] = record;
// render, add the expand feature
var rendered = session.web.qweb.render('mail.thread.message', {'record': record, 'thread': this, 'options': this.options});
$(rendered).appendTo(this.$el.children('div.oe_mail_thread_display:first'));
@@ -505,6 +520,23 @@ openerp.mail = function(session) {
});
},
+ // Render vote Display template.
+ toggle_vote: function (message_id, vote_node) {
+ var self = this;
+ var record = this.records[message_id];
+ if (record.has_voted) {
+ var idx = _.map(record.vote_user_ids, function (x) { return x[0]; }).indexOf(message_id);
+ record.vote_user_ids.splice(idx, 1);
+ }
+ else {
+ record.vote_user_ids.push([this.session.uid, 'You']);
+ }
+ record.has_voted = ! record.has_voted;
+ var vote_element = session.web.qweb.render('mail.thread.message.vote', {'record': record});
+ vote_node.empty();
+ vote_node.html(vote_element);
+ },
+
/** Display 'show more' button */
update_fetch_more: function (new_value) {
if (new_value) {
diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml
index bf339f21f1c..e3586e723e4 100644
--- a/addons/mail/static/src/xml/mail.xml
+++ b/addons/mail/static/src/xml/mail.xml
@@ -142,11 +142,9 @@
+
Reply
Reply
-
1 Attachment
@@ -180,4 +178,17 @@
+
+
+
+ votes
+
+
+ +1
+ -1
+
+
+
diff --git a/addons/mail/tests/test_mail.py b/addons/mail/tests/test_mail.py
index 1fab5dd1732..57a59e2595f 100644
--- a/addons/mail/tests/test_mail.py
+++ b/addons/mail/tests/test_mail.py
@@ -647,3 +647,33 @@ class test_mail(TestMailMockups):
msg1.refresh()
self.assertEqual(5, len(group_pigs.message_ids), 'group should contain 5 messages')
self.assertEqual(2, len(msg1.child_ids), 'msg1 should have 2 children now')
+
+ def test_60_vote(self):
+ """ Test designed for the vote/unvote feature. """
+ cr, uid = self.cr, self.uid
+ group_pigs = self.mail_group.browse(cr, uid, self.group_pigs_id)
+ user_admin = self.res_users.browse(cr, uid, uid)
+ msg1 = group_pigs.message_post(body='My Body', subject='1')
+ msg1 = self.mail_message.browse(cr, uid, msg1)
+
+ # Create user Bert Tartopoils
+ user_bert_id = self.res_users.create(cr, uid, {'name': 'Bert', 'login': 'bert'})
+ user_bert = self.res_users.browse(cr, uid, user_bert_id)
+
+ # Test: msg1 and msg2 have void vote_user_ids
+ self.assertFalse(msg1.vote_user_ids, 'newly created message msg1 has not void vote_user_ids')
+ # Do: Admin vote for msg1
+ self.mail_message.vote_toggle(cr, uid, [msg1.id])
+ msg1.refresh()
+ # Test: msg1 has Admin as voter
+ self.assertEqual(set(msg1.vote_user_ids), set([user_admin]), 'after voting, Admin is not the voter')
+ # Do: Bert vote for msg1
+ self.mail_message.vote_toggle(cr, uid, [msg1.id], [user_bert_id])
+ msg1.refresh()
+ # Test: msg1 has Admin and Bert as voters
+ self.assertEqual(set(msg1.vote_user_ids), set([user_admin, user_bert]), 'after voting, Admin and Bert are not the voters')
+ # Do: Admin unvote for msg1
+ self.mail_message.vote_toggle(cr, uid, [msg1.id])
+ msg1.refresh()
+ # Test: msg1 has Bert as voter
+ self.assertEqual(set(msg1.vote_user_ids), set([user_bert]), 'after unvoting for Admin, Bert is not the voter')
diff --git a/addons/mrp/board_manufacturing_view.xml b/addons/mrp/board_manufacturing_view.xml
index cae1fea07dc..6bd07c2648b 100644
--- a/addons/mrp/board_manufacturing_view.xml
+++ b/addons/mrp/board_manufacturing_view.xml
@@ -30,7 +30,8 @@
icon="terp-graph"
id="menu_board_manufacturing"
parent="base.menu_reporting_dashboard"
- sequence="30"/>
+ sequence="30"
+ groups="group_mrp_manager"/>
diff --git a/addons/note/note.py b/addons/note/note.py
index 65848ed7828..a7bad7f6a55 100644
--- a/addons/note/note.py
+++ b/addons/note/note.py
@@ -22,6 +22,7 @@
from openerp.osv import osv, fields
from tools.translate import _
import re
+from openerp.tools.misc import html2plaintext
class note_stage(osv.osv):
""" Category of Note """
@@ -65,23 +66,24 @@ class note_note(osv.osv):
text_note = (note.memo or '').strip().split('\n')[0]
text_note = re.sub(r'(\S?)(
|<[/]?p>|<[/]?div>|
)[\s\S]*',r'\1',text_note)
text_note = re.sub(r'<[^>]+>','',text_note)
+ text_note = html2plaintext(text_note)
res[note.id] = text_note
return res
#unactivate a sticky note and record the date
def onclick_note_is_done(self, cr, uid, ids, context=None):
- self.write(cr, uid, ids, { 'active' : False, 'date_done' : fields.date.today() })
+ self.write(cr, uid, ids, { 'open' : False, 'date_done' : fields.date.today() })
self.message_post(cr, uid, ids[0], body='Note is done.', subject=False,
type='notification', parent_id=False, attachments=None, context=context)
return False
#activate a note
def onclick_note_not_done(self, cr, uid, ids, context=None):
- self.write(cr, uid, ids, {'active' : True})
+ self.write(cr, uid, ids, {'open' : True})
self.message_post(cr, uid, ids[0], body='Note has been activated.', subject=False,
type='notification', parent_id=False, attachments=None, context=context)
return False
-
+
#used for undisplay the follower if it's the current user
def _get_my_current_partner(self, cr, uid, ids, name, args, context=None):
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
@@ -119,14 +121,14 @@ class note_note(osv.osv):
type='many2one',
relation='note.stage'),
'stage_ids': fields.many2many('note.stage','note_stage_rel','note_id','stage_id','Stages of Users'),
- 'active': fields.boolean('Active'),
+ 'open': fields.boolean('Active'),
'date_done': fields.date('Date done'),
'color': fields.integer('Color Index'),
'tag_ids' : fields.many2many('note.tag','note_tags_rel','note_id','tag_id','Tags'),
'current_partner_id' : fields.function(_get_my_current_partner),
}
_defaults = {
- 'active' : 1,
+ 'open' : 1,
'stage_id' : _get_default_stage_id,
}
_order = 'sequence'
diff --git a/addons/note/note_demo.xml b/addons/note/note_demo.xml
index 82601f903c0..1cc1b2fc790 100644
--- a/addons/note/note_demo.xml
+++ b/addons/note/note_demo.xml
@@ -138,14 +138,5 @@
7
-
-
* Open ERP: a modern approach to integrated business management
-
* Open ERP for Retail and Industrial Management
- ]]>
-
-
-
-
diff --git a/addons/note/note_view.xml b/addons/note/note_view.xml
index 1166e687750..ccb07544c09 100644
--- a/addons/note/note_view.xml
+++ b/addons/note/note_view.xml
@@ -1,7 +1,6 @@
-
@@ -51,7 +50,7 @@
-
+
@@ -65,8 +64,8 @@
- W
- è
+ W
+ è
@@ -78,13 +77,12 @@
-
-
+
-
+
@@ -105,7 +103,7 @@
-
+
@@ -139,9 +137,8 @@
-
-
-
+
+
@@ -185,7 +182,7 @@
form
kanban,tree,form
- {'search_default_active_true':True}
+ {'search_default_open_true':True}
diff --git a/addons/note/security/ir.rule.xml b/addons/note/security/ir.rule.xml
index 3d2f0d242f6..1195314cc9e 100644
--- a/addons/note/security/ir.rule.xml
+++ b/addons/note/security/ir.rule.xml
@@ -1,12 +1,19 @@
-
- Only followers can access a sticky notes
-
- ['|',('message_follower_ids','=',False),('message_follower_ids','=',user.partner_id.id)]
-
-
+
+ Only followers can access a sticky notes
+
+ [('message_follower_ids','=',user.partner_id.id)]
+
+
+
+
+ Each user have his stage name
+
+ ['|',('user_id','=',False),('user_id','=',user.id)]
+
+
diff --git a/addons/portal_project_issue/portal_project_issue_view.xml b/addons/portal_project_issue/portal_project_issue_view.xml
index 5d0b4ce081e..1030dedbdf8 100644
--- a/addons/portal_project_issue/portal_project_issue_view.xml
+++ b/addons/portal_project_issue/portal_project_issue_view.xml
@@ -27,6 +27,7 @@
+
Creation:
diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py
index ec1dc4b26fb..842f9ec27fa 100644
--- a/addons/procurement/procurement.py
+++ b/addons/procurement/procurement.py
@@ -554,7 +554,7 @@ class stock_warehouse_orderpoint(osv.osv):
'logic': fields.selection([('max','Order to Max'),('price','Best price (not yet active!)')], 'Reordering Mode', required=True),
'warehouse_id': fields.many2one('stock.warehouse', 'Warehouse', required=True, ondelete="cascade"),
'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="cascade"),
- 'product_id': fields.many2one('product.product', 'Product', required=True, ondelete='cascade', domain=[('type','=','product')]),
+ 'product_id': fields.many2one('product.product', 'Product', required=True, ondelete='cascade', domain=[('type','!=','service')]),
'product_uom': fields.many2one('product.uom', 'Product Unit of Measure', required=True),
'product_min_qty': fields.float('Minimum Quantity', required=True,
help="When the virtual stock goes below the Min Quantity specified for this field, OpenERP generates "\
diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml
index fea22c519a8..be7427fe8f4 100644
--- a/addons/project_issue/project_issue_view.xml
+++ b/addons/project_issue/project_issue_view.xml
@@ -269,9 +269,7 @@