# -*- coding: utf-8 -*- # Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details. from datetime import datetime import random import itertools from flectra import api, models, fields, _ from flectra.addons.http_routing.models.ir_http import slug from flectra.tools.translate import html_translate from flectra.tools import html2plaintext class Blog(models.Model): _name = 'blog.blog' _description = 'Blogs' _inherit = ['mail.thread', 'website.seo.metadata'] _order = 'name' name = fields.Char('Blog Name', required=True, translate=True) subtitle = fields.Char('Blog Subtitle', translate=True) active = fields.Boolean('Active', default=True) @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 post_ids = self.env['blog.post'].with_context(active_test=False).search([ ('blog_id', 'in', self.ids) ]) for blog_post in post_ids: blog_post.active = vals['active'] return res @api.multi @api.returns('self', lambda value: value.id) def message_post(self, parent_id=False, subtype=None, **kwargs): """ Temporary workaround to avoid spam. If someone replies on a channel through the 'Presentation Published' email, it should be considered as a note as we don't want all channel followers to be notified of this answer. """ self.ensure_one() if parent_id: parent_message = self.env['mail.message'].sudo().browse(parent_id) if parent_message.subtype_id and parent_message.subtype_id == self.env.ref('website_blog.mt_blog_blog_published'): if kwargs.get('subtype_id'): kwargs['subtype_id'] = False subtype = 'mail.mt_note' return super(Blog, self).message_post(parent_id=parent_id, subtype=subtype, **kwargs) @api.multi def all_tags(self, min_limit=1): req = """ SELECT p.blog_id, count(*), r.blog_tag_id FROM blog_post_blog_tag_rel r join blog_post p on r.blog_post_id=p.id WHERE p.blog_id in %s GROUP BY p.blog_id, r.blog_tag_id ORDER BY count(*) DESC """ 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) BlogTag = self.env['blog.tag'] for blog_id in tag_by_blog: tag_by_blog[blog_id] = BlogTag.browse(tag_by_blog[blog_id]) return tag_by_blog class BlogTag(models.Model): _name = 'blog.tag' _description = 'Blog Tag' _inherit = ['website.seo.metadata'] _order = 'name' name = fields.Char('Name', required=True, translate=True) post_ids = fields.Many2many('blog.post', string='Posts') _sql_constraints = [ ('name_uniq', 'unique (name)', "Tag name already exists !"), ] class BlogPost(models.Model): _name = "blog.post" _description = "Blog Post" _inherit = ['mail.thread', 'website.seo.metadata', 'website.published.mixin'] _order = 'id DESC' _mail_post_access = 'read' @api.multi def _compute_website_url(self): super(BlogPost, self)._compute_website_url() for blog_post in self: blog_post.website_url = "/blog/%s/post/%s" % (slug(blog_post.blog_id), slug(blog_post)) @api.multi @api.depends('post_date', 'visits') def _compute_ranking(self): res = {} for blog_post in self: if blog_post.id: # avoid to rank one post not yet saved and so withtout post_date in case of an onchange. age = datetime.now() - fields.Datetime.from_string(blog_post.post_date) res[blog_post.id] = blog_post.visits * (0.5 + random.random()) / max(3, age.days) return res def _default_content(self): return '''

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

''' def _default_website(self): default_website_id = self.env.ref('website.default_website') return [default_website_id.id] if default_website_id else None 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_black", "opacity": "0.2", "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) teaser = fields.Text('Teaser', compute='_compute_teaser', inverse='_set_teaser') teaser_manual = fields.Text(string='Teaser Content') website_message_ids = fields.One2many(domain=lambda self: [('model', '=', self._name), ('message_type', '=', 'comment'), ('path', '=', False)]) # creation / update stuff create_date = fields.Datetime('Created on', index=True, readonly=True) published_date = fields.Datetime('Published Date') post_date = fields.Datetime('Publishing date', compute='_compute_post_date', inverse='_set_post_date', store=True, help="The blog post will be visible for your visitors as of this date on the website if it is set as published.") 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) author_avatar = fields.Binary(related='author_id.image_small', string="Avatar") visits = fields.Integer('No of Views', copy=False) ranking = fields.Float(compute='_compute_ranking', string='Ranking') website_ids = fields.Many2many('website', 'website_blog_pub_rel', 'website_id', 'blog_id', default=_default_website, string='Websites', copy=False, help='List of websites in which ' 'Blog Post will published.') @api.multi @api.depends('content', 'teaser_manual') def _compute_teaser(self): for blog_post in self: if blog_post.teaser_manual: blog_post.teaser = blog_post.teaser_manual else: content = html2plaintext(blog_post.content).replace('\n', ' ') blog_post.teaser = content[:150] + '...' @api.multi def _set_teaser(self): for blog_post in self: blog_post.teaser_manual = blog_post.teaser @api.multi @api.depends('create_date', 'published_date') def _compute_post_date(self): for blog_post in self: if blog_post.published_date: blog_post.post_date = blog_post.published_date else: blog_post.post_date = blog_post.create_date @api.multi def _set_post_date(self): for blog_post in self: blog_post.published_date = blog_post.post_date if not blog_post.published_date: blog_post._write(dict(post_date=blog_post.create_date)) # dont trigger inverse function def _check_for_publication(self, vals): if vals.get('website_published'): 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.env['ir.model.data'].xmlid_to_res_id('website_blog.mt_blog_blog_published')) return True return False @api.model def create(self, vals): post_id = super(BlogPost, self.with_context(mail_create_nolog=True)).create(vals) post_id._check_for_publication(vals) return post_id @api.multi def write(self, vals): result = True for post in self: copy_vals = dict(vals) if 'website_published' in vals and 'published_date' not in vals and (post.published_date or '') <= fields.Datetime.now(): copy_vals['published_date'] = vals['website_published'] and fields.Datetime.now() or False result &= super(BlogPost, self).write(copy_vals) self._check_for_publication(vals) return result @api.multi def get_access_action(self, access_uid=None): """ Instead of the classic form view, redirect to the post on website directly if user is an employee or if the post is published. """ self.ensure_one() user = access_uid and self.env['res.users'].sudo().browse(access_uid) or self.env.user if user.share and not self.sudo().website_published: return super(BlogPost, self).get_access_action(access_uid) return { 'type': 'ir.actions.act_url', 'url': self.website_url, 'target': 'self', 'target_type': 'public', 'res_id': self.id, } @api.multi def _notification_recipients(self, message, groups): groups = super(BlogPost, self)._notification_recipients(message, groups) for group_name, group_method, group_data in groups: group_data['has_button_access'] = True return groups @api.multi def message_get_message_notify_values(self, message, message_values): """ Override to avoid keeping all notified recipients of a comment. We avoid tracking needaction on post comments. Only emails should be sufficient. """ if message.message_type == 'comment': return { 'needaction_partner_ids': [], } return {}