diff --git a/addons/website_blog/controllers/__init__.py b/addons/website_blog/controllers/__init__.py index 8ee9bae18d9..f48716d3a69 100644 --- a/addons/website_blog/controllers/__init__.py +++ b/addons/website_blog/controllers/__init__.py @@ -1 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import main diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 5afb08840ea..3452ad24f26 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -1,11 +1,10 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import datetime import json import werkzeug -from odoo import http, tools, SUPERUSER_ID, _ +from odoo import http, fields, _ from odoo.addons.website.controllers.main import QueryURL from odoo.addons.website.models.website import slug, unslug from odoo.exceptions import UserError @@ -18,8 +17,7 @@ class WebsiteBlog(http.Controller): _post_comment_per_page = 10 def nav_list(self, blog=None): - blog_post_obj = request.env['blog.post'] - groups = blog_post_obj._read_group_raw( + groups = request.env['blog.post']._read_group_raw( blog and [('blog_id', '=', blog.id)] or [], ['name', 'create_date'], groupby=["create_date"], orderby="create_date desc") @@ -35,27 +33,22 @@ class WebsiteBlog(http.Controller): """ create_date is a datetime so start and end are datetime strings, but we just want date strings """ - return (datetime.datetime - .strptime(dt, tools.DEFAULT_SERVER_DATETIME_FORMAT) - .date() # may be unnecessary? - .strftime(tools.DEFAULT_SERVER_DATE_FORMAT)) + return fields.Date.from_string(dt) @http.route([ '/blog', '/blog/page/', ], type='http', auth="public", website=True) def blogs(self, page=1, **post): - cr, uid, context = request.cr, request.uid, request.context - blog_obj = request.registry['blog.post'] - total = blog_obj.search(cr, uid, [], count=True, context=context) + BlogPost = request.env['blog.post'] + total = BlogPost.search([], count=True) pager = request.website.pager( url='/blog', total=total, page=page, step=self._blog_post_per_page, ) - post_ids = blog_obj.search(cr, uid, [], offset=(page-1)*self._blog_post_per_page, limit=self._blog_post_per_page, context=context) - posts = blog_obj.browse(cr, uid, post_ids, context=context) + posts = BlogPost.search([], offset=(page-1)*self._blog_post_per_page, limit=self._blog_post_per_page) blog_url = QueryURL('', ['blog', 'tag']) return request.render("website_blog.latest_blogs", { 'posts': posts, @@ -88,12 +81,10 @@ class WebsiteBlog(http.Controller): date_begin, date_end, state = opt.get('date_begin'), opt.get('date_end'), opt.get('state') published_count, unpublished_count = 0, 0 - cr, uid, context = request.cr, request.uid, request.context - blog_post_obj = request.registry['blog.post'] + BlogPost = request.env['blog.post'] - blog_obj = request.registry['blog.blog'] - blog_ids = blog_obj.search(cr, uid, [], order="create_date asc", context=context) - blogs = blog_obj.browse(cr, uid, blog_ids, context=context) + Blog = request.env['blog.blog'] + blogs = Blog.search([], order="create_date asc") # build the domain for blog post to display domain = [] @@ -107,7 +98,7 @@ class WebsiteBlog(http.Controller): domain += [("create_date", ">=", date_begin), ("create_date", "<=", date_end)] if request.env.user.has_group('base.group_website_designer'): - for group in request.env['blog.post'].read_group(domain, ['website_published'], groupby=["website_published"]): + for group in BlogPost.read_group(domain, ['website_published'], groupby=["website_published"]): if group['website_published']: published_count = group['website_published_count'] else: @@ -119,8 +110,7 @@ class WebsiteBlog(http.Controller): blog_url = QueryURL('', ['blog', 'tag'], blog=blog, tag=tag, date_begin=date_begin, date_end=date_end) - blog_post_ids = blog_post_obj.search(cr, uid, domain, order="published_date desc, create_date desc", context=context) - blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context) + blog_posts = BlogPost.search(domain, order="published_date desc, create_date desc") pager = request.website.pager( url=blog_url(), total=len(blog_posts), @@ -141,7 +131,7 @@ class WebsiteBlog(http.Controller): tag_ids.remove(current_tag) else: tag_ids.append(current_tag) - tag_ids = request.registry['blog.tag'].browse(cr, uid, tag_ids, context=context).exists() + tag_ids = request.env['blog.tag'].browse(tag_ids).exists() return ','.join(map(slug, tag_ids)) values = { @@ -187,9 +177,7 @@ class WebsiteBlog(http.Controller): - 'nav_list': a dict [year][month] for archives navigation - 'next_post': next blog post, to direct the user towards the next interesting post """ - cr, uid, context = request.cr, request.uid, request.context - tag_obj = request.registry['blog.tag'] - blog_post_obj = request.registry['blog.post'] + BlogPost = request.env['blog.post'] date_begin, date_end = post.get('date_begin'), post.get('date_end') pager_url = "/blogpost/%s" % blog_post.id @@ -213,15 +201,15 @@ class WebsiteBlog(http.Controller): if not blog_post.blog_id.id == blog.id: return request.redirect("/blog/%s/post/%s" % (slug(blog_post.blog_id), slug(blog_post))) - tags = tag_obj.browse(cr, uid, tag_obj.search(cr, uid, [], context=context), context=context) + tags = request.env['blog.tag'].search([]) # Find next Post - all_post_ids = blog_post_obj.search(cr, uid, [('blog_id', '=', blog.id)], context=context) + all_post_ids = BlogPost.search([('blog_id', '=', blog.id)]).ids # should always return at least the current post current_blog_post_index = all_post_ids.index(blog_post.id) next_post_id = all_post_ids[0 if current_blog_post_index == len(all_post_ids) - 1 \ else current_blog_post_index + 1] - next_post = next_post_id and blog_post_obj.browse(cr, uid, next_post_id, context=context) or False + next_post = next_post_id and BlogPost.browse(next_post_id) or False values = { 'tags': tags, @@ -245,37 +233,32 @@ class WebsiteBlog(http.Controller): if not (blog_post.id in request.session[request.session_id]): request.session[request.session_id].append(blog_post.id) # Increase counter - blog_post_obj.write(cr, SUPERUSER_ID, [blog_post.id], { + blog_post.sudo().write({ 'visits': blog_post.visits+1, - },context=context) + }) return response def _blog_post_message(self, blog_post_id, message_content, **post): - cr, uid, context = request.cr, request.uid, request.context - BlogPost = request.registry['blog.post'] - User = request.registry['res.users'] + BlogPost = request.env['blog.post'] # for now, only portal and user can post comment on blog post. - if uid == request.website.user_id.id: + if request.env.user.id == request.website.user_id.id: raise UserError(_('Public user cannot post comments on blog post.')) # get the partner of the current user - user = User.browse(cr, uid, uid, context=context) - partner_id = user.partner_id.id + partner_id = request.env.user.partner_id.id - message_id = BlogPost.message_post( - cr, uid, int(blog_post_id), + message = BlogPost.message_post( + int(blog_post_id), body=message_content, message_type='comment', subtype='mt_comment', author_id=partner_id, path=post.get('path', False), - context=context) - return message_id + ) + return message.id def _get_discussion_detail(self, ids, publish=False, **post): - cr, uid, context = request.cr, request.uid, request.context values = [] - mail_obj = request.registry.get('mail.message') - for message in mail_obj.browse(cr, SUPERUSER_ID, ids, context=context): + for message in request.env['mail.message'].sudo().browse(ids): values.append({ "id": message.id, "author_name": message.author_id.name, @@ -291,19 +274,16 @@ class WebsiteBlog(http.Controller): @http.route(['/blog/post_discussion'], type='json', auth="public", website=True) def post_discussion(self, blog_post_id, **post): - cr, uid = request.cr, request.uid - publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher') - id = self._blog_post_message(blog_post_id, post.get('comment'), **post) - return self._get_discussion_detail([id], publish, **post) + publish = request.env.user.has_group('base.group_website_publisher') + message_id = self._blog_post_message(blog_post_id, post.get('comment'), **post) + return self._get_discussion_detail([message_id], publish, **post) @http.route('/blog//post/new', type='http', auth="public", website=True) def blog_post_create(self, blog_id, **post): - cr, uid, context = request.cr, request.uid, request.context - new_blog_post_id = request.registry['blog.post'].create(cr, uid, { + new_blog_post = request.env['blog.post'].create({ 'blog_id': blog_id, 'website_published': False, - }, context=context) - new_blog_post = request.registry['blog.post'].browse(cr, uid, new_blog_post_id, context=context) + }) return werkzeug.utils.redirect("/blog/%s/post/%s?enable_editor=1" % (slug(new_blog_post.blog_id), slug(new_blog_post))) @http.route('/blog/post_duplicate', type='http', auth="public", website=True, methods=['POST']) @@ -314,26 +294,20 @@ class WebsiteBlog(http.Controller): :return redirect to the new blog created """ - cr, uid, context = request.cr, request.uid, request.context - create_context = dict(context, mail_create_nosubscribe=True) - nid = request.registry['blog.post'].copy(cr, uid, int(blog_post_id), {}, context=create_context) - new_blog_post = request.registry['blog.post'].browse(cr, uid, nid, context=context) - post = request.registry['blog.post'].browse(cr, uid, nid, context) - return werkzeug.utils.redirect("/blog/%s/post/%s?enable_editor=1" % (slug(post.blog_id), slug(new_blog_post))) + new_blog_post = request.env['blog.post'].with_context(mail_create_nosubscribe=True).copy(int(blog_post_id), {}) + return werkzeug.utils.redirect("/blog/%s/post/%s?enable_editor=1" % (slug(new_blog_post.blog_id), slug(new_blog_post))) @http.route('/blog/post_get_discussion/', type='json', auth="public", website=True) def discussion(self, post_id=0, path=None, count=False, **post): - cr, uid, context = request.cr, request.uid, request.context - mail_obj = request.registry.get('mail.message') domain = [('res_id', '=', int(post_id)), ('model', '=', 'blog.post'), ('path', '=', path)] #check current user belongs to website publisher group - publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher') + publish = request.env.user.has_group('base.group_website_publisher') if not publish: domain.append(('website_published', '=', True)) - ids = mail_obj.search(cr, SUPERUSER_ID, domain, count=count) + messages = request.env['mail.message'].sudo().search(domain, count=count) if count: - return ids - return self._get_discussion_detail(ids, publish, **post) + return messages.ids + return self._get_discussion_detail(messages.ids, publish, **post) @http.route('/blog/post_get_discussions/', type='json', auth="public", website=True) def discussions(self, post_id=0, paths=None, count=False, **post): @@ -347,7 +321,7 @@ class WebsiteBlog(http.Controller): def change_bg(self, post_id=0, cover_properties={}, **post): if not post_id: return False - return request.registry['blog.post'].write(request.cr, request.uid, [int(post_id)], {'cover_properties': json.dumps(cover_properties)}, request.context) + return request.env['blog.post'].browse(int(post_id)).write({'cover_properties': json.dumps(cover_properties)}) @http.route('/blog/get_user/', type='json', auth="public", website=True) def get_user(self, **post): diff --git a/addons/website_blog/models/__init__.py b/addons/website_blog/models/__init__.py index 3dfe1df4d07..4397ed49f4c 100644 --- a/addons/website_blog/models/__init__.py +++ b/addons/website_blog/models/__init__.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import mail_message import website import website_blog diff --git a/addons/website_blog/models/mail_message.py b/addons/website_blog/models/mail_message.py index c5b1750dd09..683445822cd 100644 --- a/addons/website_blog/models/mail_message.py +++ b/addons/website_blog/models/mail_message.py @@ -1,14 +1,12 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.osv import osv, fields +from odoo import fields, models -class MailMessage(osv.Model): +class MailMessage(models.Model): _inherit = 'mail.message' - _columns = { - 'path': fields.char( - 'Discussion Path', select=1, - help='Used to display messages in a paragraph-based chatter using a unique path;'), - } + path = fields.Char( + 'Discussion Path', index=True, + help='Used to display messages in a paragraph-based chatter using a unique path;') diff --git a/addons/website_blog/models/website_blog.py b/addons/website_blog/models/website_blog.py index 0c0e38ea440..19256e17e01 100644 --- a/addons/website_blog/models/website_blog.py +++ b/addons/website_blog/models/website_blog.py @@ -5,39 +5,35 @@ import lxml import random from datetime import datetime - -from odoo import api, tools, SUPERUSER_ID +from odoo import api, models, fields, _ from odoo.addons.website.models.website import slug -from odoo.osv import osv, fields -from odoo.tools.translate import _, html_translate +from odoo.tools.translate import html_translate -class Blog(osv.Model): +class Blog(models.Model): _name = 'blog.blog' _description = 'Blogs' _inherit = ['mail.thread', 'website.seo.metadata'] _order = 'name' - _columns = { - 'name': fields.char('Blog Name', required=True, translate=True), - 'subtitle': fields.char('Blog Subtitle', translate=True), - 'active': fields.boolean('Active'), - } - _defaults = { - 'active': True, - } + name = fields.Char('Blog Name', required=True, translate=True) + subtitle = fields.Char('Blog Subtitle', translate=True) + active = fields.Boolean('Active', default=True) - def write(self, cr, uid, ids, vals, context=None): - res = super(Blog, self).write(cr, uid, ids, vals, context=context) + @api.multi + def write(self, vals): + res = super(Blog, self).write(vals) if 'active' in vals: # archiving/unarchiving a blog does it on its posts, too - BlogPost = self.pool['blog.post'] - ctx = dict(context or {}, active_test=False) - post_ids = BlogPost.search(cr, uid, [('blog_id', 'in', ids)], context=ctx) - BlogPost.write(cr, uid, post_ids, {'active': vals['active']}, context=context) + post_ids = self.env['blog.post'].with_context(active_test=False).search([ + ('blog_id', 'in', self) + ]) + post_ids.write({'active': vals['active']}) return res - def all_tags(self, cr, uid, ids, min_limit=1, context=None): + @api.model_cr + @api.multi + def all_tags(self, min_limit=1): req = """ SELECT p.blog_id, count(*), r.blog_tag_id @@ -52,35 +48,33 @@ class Blog(osv.Model): ORDER BY count(*) DESC """ - cr.execute(req, [tuple(ids)]) - tag_by_blog = {i: [] for i in ids} - for blog_id, freq, tag_id in cr.fetchall(): + self._cr.execute(req, [tuple(self.ids)]) + tag_by_blog = {i.id: [] for i in self} + for blog_id, freq, tag_id in self._cr.fetchall(): if freq >= min_limit: tag_by_blog[blog_id].append(tag_id) - tag_obj = self.pool['blog.tag'] + BlogTag = self.env['blog.tag'] for blog_id in tag_by_blog: - tag_by_blog[blog_id] = tag_obj.browse(cr, uid, tag_by_blog[blog_id], context=context) + tag_by_blog[blog_id] = BlogTag.browse(tag_by_blog[blog_id]) return tag_by_blog -class BlogTag(osv.Model): +class BlogTag(models.Model): _name = 'blog.tag' _description = 'Blog Tag' _inherit = ['website.seo.metadata'] _order = 'name' - _columns = { - 'name': fields.char('Name', required=True), - 'post_ids': fields.many2many( - 'blog.post', string='Posts', - ), - } + + name = fields.Char('Name', required=True) + post_ids = fields.Many2many('blog.post', string='Posts') + _sql_constraints = [ - ('name_uniq', 'unique (name)', "Tag name already exists !"), + ('name_uniq', 'unique (name)', "Tag name already exists !"), ] -class BlogPost(osv.Model): +class BlogPost(models.Model): _name = "blog.post" _description = "Blog Post" _inherit = ['mail.thread', 'website.seo.metadata', 'website.published.mixin'] @@ -93,76 +87,50 @@ class BlogPost(osv.Model): for blog_post in self: blog_post.website_url = "/blog/%s/post/%s" % (slug(blog_post.blog_id), slug(blog_post)) - def _compute_ranking(self, cr, uid, ids, name, arg, context=None): + @api.multi + def _compute_ranking(self): res = {} - for blog_post in self.browse(cr, uid, ids, context=context): - age = datetime.now() - datetime.strptime(blog_post.create_date, tools.DEFAULT_SERVER_DATETIME_FORMAT) - res[blog_post.id] = blog_post.visits * (0.5+random.random()) / max(3, age.days) + for blog_post in self: + age = datetime.now() - fields.Datetime.from_string(blog_post.create_date) + res[blog_post.id] = blog_post.visits * (0.5 + random.random()) / max(3, age.days) return res - def _default_content(self, cr, uid, context=None): + def _default_content(self): return '''

''' + _("Start writing here...") + '''

''' - _columns = { - 'name': fields.char('Title', required=True, translate=True), - 'subtitle': fields.char('Sub Title', translate=True), - 'author_id': fields.many2one('res.partner', 'Author'), - 'active': fields.boolean('Active'), - 'cover_properties': fields.text('Cover Properties'), - 'blog_id': fields.many2one( - 'blog.blog', 'Blog', - required=True, ondelete='cascade', - ), - 'tag_ids': fields.many2many( - 'blog.tag', string='Tags', - ), - 'content': fields.html('Content', translate=html_translate, sanitize=False), - 'website_message_ids': fields.one2many( - 'mail.message', 'res_id', - domain=lambda self: [ - '&', '&', ('model', '=', self._name), ('message_type', '=', 'comment'), ('path', '=', False) - ], - string='Website Messages', - help="Website communication history", - ), - # creation / update stuff - 'create_date': fields.datetime( - 'Created on', - select=True, readonly=True, - ), - 'create_uid': fields.many2one( - 'res.users', 'Created by', - select=True, readonly=True, - ), - 'write_date': fields.datetime( - 'Last Modified on', - select=True, readonly=True, - ), - 'write_uid': fields.many2one( - 'res.users', 'Last Contributor', - select=True, readonly=True, - ), - 'published_date': fields.datetime('Published Date'), - 'author_avatar': fields.related( - 'author_id', 'image_small', - string="Avatar", type="binary"), - 'visits': fields.integer('No of Views'), - 'ranking': fields.function(_compute_ranking, string='Ranking', type='float'), - } + name = fields.Char('Title', required=True, translate=True, default='') + subtitle = fields.Char('Sub Title', translate=True) + author_id = fields.Many2one('res.partner', 'Author', default=lambda self: self.env.user.partner_id) + active = fields.Boolean('Active', default=True) + cover_properties = fields.Text( + 'Cover Properties', + default='{"background-image": "none", "background-color": "oe_none", "opacity": "0.6", "resize_class": ""}') + blog_id = fields.Many2one('blog.blog', 'Blog', required=True, ondelete='cascade') + tag_ids = fields.Many2many('blog.tag', string='Tags') + content = fields.Html('Content', default=_default_content, translate=html_translate, sanitize=False) + website_message_ids = fields.One2many( + 'mail.message', 'res_id', + domain=lambda self: [ + '&', '&', ('model', '=', self._name), ('message_type', '=', 'comment'), ('path', '=', False) + ], + string='Website Messages', + help="Website communication history", + ) + # creation / update stuff + create_date = fields.Datetime('Created on', index=True, readonly=True) + create_uid = fields.Many2one('res.users', 'Created by', index=True, readonly=True) + write_date = fields.Datetime('Last Modified on', index=True, readonly=True) + write_uid = fields.Many2one('res.users', 'Last Contributor', index=True, readonly=True) + published_date = fields.Datetime('Published Date') + author_avatar = fields.Binary(related='author_id.image_small', string="Avatar") + visits = fields.Integer('No of Views') + ranking = fields.Float(compute='_compute_ranking', string='Ranking') - _defaults = { - 'name': '', - 'active': True, - 'content': _default_content, - 'cover_properties': '{"background-image": "none", "background-color": "oe_none", "opacity": "0.6", "resize_class": ""}', - 'author_id': lambda self, cr, uid, ctx=None: self.pool['res.users'].browse(cr, uid, uid, context=ctx).partner_id.id, - } - - def html_tag_nodes(self, html, attribute=None, tags=None, context=None): + def html_tag_nodes(self, html, attribute=None, tags=None): """ Processing of html content to tag paragraphs and set them an unique ID. :return result: (html, mappin), where html is the updated html with ID @@ -210,74 +178,76 @@ class BlogPost(osv.Model): html = html[5:-6] return html, mapping - def _postproces_content(self, cr, uid, id, content=None, context=None): + # noguess used to be able to call the method with record = None in the create method + @api.noguess + def _postproces_content(self, record, content=None): if content is None: - content = self.browse(cr, uid, id, context=context).content + content = self.content if content is False: return content - content, mapping = self.html_tag_nodes(content, attribute='data-chatter-id', tags=['p'], context=context) - if id: # not creating + content, mapping = self.html_tag_nodes(content, attribute='data-chatter-id', tags=['p']) + if record: # not creating existing = [x[0] for x in mapping if x[0]] - msg_ids = self.pool['mail.message'].search(cr, SUPERUSER_ID, [ - ('res_id', '=', id), + self.env['mail.message'].sudo().search([ + ('res_id', '=', record.id), ('model', '=', self._name), ('path', 'not in', existing), ('path', '!=', False) - ], context=context) - self.pool['mail.message'].unlink(cr, SUPERUSER_ID, msg_ids, context=context) + ]).sudo().unlink() return content - def _check_for_publication(self, cr, uid, ids, vals, context=None): + @api.multi + def _check_for_publication(self, vals): if vals.get('website_published'): - for post in self.browse(cr, uid, ids, context=context): + for post in self: post.blog_id.message_post_with_view( 'website_blog.blog_post_template_new_post', subject=post.name, values={'post': post}, - subtype_id=self.pool['ir.model.data'].xmlid_to_res_id(cr, SUPERUSER_ID, 'website_blog.mt_blog_blog_published')) + subtype_id=self.env['ir.model.data'].sudo().xmlid_to_res_id('website_blog.mt_blog_blog_published')) return True return False - def create(self, cr, uid, vals, context=None): - if context is None: - context = {} + @api.model + def create(self, vals): if 'content' in vals: - vals['content'] = self._postproces_content(cr, uid, None, vals['content'], context=context) - create_context = dict(context, mail_create_nolog=True) - post_id = super(BlogPost, self).create(cr, uid, vals, context=create_context) - self._check_for_publication(cr, uid, [post_id], vals, context=context) + vals['content'] = self._postproces_content(None, vals['content']) + post_id = super(BlogPost, self.with_context(mail_create_nolog=True)).create(vals) + post_id._check_for_publication(vals) return post_id - def write(self, cr, uid, ids, vals, context=None): - if isinstance(ids, (int, long)): - ids = [ids] + @api.multi + def write(self, vals): + self.ensure_one() if 'content' in vals: - vals['content'] = self._postproces_content(cr, uid, ids[0], vals['content'], context=context) + vals['content'] = self._postproces_content(self, vals['content']) if 'website_published' in vals: - vals['published_date'] = fields.datetime.now() if vals['website_published'] else False - result = super(BlogPost, self).write(cr, uid, ids, vals, context) - self._check_for_publication(cr, uid, ids, vals, context=context) + vals['published_date'] = fields.Datetime.now() if vals['website_published'] else False + result = super(BlogPost, self).write(vals) + self._check_for_publication(vals) return result - def get_access_action(self, cr, uid, ids, context=None): + @api.multi + def get_access_action(self): """ Override method that generated the link to access the document. Instead of the classic form view, redirect to the post on the website directly """ - post = self.browse(cr, uid, ids[0], context=context) + self.ensure_one() return { 'type': 'ir.actions.act_url', - 'url': '/blog/%s/post/%s' % (post.blog_id.id, post.id), + 'url': '/blog/%s/post/%s' % (self.blog_id.id, self.id), 'target': 'self', 'res_id': self.id, } - def _notification_get_recipient_groups(self, cr, uid, ids, message, recipients, context=None): + @api.multi + def _notification_get_recipient_groups(self, message, recipients): """ Override to set the access button: everyone can see an access button on their notification email. It will lead on the website view of the post. """ - res = super(BlogPost, self)._notification_get_recipient_groups(cr, uid, ids, message, recipients, context=context) - access_action = self._notification_link_helper(cr, uid, ids, 'view', model=message.model, res_id=message.res_id) + res = super(BlogPost, self)._notification_get_recipient_groups(message, recipients) + access_action = self._notification_link_helper('view', model=message.model, res_id=message.res_id) for category, data in res.iteritems(): res[category]['button_access'] = {'url': access_action, 'title': _('View Blog Post')} return res