[IMP] website_blog: refactoring

[IMP] website_blog: display author avatar option
[IMP] website_blog: refactore cover option for blog
[IMP] website_blog: tags are now always displayed and as a label
[IMP] website_blog: side bar cleaning
[IMP] website_blog: new teaser
[IMP] website_blog: replace inline comments by quotes comment
[IMP] website_blog: new grid layout
[WIP] website_blog: read next options
[IMP] website_blog: refactoring sharing links
[IMP] website_blog: Delete cover narrow option
[IMP] website_blog: improvement for sharing links
[IMP] website_blog: blog post list cover is an option
[IMP] website_blog: archives group by year
[IMP] website_blog: if only one blog, no index blog post

Courtesy of @movanoncem
This commit is contained in:
Jeremy Kersten
2016-08-10 18:38:28 +02:00
parent c335c2dcf9
commit 8cc850f3a5
13 changed files with 515 additions and 670 deletions
-1
View File
@@ -28,7 +28,6 @@ Odoo Blog
'test': [
],
'qweb': [
'static/src/xml/*.xml'
],
'installable': True,
'application': True,
+13 -4
View File
@@ -3,6 +3,7 @@
import json
import werkzeug
import itertools
from odoo import http, fields, _
from odoo.addons.website.controllers.main import QueryURL
@@ -25,9 +26,11 @@ class WebsiteBlog(http.Controller):
(r, label) = group['create_date']
start, end = r.split('/')
group['create_date'] = label
group['date_begin'] = self._to_date(start)
group['date_end'] = self._to_date(end)
return groups
group['date_begin'] = fields.Date.to_string(self._to_date(start))
group['date_end'] = fields.Date.to_string(self._to_date(end))
group['month'] = self._to_date(start).strftime("%B")
group['year'] = self._to_date(start).strftime("%Y")
return {year: [m for m in months] for year, months in itertools.groupby(groups, lambda g: g['year'])}
def _to_date(self, dt):
""" create_date is a datetime so start and end are datetime strings,
@@ -40,8 +43,14 @@ class WebsiteBlog(http.Controller):
'/blog/page/<int:page>',
], type='http', auth="public", website=True)
def blogs(self, page=1, **post):
Blog = request.env['blog.blog']
blogs = Blog.search([], limit=2)
if len(blogs) == 1:
return werkzeug.utils.redirect('/blog/%s' % slug(blogs[0]), code=302)
BlogPost = request.env['blog.post']
total = BlogPost.search([], count=True)
pager = request.website.pager(
url='/blog',
total=total,
@@ -133,7 +142,6 @@ class WebsiteBlog(http.Controller):
tag_ids.append(current_tag)
tag_ids = request.env['blog.tag'].browse(tag_ids).exists()
return ','.join(map(slug, tag_ids))
values = {
'blog': blog,
'blogs': blogs,
@@ -143,6 +151,7 @@ class WebsiteBlog(http.Controller):
'active_tag_ids': active_tag_ids,
'tags_list' : tags_list,
'blog_posts': blog_posts,
'blog_posts_cover_properties': [json.loads(b.cover_properties) for b in blog_posts],
'pager': pager,
'nav_list': self.nav_list(blog),
'blog_url': blog_url,
+19 -75
View File
@@ -1,14 +1,14 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import lxml
from datetime import datetime
from lxml import html
import random
from datetime import datetime
from odoo import api, models, fields, _
from odoo.addons.website.models.website import slug
from odoo.tools.translate import html_translate
from odoo.tools import html2plaintext
class Blog(models.Model):
_name = 'blog.blog'
@@ -31,7 +31,6 @@ class Blog(models.Model):
post_ids.write({'active': vals['active']})
return res
@api.model_cr
@api.multi
def all_tags(self, min_limit=1):
req = """
@@ -112,6 +111,9 @@ class BlogPost(models.Model):
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='_get_teaser', inverse='_set_teaser')
teaser_manual = fields.Text(string='Teaser Content')
website_message_ids = fields.One2many(
'mail.message', 'res_id',
domain=lambda self: [
@@ -130,75 +132,21 @@ class BlogPost(models.Model):
visits = fields.Integer('No of Views')
ranking = fields.Float(compute='_compute_ranking', string='Ranking')
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
and mapping is a list of (old_ID, new_ID), where old_ID
is None is the paragraph is a new one. """
existing_attributes = []
mapping = []
if not html:
return html, mapping
if tags is None:
tags = ['p']
if attribute is None:
attribute = 'data-unique-id'
# form a tree
root = lxml.html.fragment_fromstring(html, create_parent='div')
if not len(root) and root.text is None and root.tail is None:
return html, mapping
# check all nodes, replace :
# - img src -> check URL
# - a href -> check URL
for node in root.iter():
if node.tag not in tags:
continue
ancestor_tags = [parent.tag for parent in node.iterancestors()]
old_attribute = node.get(attribute)
new_attribute = old_attribute
if not new_attribute or (old_attribute in existing_attributes):
if ancestor_tags:
ancestor_tags.pop()
counter = random.randint(10000, 99999)
ancestor_tags.append('counter_%s' % counter)
new_attribute = '/'.join(reversed(ancestor_tags))
node.set(attribute, new_attribute)
existing_attributes.append(new_attribute)
mapping.append((old_attribute, new_attribute))
html = lxml.html.tostring(root, pretty_print=False, method='html')
# this is ugly, but lxml/etree tostring want to put everything in a 'div' that breaks the editor -> remove that
if html.startswith('<div>') and html.endswith('</div>'):
html = html[5:-6]
return html, mapping
# 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.content
if content is False:
return content
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]]
self.env['mail.message'].sudo().search([
('res_id', '=', record.id),
('model', '=', self._name),
('path', 'not in', existing),
('path', '!=', False)
]).sudo().unlink()
return content
@api.multi
@api.depends('content', 'teaser_manual')
def _get_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 = ' '.join(filter(None, content.split(' '))[:50]) + '...'
@api.multi
def _set_teaser(self):
for blog_post in self:
blog_post.teaser_manual = blog_post.teaser
def _check_for_publication(self, vals):
if vals.get('website_published'):
for post in self:
@@ -212,8 +160,6 @@ class BlogPost(models.Model):
@api.model
def create(self, vals):
if 'content' in vals:
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
@@ -221,8 +167,6 @@ class BlogPost(models.Model):
@api.multi
def write(self, vals):
self.ensure_one()
if 'content' in vals:
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(vals)
@@ -1,65 +0,0 @@
(function(){
$.fn.share = function(options) {
var option = $.extend($.fn.share.defaults,options);
$.extend($.fn.share,{
init : function(shareable) {
var self = this;
$.fn.share.defaults.shareable = shareable;
$.fn.share.defaults.shareable.on('mouseup',function(){
if ($(this).parents('body.editor_enable').length === 0) {
self.popOver();
}
});
$.fn.share.defaults.shareable.on('mousedown',function(){
self.destroy();
});
},
getContent : function() {
var current_url = window.location.href
var selected_text = this.getSelection('string').substring(0,option.maxLength-(current_url.length+option.author_name.length+7));
var text = encodeURIComponent('\"'+selected_text+'\" '+'--@'+option.author_name+' '+current_url)
return '<a onclick="window.open(\''+option.shareLink+text+'\',\'_'+option.target+'\',\'location=yes,height=570,width=520,scrollbars=yes,status=yes\')"><i class="fa fa-twitter fa-lg"/></a>';
},
getSelection : function(share) {
if(window.getSelection){
return (share=='string')?String(window.getSelection().getRangeAt(0)).replace(/\s{2,}/g, ' '):window.getSelection().getRangeAt(0);
}
else if(document.selection){
return (share=='string')?document.selection.createRange().text.replace(/\s{2,}/g, ' '):document.selection.createRange();
}
},
popOver : function() {
this.destroy();
if(this.getSelection('string').length < option.minLength)
return;
var data = this.getContent();
var range = this.getSelection();
var newNode = document.createElement("mark");
range.surroundContents(newNode);
$('mark').addClass(option.className);
$('.'+option.className).popover({trigger:'manual', placement: option.placement, html: true
, content:function(){
return data;
}
});
$('.'+option.className).popover('show');
},
destroy : function(){
$('.'+option.className).popover('hide');
$('mark').contents().unwrap();
$('mark').remove();
}
});
$.fn.share.init(this);
};
$.fn.share.defaults = {
shareLink : "http://twitter.com/intent/tweet?text=",
minLength : 5,
maxLength : 140,
target : "blank",
className : "share",
placement : "top",
};
}());
@@ -58,6 +58,10 @@ div#blog_angle_down a:hover {
height: 100vh;
}
.cover_midscreen {
height: 50vh;
}
.cover_narrow {
height: 50vh;
overflow: hidden;
@@ -95,103 +99,10 @@ div#blog_angle_down a:hover {
top: 2%;
}
}
/*Inline Discussion */
.discussion {
padding: 5px 10px 10px;
position: absolute;
top: 0;
left: 0;
line-height: 16px;
font-size: 13px;
font-weight: bold;
font-family: sans-serif;
text-align: center;
z-index: 7;
}
.discussion > a {
opacity: 0;
display: block;
overflow: hidden;
width: 20px;
height: 17px;
color: white;
text-decoration: none;
cursor: pointer;
background: #bbbbbb;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
-ms-border-radius: 2px;
-o-border-radius: 2px;
border-radius: 2px;
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
-o-transition: all 0.5s;
transition: all 0.5s;
}
.discussion > a.has-comments {
opacity: 0.6;
}
.discussion-contain:hover .discussion > a {
opacity: 1;
}
.discussion > a:after {
border-right: 7px solid transparent;
border-top: 7px solid #bbbbbb;
right: 19px;
top: 22px;
height: 0;
width: 0;
display: block;
content: " ";
position: absolute;
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
-o-transition: all 0.5s;
transition: all 0.5s;
}
.discussion:hover > a, .discussion.hovered > a {
opacity: 1;
background: #57AD68;
}
.discussion:hover > a:after, .discussion.hovered > a:after {
border-top-color: #57AD68;
}
#discussions_wrapper {
position: absolute;
top: 0;
left: 0;
}
#discussions_overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
z-index: 8;
display: none;
}
.discussion .popover-content {
max-height: 250px;
width: 250px;
overflow: auto;
font-weight: normal;
padding-bottom: 0px;
}
mark + .popover, .cover_footer {
cursor: pointer;
}
.discussion_scroll_post {
overflow-x: auto;
}
.blog_publish_btn {
padding-top: 16px;
}
@@ -202,11 +113,107 @@ mark + .popover, .cover_footer {
#js_blogcover {
background-blend-mode: multiply;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
height: 100%;
}
.o_blog_post_title {
font-size: 24px;
}
.o_blog_post_teaser {
text-align: justify;
}
/*To display the grid or inline presentation of the blog.post_list */
.o_grid {
margin: 0 6% 6% 0;
border: 1px solid #EEEEEE;
box-shadow: 0.5px 0.5px 0.5px 0.5px #eeeeee;
padding-bottom: 15px;
}
.o_grid .o_blog_post_teaser {
width: 100%;
}
.o_grid > .text-muted > .mb0 > hr {
display: none;
}
#js_blogcover  {
height: 150px;
opacity: 1 !important;
}
.o_author_avatar_little {
width: 40px;
display: inline;
margin-top: -5px;
}
/* Sharing links style */
.o_sharing_links {
margin-top: 20px;
}
.o_sharing_links > p {
display: inline;
font-size: 12px;
}
.o_sharing_links > a {
color: #b3b3b3;
font: normal normal normal 28px/1 FontAwesome;
margin: 0 4px 0 4px;
cursor: pointer;
}
.o_sharing_links > a:hover {
text-decoration: none;
}
.o_sharing_links > a.o_twitter_complete, .o_sharing_links > a.o_twitter:hover {
color: #4099FF;
}
.o_sharing_links > a.o_google_complete, .o_sharing_links > a.o_google:hover {
color: #DD4B39;
}
.o_sharing_links > a.o_facebook_complete, .o_sharing_links > a.o_facebook:hover {
color: #3b5998;
}
.o_sharing_links > a.o_linkedin_complete, .o_sharing_links > a.o_linkedin:hover {
color: #0E76A8;
}
.o_sharing_links > a.unhover {
color: #b3b3b3;
}
.o_blog_post_complete.o_sharing_links {
max-width: 700px;
margin: 50px auto 50px auto;
text-align: center;
}
.o_blog_post_complete.o_sharing_links > p {
font-size: 16px;
font-weight: 600;
}
.o_blog_post_complete.o_sharing_links > a {
font: normal normal normal 40px/1 FontAwesome;
}
div[name="blog_post_data"] {
margin-top: 20px !important;
font-size: 13px;
}
div[name="blog_post"] {
position: relative;
}
div[name="blog_post"] hr {
margin-top: 10px;
}
/*Button "unpublished"/"published" on each article */
.container.blog_publish_btn {
position: absolute;
right: 20px;
z-index: 1000;
}
/*# sourceMappingURL=website_blog.css.map */
@@ -48,6 +48,9 @@ div#blog_angle_down
.cover_full
height: 100vh
.cover_midscreen
height: 50vh
.cover_narrow
height: 50vh
overflow: hidden
@@ -81,93 +84,9 @@ div#blog_angle_down
.blog_title
top: 2%
/*Inline Discussion
.discussion
padding: 5px 10px 10px
position: absolute
top: 0
left: 0
line-height: 16px
font-size: 13px
font-weight: bold
font-family: sans-serif
text-align: center
z-index: 7
> a
opacity: 0
display: block
overflow: hidden
width: 20px
height: 17px
color: white
text-decoration: none
cursor: pointer
background: #bbbbbb
-webkit-border-radius: 2px
-moz-border-radius: 2px
-ms-border-radius: 2px
-o-border-radius: 2px
border-radius: 2px
-webkit-transition: all 0.5s
-moz-transition: all 0.5s
-o-transition: all 0.5s
transition: all 0.5s
&.has-comments
opacity: .6
.discussion-contain:hover .discussion > a
opacity: 1
.discussion
> a:after
border-right: 7px solid transparent
border-top: 7px solid #bbbbbb
right: 19px
top: 22px
height: 0
width: 0
display: block
content: " "
position: absolute
-webkit-transition: all 0.5s
-moz-transition: all 0.5s
-o-transition: all 0.5s
transition: all 0.5s
&:hover > a, &.hovered > a
opacity: 1
background: #57AD68
&:hover > a:after, &.hovered > a:after
border-top-color: #57AD68
#discussions_wrapper
position: absolute
top: 0
left: 0
#discussions_overlay
position: fixed
top: 0
left: 0
right: 0
bottom: 0
background: rgba(0, 0, 0, 0.5)
z-index: 8
display: none
.discussion .popover-content
max-height: 250px
width: 250px
overflow: auto
font-weight: normal
padding-bottom: 0px
mark + .popover, .cover_footer
cursor: pointer
.discussion_scroll_post
overflow-x: auto
.blog_publish_btn
padding-top: 16px
@@ -176,10 +95,86 @@ mark + .popover, .cover_footer
#js_blogcover
background-blend-mode: multiply
-webkit-background-size: cover
-moz-background-size: cover
-o-background-size: cover
background-size: cover
background-position: center
background-repeat: no-repeat
height: 100%
.o_blog_post_title
font-size: 24px
.o_blog_post_teaser
text-align: justify
/*To display the grid or inline presentation of the blog.post_list*/
.o_grid
margin: 0 6% 6% 0
border: 1px solid #EEEEEE
box-shadow: .5px .5px .5px .5px rgba(238,238,238,1)
padding-bottom: 15px
.o_blog_post_teaser
width: 100%
> .text-muted > .mb0 > hr
display: none
#js_blogcover 
height: 150px
opacity: 1 !important
.o_author_avatar_little
width: 40px
display: inline
margin-top: -5px
/* Sharing links style */
.o_sharing_links
margin-top: 20px
>
p
display: inline
font-size: 12px
a
color: #b3b3b3
font: normal normal normal 28px/1 FontAwesome
margin: 0 4px 0 4px
cursor: pointer
&:hover
text-decoration: none
&.o_twitter_complete, &.o_twitter:hover
color: #4099FF
&.o_google_complete, &.o_google:hover
color: #DD4B39
&.o_facebook_complete, &.o_facebook:hover
color: #3b5998
&.o_linkedin_complete, &.o_linkedin:hover
color: #0E76A8
&.unhover
color: #b3b3b3
.o_blog_post_complete.o_sharing_links
max-width: 700px
margin: 50px auto 50px auto
text-align: center
>
p
font-size: 16px
font-weight: 600
a
font: normal normal normal 40px/1 FontAwesome
div
&[name="blog_post_data"]
margin-top: 20px !important
font-size: 13px
&[name="blog_post"]
position: relative
hr
margin-top: 10px
/*Button "unpublished"/"published" on each article
.container.blog_publish_btn
position: absolute
right: 20px
z-index: 1000
@@ -0,0 +1,96 @@
(function(){
$.fn.share = function(options) {
var option = $.extend($.fn.share.defaults,options);
var selected_text ="";
$.extend($.fn.share,{
init : function(shareable) {
var self = this;
$.fn.share.defaults.shareable = shareable;
$.fn.share.defaults.shareable.on('mouseup',function(){
if ($(this).parents('body.editor_enable').length === 0) {
self.popOver();
}
});
$.fn.share.defaults.shareable.on('mousedown',function(){
self.destroy();
});
},
getContent : function() {
var popover_content = '';
if($('.blog_title, .blog_content').hasClass('js_comment')){
selected_text = this.getSelection('string');
popover_content += '<a class="o_share_comment mr12"><i class="fa fa-comment fa-lg mr4 ml4"/></a>';
}
if($('.blog_title, .blog_content').hasClass('js_tweet')){
var current_url = window.location.href;
var tweet_selected_text = this.getSelection('string').substring(0,option.maxLength-(current_url.length+option.author_name.length+7));
var text = encodeURIComponent(_.str.sprintf('\"%s\" --@%s %s', tweet_selected_text, option.author_name, current_url));
popover_content += '<a onclick="window.open(\''+option.shareLink+text+'\',\'_'+option.target+'\',\'location=yes,height=570,width=520,scrollbars=yes,status=yes\')"><i class="ml4 mr4 fa fa-twitter fa-lg"/></a>';
}
return popover_content;
},
commentEdition : function(){
var positionComment = ($('#comments').position()).top-50;
$(".o_website_chatter_form textarea").val('"' + selected_text + '" ').focus();
$('html, body').stop().animate({
'scrollTop': positionComment
}, 500, 'swing', function () {
window.location.hash = 'blog_post_comment_quote';
});
},
getSelection : function(share) {
if(window.getSelection){
if (share === 'string') {
return String(window.getSelection().getRangeAt(0)).replace(/\s{2,}/g, ' ');
} else {
return window.getSelection().getRangeAt(0);
}
}
else if(document.selection){
if (share === 'string') {
return document.selection.createRange().text.replace(/\s{2,}/g, ' ');
} else {
return document.selection.createRange();
}
}
},
popOver : function() {
this.destroy();
if(this.getSelection('string').length < option.minLength)
return;
var data = this.getContent();
var range = this.getSelection();
var newNode = document.createElement("span");
range.insertNode(newNode);
newNode.className = option.className;
var $pop = $(newNode);
$pop.popover({
trigger:'manual',
placement: option.placement,
html: true,
content: function(){
return data;
}
}).popover('show');
$('.o_share_comment').on('click', this.commentEdition);
},
destroy : function(){
var $span = $('span.'+option.className);
$span.popover('hide');
$span.remove();
}
});
$.fn.share.init(this);
};
$.fn.share.defaults = {
shareLink : "http://twitter.com/intent/tweet?text=",
minLength : 5,
maxLength : 140,
target : "blank",
className : "share",
placement : "top",
};
}());
@@ -45,6 +45,12 @@ odoo.define('website_blog.editor', function (require) {
}
rte.Class.include({
// Destroy popOver and stop listening mouseup event on edit mode
start: function () {
var self = this;
$(".js_tweet, .js_comment").off('mouseup').trigger('mousedown');
return this._super.apply(this, arguments);
},
saveElement: function ($el, context) {
if ($el.is('.website_blog #title')) {
return ajax.jsonRpc("/blog/post_change_background", 'call', {
@@ -61,7 +67,6 @@ odoo.define('website_blog.editor', function (require) {
},
});
options.registry.many2one.include({
select_record: function (li) {
var self = this;
@@ -91,7 +96,7 @@ odoo.define('website_blog.editor', function (require) {
this.src = null;
this.$cover.css({"background-image": '', 'min-height': ''});
this.$image.removeAttr("src");
this.$target.removeClass('cover cover_full cover_narrow');
this.$target.removeClass('cover cover_full cover_narrow cover_midscreen cover_mid_narrow');
},
change : function(type, value, $li) {
if (type !== 'click') return;
@@ -99,7 +104,7 @@ odoo.define('website_blog.editor', function (require) {
var editor = new widget.MediaDialog(null, {only_images: true}, this.$image, this.$image[0]).open();
editor.on('saved', self, function (event, img) {
var url = self.$image.attr('src');
self.$cover.css({"background-image": url ? 'url(' + url + ')' : "", 'min-height': $(window).height()-this.$cover.offset().top});
self.$cover.css({"background-image": url ? 'url(' + url + ')' : ""});
self.$target.addClass('o_dirty cover cover_full');
self.set_active();
});
@@ -123,7 +128,15 @@ odoo.define('website_blog.editor', function (require) {
this.value = this.$cover.css('opacity');
this.$el.parent().find('.snippet-option-website_blog:not(li[data-change])').toggleClass("hidden", !this.$target.hasClass("cover"));
this.$el.find('li[data-bgcolor], li[data-opacity], li[data-cover_class]').removeClass("active");
this.$el.find('[data-bgcolor="' + this.background + '"], [data-opacity="' + parseFloat(this.value).toFixed(1) + '"], [data-cover_class*="' + ((this.class||'').indexOf('cover_full') === -1 ? 'container' : 'cover_full') + '"]').addClass("active");
this.$el.find('[data-bgcolor="' + this.background + '"], [data-opacity="' + parseFloat(this.value).toFixed(1) + '"]').addClass("active");
if ((this.class || '').indexOf('cover_narrow') != -1) {
this.$el.find('[data-cover_class*=cover_narrow]').addClass("active");
} else if ((this.class || '').indexOf('cover_midscreen') != -1) {
this.$el.find('[data-cover_class*=cover_midscreen]').addClass("active");
} else {
this.$el.find('[data-cover_class*=cover_full]').addClass("active");
}
},
});
});
@@ -1,180 +0,0 @@
odoo.define('website_blog.InlineDiscussion', function (require) {
'use strict';
// Inspired from https://github.com/tsi/inlineDisqussions
var ajax = require('web.ajax');
var core = require('web.core');
var base = require('web_editor.base');
var qweb = core.qweb;
ajax.loadXML('/website_blog/static/src/xml/website_blog.inline.discussion.xml', qweb);
var InlineDiscussion = core.Class.extend({
init: function(options) {
var defaults = {
position: 'right',
post_id: $('#blog_post_name').attr('data-blog-id'),
content : false,
public_user: false,
};
this.settings = $.extend({}, defaults, options);
},
start: function() {
var self = this;
var $wrapper = $('#discussions_wrapper');
if (!$wrapper.length && this.settings.content.length > 0) {
$wrapper = $('<div id="discussions_wrapper"></div>').insertAfter($('#blog_content'));
}
// Attach a discussion to each paragraph.
this.discussions_handler(this.settings.content);
// Open/close the discussion.
$('html').click(function (event) {
var $target = $(event.target);
var open;
if ((!$target.is('.discussion-link:not(.active)') && !$target.closest('.popover').length) || !$target.closest('#discussions_wrapper').length) {
self.hide_discussion();
open = false;
} else if ($target.is('.discussion-link')) {
self.get_discussion($target);
open = true;
}
if (open === !$wrapper.hasClass('o_move_discuss')) {
$wrapper.toggleClass('o_move_discuss', open);
$wrapper.animate({
'marginLeft': (open ? '-' : '+')+"=20%"
});
$('#blog_content[enable_chatter_discuss=True]').animate({
'marginLeft': (open ? '-' : '+')+"=40%"
});
}
});
},
prepare_data : function(identifier, comment_count) {
var self = this;
return ajax.jsonRpc("/blog/post_get_discussion/", 'call', {
'post_id': self.settings.post_id,
'path': identifier,
'count': comment_count, //if true only get length of total comment, display on discussion thread.
});
},
prepare_multi_data : function(identifiers, comment_count) {
var self = this;
return ajax.jsonRpc("/blog/post_get_discussions/", 'call', {
'post_id': self.settings.post_id,
'paths': identifiers,
'count': comment_count, //if true only get length of total comment, display on discussion thread.
});
},
discussions_handler: function() {
var self = this;
var node_by_id = {};
$(self.settings.content).each(function() {
var node = $(this);
var identifier = node.attr('data-chatter-id');
if (identifier) {
node_by_id[identifier] = node;
}
});
self.prepare_multi_data(_.keys(node_by_id), true).then( function (multi_data) {
_.forEach(multi_data, function(data) {
self.prepare_discuss_link(data.val, data.path, node_by_id[data.path]);
});
});
},
prepare_discuss_link : function(data, identifier, node) {
var self = this;
var cls = data > 0 ? 'discussion-link has-comments' : 'discussion-link';
var a = $('<a class="'+ cls +' css_editable_mode_hidden" />')
.attr('data-discus-identifier', identifier)
.attr('data-discus-position', self.settings.position)
.text(data > 0 ? data : '+')
.attr('data-contentwrapper', '.mycontent')
.wrap('<div class="discussion" />')
.parent()
.appendTo('#discussions_wrapper');
a.css({
'top': node.offset().top,
'left': self.settings.position == 'right' ? node.outerWidth() + node.offset().left: node.offset().left - a.outerWidth()
});
// node.attr('data-discus-identifier', identifier)
node.mouseover(function() {
a.addClass("hovered");
}).mouseout(function() {
a.removeClass("hovered");
});
},
get_discussion : function(source) {
var self = this;
var identifier = source.attr('data-discus-identifier');
self.hide_discussion();
self.discus_identifier = identifier;
var elt = $('a[data-discus-identifier="'+identifier+'"]');
self.settings.current_url = window.location;
elt.append(qweb.render("website.blog_discussion.popover", {'identifier': identifier , 'options': self.settings}));
var comment = '';
self.prepare_data(identifier,false).then(function(data){
_.each(data, function(res){
comment += qweb.render("website.blog_discussion.comment", {'res': res});
});
$('.discussion_history').html('<ul class="media-list mt8">' + comment + '</ul>');
self.create_popover(elt, identifier);
// Add 'active' class.
$('a.discussion-link, a.main-discussion-link').removeClass('active').filter(source).addClass('active');
elt.popover('hide').filter(source).popover('show');
});
},
create_popover : function(elt, identifier) {
var self = this;
elt.popover({
placement:'right',
trigger:'manual',
html:true, content:function(){
return $($(this).data('contentwrapper')).html();
}
}).parent().delegate(self).on('click','button#comment_post',function(e) {
e.stopImmediatePropagation();
self.post_discussion(identifier);
});
},
validate : function(public_user){
var comment = $(".popover textarea#inline_comment").val();
if(!comment) {
$('div#inline_comment').addClass('has-error');
return false;
}
$("div#inline_comment").removeClass('has-error');
$(".popover textarea#inline_comment").val('');
return [comment];
},
post_discussion : function() {
var self = this;
var val = self.validate(self.settings.public_user);
if(!val) return;
ajax.jsonRpc("/blog/post_discussion", 'call', {
'blog_post_id': self.settings.post_id,
'path': self.discus_identifier,
'comment': val[0],
}).then(function(res){
$(".popover ul.media-list").prepend(qweb.render("website.blog_discussion.comment", {'res': res[0]}));
var ele = $('a[data-discus-identifier="'+ self.discus_identifier +'"]');
ele.text(_.isNaN(parseInt(ele.text())) ? 1 : parseInt(ele.text())+1);
ele.addClass('has-comments');
});
},
hide_discussion : function() {
var self = this;
$('a[data-discus-identifier="'+ self.discus_identifier+'"]').popover('destroy');
$('a.discussion-link').removeClass('active');
}
});
return InlineDiscussion;
});
@@ -2,7 +2,6 @@ odoo.define('website_blog.website_blog', function (require) {
"use strict";
var ajax = require('web.ajax');
var InlineDiscussion = require('website_blog.InlineDiscussion');
function page_transist(event) {
event.preventDefault();
@@ -28,26 +27,51 @@ function animate(event) {
});
}
$(document).ready(function() {
if ($('.website_blog').length) {
var content = $("div[enable_chatter_discuss='True']").find('p[data-chatter-id]');
if (content) {
ajax.jsonRpc("/blog/get_user/", 'call', {}).then(function(data){
$('#discussions_wrapper').empty();
new InlineDiscussion({'content' : content, 'public_user':data[0]}).start();
});
}
$(".js_tweet").share({'author_name': $('#blog_author').text()});
$('.cover_footer').on('click', page_transist);
$('a[href^="#blog_content"]').on('click', animate);
if ($('#js_blogcover').length) {
$('#js_blogcover[style*="background-image: url"]').css('min-height', $(window).height()-$('#js_blogcover').offset().top);
function shareArticle(event){
var url = '';
if ($(this).is('*[class*="_complete"]')) {
var blog_title_complete = $('#blog_post_name').html() || '';
if ($(this).hasClass('o_twitter_complete')){
url = 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=Amazing blog article : '+blog_title_complete+"! Check it live: "+window.location.href;
} else if ($(this).hasClass('o_facebook_complete')){
url = 'https://www.facebook.com/sharer/sharer.php?u='+window.location.href;
} else if ($(this).hasClass('o_linkedin_complete')){
url = 'https://www.linkedin.com/shareArticle?mini=true&url='+window.location.href+'&title='+blog_title_complete;
} else{
url = 'https://plus.google.com/share?url='+window.location.href;
};
}
else {
var blog_post = $(this).parents("[name='blog_post']");
var blog_post_title = blog_post.find('.o_blog_post_title').html() || '';
var blog_article_link = blog_post.find('.o_blog_post_title').parent('a').attr('href');
if ($(this).hasClass('o_twitter')) {
url = 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=Amazing blog article : '+blog_post_title+"! "+window.location.host+blog_article_link;
} else if ($(this).hasClass('o_facebook')){
url = 'https://www.facebook.com/sharer/sharer.php?u='+window.location.host+blog_article_link;
} else if ($(this).hasClass('o_linkedin')){
url = 'https://www.linkedin.com/shareArticle?mini=true&url='+window.location.host+blog_article_link+'&title='+blog_post_title;
} else if ($(this).hasClass('o_google')){
url = 'https://plus.google.com/share?url='+window.location.host+blog_article_link;
}
}
});
window.open(url, "", "menubar=no, width=500, height=400");
}
$(document).ready(function() {
if ($('.website_blog').length) {
$(".js_tweet, .js_comment").share({'author_name': $('#blog_author').text()});
$('.cover_footer').on('click', page_transist);
$('a[href^="#blog_content"]').on('click', animate);
$('.o_twitter, .o_facebook, .o_linkedin, .o_google, .o_twitter_complete, .o_facebook_complete, .o_linkedin_complete, .o_google_complete').on('click', shareArticle);
}
/* Sharing links hover in blogpost */
$('div.o_blog_post_complete a').hover(
function() { $('div.o_blog_post_complete a').not('#'+this.id).addClass('unhover'); },
function() { $('div.o_blog_post_complete a').not('#'+this.id).removeClass('unhover'); }
);
});
});
@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates id="template" xml:space="preserve">
<t t-name="website.blog_discussion.comment">
<li class="media">
<div class="pull-left">
<img class="media-object img-circle" t-att-src="res.author_image" style="width: 30px;"/>
</div>
<div t-attf-class="pull-right btn-group js_publish_management #{res.website_published and 'css_published' or 'css_unpublished'} #{res.publish ? '' : 'hidden'}" data-object="mail.message" t-att-data-id="res.id">
<button class="btn btn-danger btn-xs js_publish_btn">Unpublished</button>
<button class="btn btn-success btn-xs js_publish_btn">Published</button>
</div>
<div class="media-body discussion_scroll_post">
<div t-esc='res.body'/>
<small class="text-muted">
by
<span t-esc='res.author_name'/>
</small>
</div>
</li>
</t>
<t t-name="website.blog_discussion.popover">
<div class="mycontent hidden">
<input name="discussion" t-att-value="identifier" type="hidden"/>
<input name="blog_post_id" t-att-value="options.post_id" type="hidden"/>
<div id="inline_comment">
<textarea rows="2" id="inline_comment" placeholder="Write a comment..." t-attf-class="mb8 form-control #{options.public_user ? 'hidden' : ''}"/>
</div>
<div t-attf-class="media-body discussion_scroll_post #{options.public_user ? '' : 'hidden'}">
Please <strong><a t-attf-href="/web/login/?redirect=#{options.current_url}"> Sign in</a></strong> to comment.
</div>
<button id='comment_post' t-attf-class="btn btn-primary btn-xs mb8 #{options.public_user ? 'hidden' : ''}">Post</button>
<div class="discussion_history"/>
</div>
</t>
</templates>
+1
View File
@@ -12,6 +12,7 @@
<a href="#">Size</a>
<ul class="dropdown-menu">
<li data-cover_class="cover cover_full"><a>Full Screen</a></li>
<li data-cover_class="cover cover_midscreen"><a>Mid screen</a></li>
<li data-cover_class="cover container cover_narrow"><a>Narrow</a></li>
</ul>
</li>
@@ -12,8 +12,7 @@
<xpath expr="." position="inside">
<link rel='stylesheet' href='/website_blog/static/src/css/website_blog.css'/>
<script type="text/javascript" src="/website_blog/static/lib/contentshare.js"/>
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.inline.discussion.js"></script>
<script type="text/javascript" src="/website_blog/static/src/js/contentshare.js"/>
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"/>
</xpath>
</template>
@@ -133,15 +132,12 @@
</p>
</div>
<div t-foreach="blog_posts" t-as="blog_post" class="mb32">
<t t-if="blog_post.author_avatar">
<span t-field="blog_post.author_avatar" class="o_not_editable" t-options='{"widget": "image", "class": "img-circle pull-right mt16 media-object"}' />
</t>
<div t-foreach="blog_posts" t-as="blog_post" class="mb32" name="blog_post">
<a t-attf-href="/blog/#{ slug(blog_post.blog_id) }/post/#{ slug(blog_post) }">
<h2 t-field="blog_post.name" class="mb4">Untitled Post</h2>
<h2 t-field="blog_post.name" class="mb4 o_blog_post_title">Untitled Post</h2>
</a>
<div class="text-muted">
<h4 t-field="blog_post.subtitle" class="mb4 mt4"></h4>
<p t-field="blog_post.teaser" class="mb12 mt12 o_blog_post_teaser"/>
<div name='blog_post_data' class='mb0'>
<span t-field="blog_post.author_id" style="display: inline-block;" t-options='{
"widget": "contact",
@@ -152,15 +148,27 @@
<span t-if="len(blog_post.message_ids) &gt; 0">
&#5760; <t t-esc="len(blog_post.message_ids)"/>
<t t-if="len(blog_post.message_ids) &lt;= 1" >comment</t>
<t t-if="len(blog_post.message_ids) > 1">comments</t>
<t t-if="len(blog_post.message_ids) &gt; 1">comments</t>
</span>
<span t-if="blog_post.visits &gt; 0">
&#5760; <t t-esc="blog_post.visits"/>
<t t-if="blog_post.visits &lt;= 1" >view</t>
<t t-if="blog_post.visits > 1">views</t>
<t t-if="blog_post.visits &gt; 1">views</t>
</span>
<span t-if="not blog_post.website_published" class="label label-danger">unpublished</span>
<span class="fa fa-tags"/>
<!-- To display tags //no options -->
<t t-foreach="blog_post.tag_ids" t-as="one_tag">
<a class="mr8" t-attf-href="#{blog_url(tag=one_tag.id, date_begin=False, date_end=False)}" t-esc="one_tag.name"/>
</t>
<div class="o_sharing_links">
<a class="fa fa-twitter-square o_twitter"></a>
<a class="fa fa-facebook-square o_facebook"></a>
<a class="fa fa-linkedin-square o_linkedin"></a>
<a class="fa fa-google-plus-square o_google"></a>
</div>
<hr/>
</div>
</div>
</div>
@@ -172,51 +180,66 @@
</t>
</template>
<!-- Option: Blog Post List: show tags -->
<template id="opt_blog_post_short_tags" name="Tags"
customize_show="True" inherit_id="website_blog.blog_post_short">
<xpath expr="//*[@name='blog_post_data']" position="inside">
<p class="post-meta text-muted text-center" t-if="len(blog_post.tag_ids)">
<span class="fa fa-tags"/>
<t t-foreach="blog_post.tag_ids" t-as="one_tag">
<a class="mr8" t-attf-href="#{blog_url(tag=one_tag.id, date_begin=False, date_end=False)}" t-esc="one_tag.name"/>
</t>
</p>
<!-- Option: Blog Post List: cover image -->
<template id="opt_blog_post_cover_image" name="Cover image"
customize_show="True" active="True" inherit_id="website_blog.blog_post_short">
<xpath expr="//h2[@t-field='blog_post.name']/.." position="before">
<t t-if="blog_posts_cover_properties[blog_post_index].get('background-image', 'none') != 'none'">
<div id="js_blogcover" t-attf-class="#{blog_posts_cover_properties[blog_post_index].get('background-color')}" t-attf-style="background-image: #{blog_posts_cover_properties[blog_post_index].get('background-image', 'none')};opacity:1; height:250px; margin-top: 15px; margin-bottom: 30px;"></div>
</t>
</xpath>
</template>
<!-- Option: Blog Post List: grid layout -->
<template id="opt_blog_post_grid_layout" name="Grid view"
customize_show="True" active="False" inherit_id="website_blog.blog_post_short">
<xpath expr="//div[@name='blog_post']" position="attributes">
<attribute name="class" add="col-sm-5 o_grid" separator=" "/>
</xpath>
</template>
<!-- Option: Blog Post List: display the author avatar -->
<template id="opt_blog_post_author_avatar" name="Author avatar"
customize_show="True" active="False" inherit_id="website_blog.blog_post_short">
<xpath expr="//span[@t-field='blog_post.author_id']" position="before">
<t t-if="blog_post.author_avatar">
<div t-field="blog_post.author_avatar" class="o_not_editable" style="display:inline" t-field-options='{"widget": "image", "class": "img-circle o_author_avatar_little "}' />
</t>
</xpath>
</template>
<!-- Blog Post Complete -->
<template id="blog_post_complete" name="Blog Post">
<t t-call="website_blog.index">
<div class="container blog_publish_btn">
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/>
<li>
<form class="duplicate hidden" action="/blog/post_duplicate" method="POST">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input name="blog_post_id" t-att-value="blog_post.id"/>
</form>
<a href="#" class="duplicate" onclick="$(this).prev('form').submit()">Duplicate</a>
</li>
</t>
</div>
<div t-attf-class="#{blog_post_cover_properties.get('resize_class', '')}" id="title" t-ignore="True">
<div id="js_blogcover" t-attf-class="#{blog_post_cover_properties.get('background-color')}" t-attf-style="background-image: #{blog_post_cover_properties.get('background-image', 'none')};opacity:#{blog_post_cover_properties.get('opacity', 1)}">
<div class="container blog_publish_btn">
<t t-call="website.publish_management">
<t t-set="object" t-value="blog_post"/>
<t t-set="publish_edit" t-value="True"/>
<li>
<form class="duplicate hidden" action="/blog/post_duplicate" method="POST">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<input name="blog_post_id" t-att-value="blog_post.id"/>
</form>
<a href="#" class="duplicate" onclick="$(this).prev('form').submit()">Duplicate</a>
</li>
</t>
</div>
</div>
<div id="js_blogcover" t-attf-class="#{blog_post_cover_properties.get('background-color')}" t-attf-style="background-image: #{blog_post_cover_properties.get('background-image', 'none')};opacity:#{blog_post_cover_properties.get('opacity', 1)}"></div>
<div class="blog_title">
<h1 t-field="blog_post.name" id="blog_post_name" t-att-data-blog-id="blog_post.id" placeholder="Blog Post Title"/>
<h2 t-field="blog_post.subtitle" placeholder="Subtitle"/>
<p class="post-meta text-muted text-center" name="blog_post_data"/>
<div>
<t t-if="blog_post.author_avatar">
<span t-field="blog_post.author_avatar" class="o_not_editable" t-options='{"widget": "image", "class": "img-circle", "style":"width: 30px; margin-right: 10px; display:inline"}' />
<p class="post-meta text-muted text-center" t-if="len(blog_post.tag_ids)">
<span class="fa fa-tags"/>
<t t-foreach="blog_post.tag_ids" t-as="one_tag">
<a class="label label-primary mr8" t-attf-href="#{blog_url(tag=one_tag.id)}" t-esc="one_tag.name"/>
</t>
<span t-field="blog_post.author_id" style="display: inline-block;" t-options='{
"widget": "contact",
"fields": ["name"]
}'/>
</div>
</p>
<div t-if="blog_post_cover_properties.get('resize_class') == 'cover cover_full'" id="blog_angle_down">
<strong><a href="#blog_content" class="fa fa-angle-down fa-3x fa-inverse mt4"/></strong>
</div>
@@ -225,29 +248,13 @@
<div id="blog_content" t-field="blog_post.content" class="blog_content mt32"/>
<t t-if="next_post">
<div class="cover cover_footer mb0 text-center" t-ignore="True">
<div id="js_blogcover" t-attf-class="cover_footer_bg #{next_post_cover_properties.get('background-color')}" t-attf-style="background-image: #{next_post_cover_properties.get('background-image')};opacity:#{next_post_cover_properties.get('opacity', '1')}"></div>
<div class="blog_title">
<a class="hidden js_next" t-attf-href="/blog/#{ slug(next_post.blog_id) }/post/#{ slug(next_post) }/#wrap"/>
<h1 t-field="next_post.name" placeholder="Blog Post Title"/>
<h2 t-field="next_post.subtitle" placeholder="Subtitle"/>
<div>
<t t-if="next_post.author_avatar">
<span t-field="next_post.author_avatar" t-options='{"widget": "image", "class": "img-circle", "style":"width: 30px; margin-right: 10px; display:inline"}' />
</t>
<span t-field="next_post.author_id" style="display: inline-block;" t-options='{
"widget": "contact",
"fields": ["name"]
}'/>
</div>
<p class="mt32">Read Next <span class="fa fa-long-arrow-right"/></p>
</div>
<div class="o_blog_post_complete o_sharing_links">
<a class="fa fa-twitter-square o_twitter_complete" id="o_twitter_complete"></a>
<a class="fa fa-facebook-square o_facebook_complete" id="o_facebook_complete"></a>
<a class="fa fa-linkedin-square o_linkedin_complete" id="o_linkedin_complete"></a>
<a class="fa fa-google-plus-square o_google_complete" id="o_google_complete"></a>
</div>
</t>
</t>
</template>
<!-- Options: Blog Post: breadcrumb -->
@@ -273,9 +280,8 @@
<!-- Options: Blog Post: user can reply -->
<template id="opt_blog_post_complete_comment" name="Allow blog post comment"
inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
<xpath expr="//div[@id='blog_content']" position="after">
<xpath expr="//div[@class='o_blog_post_complete o_sharing_links']" position="after">
<section id="comments" class="read_width">
<hr/>
<t t-call="website_mail.message_thread">
<t t-set="object" t-value="blog_post"/>
<t t-set="chatter_mode" t-value="'json'"/>
@@ -284,35 +290,66 @@
</xpath>
</template>
<!-- Options: Blog Post: user can select text for tweet -->
<!-- Options: Blog Post : Read Next Article -->
<template id="opt_blog_post_complete_read_next" name="Read next article"
inherit_id="website_blog.blog_post_complete" active="True" customize_show="True">
<xpath expr="//div[@id='comments']|//div[@class='o_blog_post_complete o_sharing_links']" position="after">
<t t-if="next_post">
<div class="cover cover_footer mb0 text-center" t-ignore="True">
<div id="js_blogcover" t-attf-class="cover_footer_bg #{next_post_cover_properties.get('background-color')}" t-attf-style="background-image: #{next_post_cover_properties.get('background-image')};opacity:#{next_post_cover_properties.get('opacity')}"></div>
<div class="blog_title">
<a class="hidden js_next" t-attf-href="/blog/#{ slug(next_post.blog_id) }/post/#{ slug(next_post) }/#wrap"/>
<h1 t-field="next_post.name" placeholder="Blog Post Title"/>
<h2 t-field="next_post.subtitle" placeholder="Subtitle"/>
<div>
<t t-if="next_post.author_avatar">
<span t-field="next_post.author_avatar" t-field-options='{"widget": "image", "class": "img-circle", "style":"width: 30px; margin-right: 10px; display:inline"}' />
</t>
<span t-field="next_post.author_id" style="display: inline-block;" t-field-options='{
"widget": "contact",
"fields": ["name"]
}'/>
</div>
<p class="mt32">Read Next <span class="fa fa-long-arrow-right"/></p>
</div>
</div>
</t>
</xpath>
</template>
<!-- Options: Blog Post: display the author avatar -->
<template id="opt_blog_post_author_avatar_display" name="Author Avatar"
inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
<xpath expr="//p[@name='blog_post_data']" position="after">
<div>
<t t-if="blog_post.author_avatar">
<span t-field="blog_post.author_avatar" class="o_not_editable" t-field-options='{"widget": "image", "class": "img-circle", "style":"width: 30px; margin-right: 10px; display:inline"}' />
</t>
<span t-field="blog_post.author_id" style="display: inline-block;" t-field-options='{
"widget": "contact",
"fields": ["name"]
}'/>
</div>
</xpath>
</template>
<template id="opt_blog_post_select_to_tweet" name="Select to Tweet"
inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
<xpath expr="//div[@id='blog_content']" position="attributes">
<attribute name="class">blog_content js_tweet mt32</attribute>
<attribute name="class" add="js_tweet" separator=" "/>
</xpath>
<xpath expr="//div[@id='title']//div[@class='blog_title']" position="attributes">
<attribute name="class">blog_title js_tweet</attribute>
<xpath expr="//div[@id='title']//div[contains(@class, 'blog_title')]" position="attributes">
<attribute name="class" add="js_tweet" separator=" "/>
</xpath>
</template>
<!-- Options: Blog Post: user can add Inline Discussion -->
<template id="opt_blog_post_inline_discussion" name="Allow comment in text"
inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
<template id="opt_blog_post_select_to_comment" name="Select to comment"
inherit_id="website_blog.opt_blog_post_complete_comment" customize_show="False">
<xpath expr="//div[@id='blog_content']" position="attributes">
<attribute name="enable_chatter_discuss">True</attribute>
<attribute name="class" add="js_comment" separator=" "/>
</xpath>
</template>
<!-- Options: Blog Post: show tags -->
<template id="opt_blog_post_complete_tags" name="Tags"
customize_show="True" inherit_id="website_blog.blog_post_complete">
<xpath expr="//p[@name='blog_post_data']" position="after">
<p class="post-meta text-muted text-center" t-if="len(blog_post.tag_ids)">
<span class="fa fa-tags"/>
<t t-foreach="blog_post.tag_ids" t-as="one_tag">
<a class="mr8" t-attf-href="#{blog_url(tag=one_tag.id)}" t-esc="one_tag.name"/>
</t>
</p>
<xpath expr="//div[@id='title']//div[contains(@class, 'blog_title')]" position="attributes">
<attribute name="class" add="js_comment" separator=" "/>
</xpath>
</template>
@@ -366,16 +403,19 @@
<section class="mt32">
<h4>Archives</h4>
<ul class="nav nav-pills nav-stacked">
<t t-foreach="nav_list" t-as="months">
<t t-if="months['date_begin'] == date">
<li class="active">
<a t-ignore="True" t-attf-href="#{blog_url(date_begin=False, date_end=False)}"><t t-esc="months['create_date']"/><span class="pull-right badge" t-esc="months['create_date_count']"/></a>
</li>
</t>
<t t-if="months['date_begin'] != date">
<li>
<a t-ignore="True" t-attf-href="#{blog_url(date_begin=months['date_begin'], date_end=months['date_end'])}"><t t-esc="months['create_date']"/><span class="pull-right badge" t-esc="months['create_date_count']"/></a>
</li>
<t t-foreach="nav_list" t-as="year">
<li t-esc="year"/>
<t t-foreach="nav_list[year]" t-as="months">
<t t-if="months['date_begin'] == date">
<li class="active">
<a t-ignore="True" t-attf-href="#{blog_url(date_begin=False, date_end=False)}"><t t-esc="months['month']"/><span class="pull-right badge" t-esc="months['create_date_count']"/></a>
</li>
</t>
<t t-else="1">
<li>
<a t-ignore="True" t-attf-href="#{blog_url(date_begin=months['date_begin'], date_end=months['date_end'])}"><t t-esc="months['month']"/><span class="pull-right badge" t-esc="months['create_date_count']"/></a>
</li>
</t>
</t>
</t>
</ul>
@@ -395,23 +435,20 @@
<p>
Write a small text here for when <b>new visitors</b> find your website
through your <b>blog entries</b>, referenced in Google.
</p>
<div>
<a href="/page/website.contactus"><button class="btn btn-primary">Contact us</button></a>
</div>
</p>
</section>
</xpath>
</template>
<!-- Option:Right Column: follow us -->
<template id="opt_blog_rc_follow_us" name="Follow us" priority="4"
<template id="opt_blog_rc_follow_us" name="Subscription" priority="4"
inherit_id="website_blog.index_right" active="True" customize_show="True">
<xpath expr="//div[@id='main_column']" position="attributes">
<attribute name="class">col-sm-8</attribute>
</xpath>
<xpath expr="//div[@id='blog_right_column']" position="inside">
<section class="mt32">
<h4>Follow us<small t-if="blog">: <t t-esc="blog.name"/></small></h4>
<h4>Stay updated :</h4>
<t t-if="blog">
<t t-call="website_mail.follow">
<t t-set="email" t-value="user_id.email"/>