1 # -*- coding: utf-8 -*-
3 from urlparse import urljoin
6 from openerp import SUPERUSER_ID
7 from openerp.addons.website.models.website import slug
8 from openerp.osv import osv, fields
9 from openerp.tools.translate import _
12 class Forum(osv.Model):
13 """TDE TODO: set karma values for actions dynamic for a given forum"""
15 _description = 'Forums'
16 _inherit = ['website.seo.metadata']
18 _karma_upvote = 5 # done
19 _karma_downvote = 50 # done
20 _karma_answer_accept_own = 20 # done
21 _karma_answer_accept_own_now = 50
22 _karma_answer_accept_all = 500
23 _karma_editor_link_files = 30 # done
24 _karma_editor_clickable_link = 50
26 _karma_modo_retag = 75
27 _karma_modo_flag = 100
28 _karma_modo_flag_see_all = 300
29 _karma_modo_unlink_comment = 750
30 _karma_modo_edit_own = 1 # done
31 _karma_modo_edit_all = 300 # done
32 _karma_modo_close_own = 100 # done
33 _karma_modo_close_all = 900 # done
34 _karma_modo_unlink_own = 500 # done
35 _karma_modo_unlink_all = 1000 # done
37 _karma_gen_quest_new = 2 # done
38 _karma_gen_upvote_quest = 5 # done
39 _karma_gen_downvote_quest = -2 # done
40 _karma_gen_upvote_ans = 10 # done
41 _karma_gen_downvote_ans = -2 # done
42 _karma_gen_ans_accept = 2 # done
43 _karma_gen_ans_accepted = 15 # done
44 _karma_gen_ans_flagged = -100
47 'name': fields.char('Name', required=True, translate=True),
48 'faq': fields.html('Guidelines'),
49 'description': fields.html('Description'),
52 def _get_default_faq(self, cr, uid, context=None):
53 fname = openerp.modules.get_module_resource('website_forum', 'data', 'forum_default_faq.html')
54 with open(fname, 'r') as f:
59 'description': 'This community is for professionals and enthusiasts of our products and services.',
60 'faq': _get_default_faq,
63 def create(self, cr, uid, values, context=None):
66 create_context = dict(context, mail_create_nolog=True)
67 return super(Forum, self).create(cr, uid, values, context=create_context)
70 class Post(osv.Model):
72 _description = 'Forum Post'
73 _inherit = ['mail.thread', 'website.seo.metadata']
74 _order = "is_correct DESC, vote_count DESC"
76 def _get_user_vote(self, cr, uid, ids, field_name, arg, context):
77 res = dict.fromkeys(ids, 0)
78 vote_ids = self.pool['forum.post.vote'].search(cr, uid, [('post_id', 'in', ids), ('user_id', '=', uid)], context=context)
79 for vote in self.pool['forum.post.vote'].browse(cr, uid, vote_ids, context=context):
80 res[vote.post_id.id] = vote.vote
83 def _get_vote_count(self, cr, uid, ids, field_name, arg, context):
84 res = dict.fromkeys(ids, 0)
85 for post in self.browse(cr, uid, ids, context=context):
86 for vote in post.vote_ids:
87 res[post.id] += int(vote.vote)
90 def _get_post_from_vote(self, cr, uid, ids, context=None):
92 for vote in self.pool['forum.post.vote'].browse(cr, uid, ids, context=context):
93 result[vote.post_id.id] = True
96 def _get_user_favourite(self, cr, uid, ids, field_name, arg, context):
97 res = dict.fromkeys(ids, False)
98 for post in self.browse(cr, uid, ids, context=context):
99 if uid in [f.id for f in post.favourite_ids]:
103 def _get_favorite_count(self, cr, uid, ids, field_name, arg, context):
104 res = dict.fromkeys(ids, 0)
105 for post in self.browse(cr, uid, ids, context=context):
106 res[post.id] += len(post.favourite_ids)
109 def _get_post_from_hierarchy(self, cr, uid, ids, context=None):
111 for post in self.browse(cr, SUPERUSER_ID, ids, context=context):
113 post_ids.add(post.parent_id.id)
114 return list(post_ids)
116 def _get_child_count(self, cr, uid, ids, field_name=False, arg={}, context=None):
117 res = dict.fromkeys(ids, 0)
118 for post in self.browse(cr, uid, ids, context=context):
120 res[post.parent_id.id] = len(post.parent_id.child_ids)
122 res[post.id] = len(post.child_ids)
125 def _get_uid_answered(self, cr, uid, ids, field_name, arg, context=None):
126 res = dict.fromkeys(ids, False)
127 for post in self.browse(cr, uid, ids, context=context):
128 res[post.id] = any(answer.create_uid.id == uid for answer in post.child_ids)
131 def _get_has_validated_answer(self, cr, uid, ids, field_name, arg, context=None):
132 res = dict.fromkeys(ids, False)
133 ans_ids = self.search(cr, uid, [('parent_id', 'in', ids), ('is_correct', '=', True)], context=context)
134 for answer in self.browse(cr, uid, ans_ids, context=context):
135 res[answer.parent_id.id] = True
138 def _is_self_reply(self, cr, uid, ids, field_name, arg, context=None):
139 res = dict.fromkeys(ids, False)
140 for post in self.browse(cr, uid, ids, context=context):
141 res[post.id] = post.parent_id and post.parent_id.create_uid == post.create_uid or False
145 'name': fields.char('Title', size=128),
146 'forum_id': fields.many2one('forum.forum', 'Forum', required=True),
147 'content': fields.html('Content'),
148 'tag_ids': fields.many2many('forum.tag', 'forum_tag_rel', 'forum_id', 'forum_tag_id', 'Tags'),
149 'state': fields.selection([('active', 'Active'), ('close', 'Close'), ('offensive', 'Offensive')], 'Status'),
150 'views': fields.integer('Number of Views'),
151 'active': fields.boolean('Active'),
152 'is_correct': fields.boolean('Valid Answer', help='Correct Answer or Answer on this question accepted.'),
153 'website_message_ids': fields.one2many(
154 'mail.message', 'res_id',
155 domain=lambda self: [
156 '&', ('model', '=', self._name), ('type', '=', 'comment')
158 string='Post Messages', help="Comments on forum post",
161 'create_date': fields.datetime('Asked on', select=True, readonly=True),
162 'create_uid': fields.many2one('res.users', 'Created by', select=True, readonly=True),
163 'write_date': fields.datetime('Update on', select=True, readonly=True),
164 'write_uid': fields.many2one('res.users', 'Updated by', select=True, readonly=True),
166 'vote_ids': fields.one2many('forum.post.vote', 'post_id', 'Votes'),
167 'user_vote': fields.function(_get_user_vote, string='My Vote', type='integer'),
168 'vote_count': fields.function(
169 _get_vote_count, string="Votes", type='integer',
171 'forum.post': (lambda self, cr, uid, ids, c={}: ids, ['vote_ids'], 10),
172 'forum.post.vote': (_get_post_from_vote, [], 10),
175 'favourite_ids': fields.many2many('res.users', string='Favourite'),
176 'user_favourite': fields.function(_get_user_favourite, string="My Favourite", type='boolean'),
177 'favourite_count': fields.function(
178 _get_favorite_count, string='Favorite Count', type='integer',
180 'forum.post': (lambda self, cr, uid, ids, c={}: ids, ['favourite_ids'], 10),
183 'parent_id': fields.many2one('forum.post', 'Question', ondelete='cascade'),
184 'self_reply': fields.function(
185 _is_self_reply, 'Reply to own question', type='boolean',
187 'forum.post': (lambda self, cr, uid, ids, c={}: ids, ['parent_id', 'create_uid'], 10),
189 'child_ids': fields.one2many('forum.post', 'parent_id', 'Answers'),
190 'child_count': fields.function(
191 _get_child_count, string="Answers", type='integer',
193 'forum.post': (_get_post_from_hierarchy, ['parent_id', 'child_ids'], 10),
195 'uid_has_answered': fields.function(
196 _get_uid_answered, string='Has Answered', type='boolean',
198 'has_validated_answer': fields.function(
199 _get_has_validated_answer, string='Has a Validated Answered', type='boolean',
201 'forum.post': (_get_post_from_hierarchy, ['parent_id', 'child_ids', 'is_correct'], 10),
205 'closed_reason_id': fields.many2one('forum.post.reason', 'Reason'),
206 'closed_uid': fields.many2one('res.users', 'Closed by', select=1),
207 'closed_date': fields.datetime('Closed on', readonly=True),
215 'favourite_ids': list(),
219 def create(self, cr, uid, vals, context=None):
222 create_context = dict(context, mail_create_nolog=True)
223 post_id = super(Post, self).create(cr, uid, vals, context=create_context)
224 # post message + subtype depending on parent_id
225 if vals.get("parent_id"):
226 parent = self.browse(cr, SUPERUSER_ID, vals['parent_id'], context=context)
227 body = _('<p><a href="forum/%s/question/%s">New Answer Posted</a></p>' % (slug(parent.forum_id), slug(parent)))
228 self.message_post(cr, uid, parent.id, subject=_('Re: %s') % parent.name, body=body, subtype='website_forum.mt_answer_new', context=context)
230 self.message_post(cr, uid, post_id, subject=vals.get('name', ''), body=_('New Question Created'), subtype='website_forum.mt_question_new', context=context)
231 self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], self.pool['forum.forum']._karma_gen_quest_new, context=context)
234 def write(self, cr, uid, ids, vals, context=None):
235 Forum = self.pool['forum.forum']
236 # update karma when accepting/rejecting answers
237 if 'is_correct' in vals:
238 mult = 1 if vals['is_correct'] else -1
239 for post in self.browse(cr, uid, ids, context=context):
240 if vals['is_correct'] != post.is_correct:
241 self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], Forum._karma_gen_ans_accepted * mult, context=context)
242 self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [uid], Forum._karma_gen_ans_accept * mult, context=context)
243 res = super(Post, self).write(cr, uid, ids, vals, context=context)
244 # if post content modify, notify followers
245 if 'content' in vals or 'name' in vals:
246 for post in self.browse(cr, uid, ids, context=context):
248 body, subtype = _('Answer Edited'), 'website_forum.mt_answer_edit'
249 obj_id = post.parent_id.id
251 body, subtype = _('Question Edited'), 'website_forum.mt_question_edit'
253 self.message_post(cr, uid, obj_id, body=_(body), subtype=subtype, context=context)
256 def vote(self, cr, uid, ids, upvote=True, context=None):
257 Vote = self.pool['forum.post.vote']
258 vote_ids = Vote.search(cr, uid, [('post_id', 'in', ids), ('user_id', '=', uid)], context=context)
260 for vote in Vote.browse(cr, uid, vote_ids, context=context):
262 new_vote = '0' if vote.vote == '-1' else '1'
264 new_vote = '0' if vote.vote == '1' else '-1'
265 Vote.write(cr, uid, vote_ids, {'vote': new_vote}, context=context)
268 new_vote = '1' if upvote else '-1'
269 Vote.create(cr, uid, {'post_id': post_id, 'vote': new_vote}, context=context)
270 return {'vote_count': self._get_vote_count(cr, uid, ids, None, None, context=context)[ids[0]]}
272 def set_viewed(self, cr, uid, ids, context=None):
273 cr.execute("""UPDATE forum_post SET views = views+1 WHERE id IN %s""", (tuple(ids),))
276 def _get_access_link(self, cr, uid, mail, partner, context=None):
277 post = self.pool['forum.post'].browse(cr, uid, mail.res_id, context=context)
278 res_id = post.parent_id and "%s#answer-%s" % (post.parent_id.id, post.id) or post.id
279 return "/forum/%s/question/%s" % (post.forum_id.id, res_id)
282 class PostReason(osv.Model):
283 _name = "forum.post.reason"
284 _description = "Post Closing Reason"
287 'name': fields.char('Post Reason', required=True, translate=True),
291 class Vote(osv.Model):
292 _name = 'forum.post.vote'
293 _description = 'Vote'
295 'post_id': fields.many2one('forum.post', 'Post', ondelete='cascade', required=True),
296 'user_id': fields.many2one('res.users', 'User', required=True),
297 'vote': fields.selection([('1', '1'), ('-1', '-1'), ('0', '0')], 'Vote', required=True),
298 'create_date': fields.datetime('Create Date', select=True, readonly=True),
301 'user_id': lambda self, cr, uid, ctx: uid,
302 'vote': lambda *args: '1',
305 def create(self, cr, uid, vals, context=None):
306 vote_id = super(Vote, self).create(cr, uid, vals, context=context)
307 post = self.pool['forum.post'].browse(cr, uid, vals['post_id'], context=context)
309 if vals.get('vote', '1') == '1':
311 karma = self.pool['forum.forum']._karma_gen_upvote_ans
313 karma = self.pool['forum.forum']._karma_gen_upvote_quest
314 elif vals.get('vote', '1') == '-1':
316 karma = self.pool['forum.forum']._karma_gen_downvote_ans
318 karma = self.pool['forum.forum']._karma_gen_downvote_quest
319 self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [post.create_uid.id], karma, context=context)
322 def write(self, cr, uid, ids, values, context=None):
323 def _get_karma_value(old_vote, new_vote, up_karma, down_karma):
325 '-1': {'-1': 0, '0': -1 * down_karma, '1': -1 * down_karma + up_karma},
326 '0': {'-1': 1 * down_karma, '0': 0, '1': up_karma},
327 '1': {'-1': -1 * up_karma + down_karma, '0': -1 * up_karma, '1': 0}
329 return _karma_upd[old_vote][new_vote]
331 Forum = self.pool['forum.forum']
332 for vote in self.browse(cr, uid, ids, context=context):
333 if vote.post_id.parent_id:
334 karma_value = _get_karma_value(vote.vote, values['vote'], Forum._karma_gen_upvote_ans, Forum._karma_gen_downvote_ans)
336 karma_value = _get_karma_value(vote.vote, values['vote'], Forum._karma_gen_upvote_quest, Forum._karma_gen_downvote_quest)
337 self.pool['res.users'].add_karma(cr, SUPERUSER_ID, [vote.post_id.create_uid.id], karma_value, context=context)
338 res = super(Vote, self).write(cr, uid, ids, values, context=context)
342 class Tags(osv.Model):
345 _inherit = ['website.seo.metadata']
347 def _get_posts_count(self, cr, uid, ids, field_name, arg, context=None):
348 return dict((tag_id, self.pool['forum.post'].search_count(cr, uid, [('tag_ids', 'in', tag_id)], context=context)) for tag_id in ids)
350 def _get_tag_from_post(self, cr, uid, ids, context=None):
352 [tag.id for post in self.pool['forum.post'].browse(cr, SUPERUSER_ID, ids, context=context) for tag in post.tag_ids]
356 'name': fields.char('Name', required=True),
357 'forum_id': fields.many2one('forum.forum', 'Forum', required=True),
358 'post_ids': fields.many2many('forum.post', 'forum_tag_rel', 'tag_id', 'post_id', 'Posts'),
359 'posts_count': fields.function(
360 _get_posts_count, type='integer', string="Number of Posts",
362 'forum.post': (_get_tag_from_post, ['tag_ids'], 10),
365 'create_uid': fields.many2one('res.users', 'Created by', readonly=True),