summaryrefslogtreecommitdiff
path: root/addons/website_blog/views
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/website_blog/views
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/website_blog/views')
-rw-r--r--addons/website_blog/views/snippets/s_latest_posts.xml148
-rw-r--r--addons/website_blog/views/snippets/snippets.xml37
-rw-r--r--addons/website_blog/views/website_blog_components.xml282
-rw-r--r--addons/website_blog/views/website_blog_posts_loop.xml235
-rw-r--r--addons/website_blog/views/website_blog_templates.xml530
-rw-r--r--addons/website_blog/views/website_blog_views.xml274
6 files changed, 1506 insertions, 0 deletions
diff --git a/addons/website_blog/views/snippets/s_latest_posts.xml b/addons/website_blog/views/snippets/s_latest_posts.xml
new file mode 100644
index 00000000..01628c02
--- /dev/null
+++ b/addons/website_blog/views/snippets/s_latest_posts.xml
@@ -0,0 +1,148 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+<!-- Snippet -->
+<template id="s_latest_posts" name="Blog Posts">
+ <section class="s_latest_posts pt16 pb16" data-vcss="001">
+ <div class="container">
+ <div class="row s_col_no_bgcolor s_nb_column_fixed js_get_posts s_latest_posts_big_picture s_latest_posts_effect_marley"
+ data-loading="true"
+ data-template="website_blog.s_latest_posts_big_picture_template"
+ data-filter-by-blog-id="0"
+ data-order="published_date desc"/>
+ </div>
+ </section>
+</template>
+
+<!-- Load-time templates (rendered in JS on page load) -->
+<!-- List layout -->
+<template id="s_latest_posts_list_template">
+ <div t-foreach="posts" t-as="p" class="d-flex col-12 mt-3 s_latest_posts_post">
+ <a class="s_latest_posts_post_cover flex-grow-0 flex-shrink-0 align-self-baseline position-relative" t-attf-href="/blog/#{p.blog_id.id}/#{p.id}" t-att-title="'Read' + p.name">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="p"/>
+ <t t-set="additionnal_classes" t-value="'w-100 h-100 bg-200 position-absolute'"/>
+
+ <div class="s_latest_posts_loader d-flex align-items-center justify-content-center bg-200">
+ <div class="d-none rounded-circle bg-black-25 w-25 h-25"/>
+ </div>
+ </t>
+ </a>
+ <div class="pl-2">
+ <a class="" t-att-title="'Read' + p.name" t-attf-href="/blog/#{p.blog_id.id}/#{p.id}">
+ <div class="s_latest_posts_post_title mb-1" t-field="p.name"/>
+ </a>
+ <div class="s_latest_posts_post_subtitle mb-1 d-none d-sm-block" t-field="p.subtitle"/>
+ </div>
+ </div>
+</template>
+<!-- Big picture layout -->
+<template id="s_latest_posts_big_picture_template">
+ <figure t-foreach="posts" t-as="p" class="col-md-6 col-lg-4 my-3 s_latest_posts_post">
+ <a class="s_latest_posts_post_cover position-relative d-flex flex-column shadow-sm overflow-hidden rounded text-decoration-none" t-attf-href="/blog/#{p.blog_id.id}/#{p.id}">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="p"/>
+ <t t-set="use_filters" t-value="True"/>
+ <t t-set="additionnal_classes" t-value="'h-100 w-100 bg-600 position-absolute'"/>
+ </t>
+
+ <figcaption class="text-center w-100 h-100 px-3 d-flex flex-column flex-grow-1">
+ <div class="s_latest_posts_post_title text-white" t-field="p.name"/>
+ <div class="s_latest_posts_post_subtitle text-white" t-field="p.subtitle"/>
+ </figcaption>
+
+ <div class="s_latest_posts_loader d-flex align-items-center justify-content-center bg-200">
+ <div class="d-none rounded-circle bg-black-25 w-25 h-25"/>
+ </div>
+ </a>
+ </figure>
+</template>
+<!-- Horizontal layout -->
+<template id="s_latest_posts_horizontal_template">
+ <figure t-foreach="posts" t-as="p" class="post s_latest_posts_post col-md-6 col-lg-4">
+ <figcaption>
+ <h4 class="mb0"><a t-att-href="'/blog/%s/%s' % (p.blog_id.id, p.id)"><t t-esc="p.name"/></a></h4>
+ <h5 class="mt0 mb4" t-field="p.post_date" t-options='{"format": "dd/MM"}' />
+ </figcaption>
+ <a t-att-href="'/blog/%s/%s' % (p.blog_id.id, p.id)">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="p"/>
+ <t t-set="additionnal_classes" t-value="'loading_container thumb'"/>
+ </t>
+ </a>
+ </figure>
+</template>
+<!-- Card layout -->
+<template id="s_latest_posts_card_template">
+ <div t-foreach="posts" t-as="p" class="col-md-6 col-lg-4 s_latest_posts_post pb32">
+ <div class="card">
+ <a class="s_latest_posts_post_cover" t-att-href="'/blog/%s/%s' % (p.blog_id.id, p.id)">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="p"/>
+ <t t-set="additionnal_classes" t-value="'loading_container thumb'"/>
+ </t>
+ </a>
+ <div class="card-body">
+ <a t-att-href="'/blog/%s/%s' % (p.blog_id.id, p.id)"><h4 class="mb-0"><t t-esc="p.name"/></h4></a>
+ </div>
+ <div class="card-footer d-flex justify-content-between">
+ <span class="text-muted mb-0" t-field="p.post_date" t-options='{"format": "MMM d, yyyy"}' />
+ <span class="text-muted mb-0">In <a class="font-weight-bold" t-esc="p.blog_id.name" t-att-href="'/blog/%s' % (p.blog_id.id)" /></span>
+ </div>
+ </div>
+ </div>
+</template>
+
+<!-- Options -->
+<template id="s_latest_posts_options" inherit_id="website.snippet_options">
+ <xpath expr="//div[@data-js='Box']" position="before">
+ <div data-js="js_get_posts_selectBlog" data-selector=".s_latest_posts" data-target=".js_get_posts" data-no-check="true">
+ <we-select string="Choose a blog" data-no-preview="true" name="blog_selection" data-attribute-name="filterByBlogId">
+ <we-button data-select-data-attribute="0">All blogs</we-button>
+ <!-- the blog list will be generated in js -->
+ </we-select>
+ <we-select string="Posts" data-no-preview="true" data-attribute-name="order" class="o_we_inline">
+ <we-button data-select-data-attribute="published_date desc">Latest</we-button>
+ <we-button data-select-data-attribute="visits desc">Most viewed</we-button>
+ </we-select>
+ <we-select string="Layout" data-attribute-name="template">
+ <we-button data-select-data-attribute="website_blog.s_latest_posts_list_template" data-select-class="s_latest_posts_list">List</we-button>
+ <we-button data-select-data-attribute="website_blog.s_latest_posts_big_picture_template"
+ data-select-class="s_latest_posts_big_picture"
+ data-name="big_picture_opt">Big Pictures</we-button>
+ <we-button data-select-data-attribute="website_blog.s_latest_posts_horizontal_template"
+ data-select-class="s_latest_posts_horizontal">Horizontal</we-button>
+ <we-button data-select-data-attribute="website_blog.s_latest_posts_card_template"
+ data-select-class="s_latest_posts_card">Cards</we-button>
+ </we-select>
+ <we-select string="Hover effect" data-no-widget-refresh="true" data-dependencies="big_picture_opt" class="o_we_inline">
+ <we-button data-select-class="">None</we-button>
+ <we-button data-select-class="s_latest_posts_effect_marley">Marley</we-button>
+ <we-button data-select-class="s_latest_posts_effect_dexter">Dexter</we-button>
+ <we-button data-select-class="s_latest_posts_effect_chico">Silly-Chico</we-button>
+ </we-select>
+ </div>
+ </xpath>
+ <xpath expr="//div[@data-js='layout_column']" position="attributes">
+ <attribute name="data-exclude" add=".s_latest_posts, .s_latest_posts_big_picture" separator=","/>
+ </xpath>
+</template>
+
+<!-- Assets -->
+<template id="assets_snippet_s_latest_posts_css_000" inherit_id="website.assets_frontend" active="False">
+ <xpath expr="//link[last()]" position="after">
+ <link rel="stylesheet" type="text/scss" href="/website_blog/static/src/snippets/s_latest_posts/000.scss"/>
+ </xpath>
+</template>
+<template id="assets_snippet_s_latest_posts_css_001" inherit_id="website.assets_frontend">
+ <xpath expr="//link[last()]" position="after">
+ <link rel="stylesheet" type="text/scss" href="/website_blog/static/src/snippets/s_latest_posts/001.scss"/>
+ </xpath>
+</template>
+
+<template id="assets_snippet_s_latest_posts_js_000" inherit_id="website.assets_frontend">
+ <xpath expr="//script[last()]" position="after">
+ <script type="text/javascript" src="/website_blog/static/src/snippets/s_latest_posts/000.js"/>
+ </xpath>
+</template>
+
+</odoo>
diff --git a/addons/website_blog/views/snippets/snippets.xml b/addons/website_blog/views/snippets/snippets.xml
new file mode 100644
index 00000000..d33581c3
--- /dev/null
+++ b/addons/website_blog/views/snippets/snippets.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+<template id="website_blog" inherit_id="website.snippets" name="Snippet Blog">
+ <xpath expr="//t[@id='blog_posts_hook']" position="replace">
+ <t t-snippet="website_blog.s_latest_posts" t-thumbnail="/website_blog/static/src/img/s_latest_posts.svg"/>
+ </xpath>
+</template>
+
+<template id="snippet_options" inherit_id="website.snippet_options" name="Blog snippet options">
+ <xpath expr="." position="inside">
+ <div data-js="BlogPostTagSelection" data-selector=".o_wblog_post_page_cover" data-target="#o_wblog_post_name">
+ <we-row string="Tag List">
+ <we-button data-name="edit_tags_opt" class="fa fa-pencil" title="Edit" data-edit-tag-list="" data-no-preview="true"/>
+ <we-button data-name="save_tags_opt" class="fa fa-save" title="Save" data-save-tag-list="" data-no-preview="true"/>
+ </we-row>
+
+ <div class="o_wblog_tag_list"/>
+
+ <!-- Addition controls -->
+ <we-select string="Existing" data-name="blog_existing_tag_opt" data-no-preview="true"/>
+ <we-row string="New tag" class="o_wblog_new_tag">
+ <we-input data-name="new_tag_input_opt" data-set-new-tag-name="" placeholder="Name"/>
+ <we-button title="Confirm" data-name="new_tag_button_opt" class="fa fa-plus o_we_text_success" data-confirm-new="" data-no-preview="true"/>
+ </we-row>
+ </div>
+ </xpath>
+ <xpath expr="//*[@data-js='anchor']" position="attributes">
+ <attribute name="data-exclude" add=".o_wblog_post_content_field > :not(div, section)" separator=","/>
+ </xpath>
+
+ <!-- Hides ContainerWidth option for content in blog posts -->
+ <xpath expr="//div[@data-js='ContainerWidth']" position="attributes">
+ <attribute name="data-exclude" add="#o_wblog_post_content *" separator=","/>
+ </xpath>
+</template>
+</odoo>
diff --git a/addons/website_blog/views/website_blog_components.xml b/addons/website_blog/views/website_blog_components.xml
new file mode 100644
index 00000000..b6c69969
--- /dev/null
+++ b/addons/website_blog/views/website_blog_components.xml
@@ -0,0 +1,282 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+
+<!-- ====== Template: Blogs Navbar =========================================
+============================================================================ -->
+<template id="blogs_nav" name="Blogs Bar Template">
+ <nav t-attf-class="navbar navbar-expand-sm navbar-light #{additionnal_classes}" t-if="len(blogs) > 1">
+ <div class="container">
+ <ul class="navbar-nav">
+ <li class="nav-item">
+ <span class="nav-link disabled pl-0">Blogs:</span>
+ </li>
+ <li class="nav-item">
+ <a href="/blog" t-attf-class="nav-link font-weight-bold #{(not blog) and 'active'}">All</a>
+ </li>
+ <li class="nav-item" t-foreach="blogs" t-as="nav_blog">
+ <a t-attf-href="/blog/#{slug(nav_blog)}"
+ t-attf-class="nav-link #{(blog and nav_blog.id == blog.id) and 'active' or ''}">
+ <b t-field="nav_blog.name"/>
+ </a>
+ </li>
+ </ul>
+ <!-- 'Search Box' -->
+ <form id="o_wblog_post_search" class="form-inline w-100 w-md-auto pt-2 pt-md-0 d-md-flex"
+ role="search" t-att-action="blog_url(tag=tag,search=search)" method="get">
+ <t t-call="website.website_search_box">
+ <t t-set="_classes" t-valuef="w-100"/>
+ </t>
+ </form>
+ </div>
+ </nav>
+</template>
+
+<!-- ====== Template: List Tags in inline list ============================
+============================================================================ -->
+<template id="tags_list" name="Tags List">
+ <t t-if="tags">
+ <div t-if="not hide_title and categ_title" class="text-muted mb-1 h6" t-esc="categ_title"/>
+ <t t-foreach="tags" t-as="tag">
+ <t t-if="tag.post_ids">
+ <span t-if="dismissibleBtn and tag.id in active_tag_ids" class="align-items-baseline border d-inline-flex pl-2 rounded mb-2">
+ <i class="fa fa-tag mr-2 text-muted"/>
+ <t t-esc="tag.name"/>
+ <a t-attf-href="#{blog_url(tag=tags_list(active_tag_ids, tag.id))}" class="btn border-0 py-1 post_link" t-att-rel="len(active_tag_ids) and 'nofollow'">&#215;</a>
+ </span>
+ <a t-elif="showInactive" t-attf-href="#{blog_url(tag=tags_list(active_tag_ids, tag.id))}" t-attf-class="badge mb-2 mw-100 text-truncate #{tag.id in active_tag_ids and 'badge-primary' or 'border'} post_link" t-attf-rel="len(active_tag_ids) and 'nofollow'" t-esc="tag.name"/>
+ </t>
+ </t>
+ </t>
+</template>
+
+<!-- ====== Template: Date Selector ========================================
+============================================================================ -->
+<template id="date_selector">
+ <select name="archive" oninput="location = this.value;" class="custom-select">
+ <option t-att-value="blog_url(date_begin=False, date_end=False) if blog else '/blog'"
+ t-att="[('selected' if (not date_begin) else 'unselected' ) , 'true' ]">
+ -- All dates
+ </option>
+
+ <optgroup t-foreach="nav_list" t-as="year" t-attf-label="#{year}">
+ <option t-foreach="nav_list[year]" t-as="months"
+ t-att="[('selected' if date_begin and (months['date_begin'] == date_begin) else 'unselected' ) , 'true' ]"
+ t-attf-value="#{blog_url(date_begin=months['date_begin'], date_end=months['date_end'])}">
+ <t t-esc="months['month']"/>
+ <t t-esc="year"/>
+ </option>
+ </optgroup>
+ </select>
+</template>
+
+<!-- ====== Template: Post Author ==========================================
+============================================================================ -->
+<template id="post_author">
+ <div t-attf-class="o_not_editable align-items-center position-relative #{additionnal_classes or ''}">
+ <div t-if="blog_post.author_avatar"
+ t-field="blog_post.author_avatar"
+ style="line-height:1"
+ t-options='{"widget": "image", "class": "rounded-circle " + "o_wblog_author_avatar mr-1" if hide_date else "o_wblog_author_avatar_date mr-2"}' />
+ <div t-att-class="not hide_date and 'small font-weight-bold'" style="line-height:1">
+ <span t-if="editable" t-field="blog_post.author_id" t-options='{ "widget": "contact", "fields": ["name"]}'/>
+ <span t-else="" t-esc="blog_post.author_name"/>
+ <small t-if="not hide_date" t-field="blog_post.post_date" t-options='{"format": "d MMMM, yyyy"}'/>
+ </div>
+ </div>
+</template>
+
+<!-- ====== Template: Post Breadcrumbs =====================================
+============================================================================ -->
+<template id="post_breadcrumbs">
+ <nav aria-label="breadcrumb" t-attf-class="breadcrumb flex-nowrap py-0 px-0 css_editable_mode_hidden #{additionnal_classes or ''}">
+ <li t-if="len(blogs) &gt; 1" class="breadcrumb-item"><a href="/blog">All Blogs</a></li>
+ <li class="breadcrumb-item">
+ <a t-attf-href="#{blog_url(tag=None, date_begin=None, date_end=None)}" t-esc="blog.name"/>
+ </li>
+ <li class="breadcrumb-item text-truncate active"><span t-esc="blog_post.name"/></li>
+ </nav>
+</template>
+
+<!-- ====== Template: Sidebar Blog ==========================================
+Display sidebar in 'All blogs'/single blog pages.
+
+Options:
+# opt_sidebar_blog_index_follow_us : Display follow-us links
+# opt_sidebar_blog_index_archives : Display a <select> input with post by month
+# opt_sidebar_blog_index_tags: Display tags cloud
+============================================================================ -->
+<template id="sidebar_blog_index" name="Sidebar - Blog page">
+ <div id="o_wblog_sidebar" class="w-100">
+ <div class="oe_structure" id="oe_structure_blog_sidebar_index_1"/>
+ <div class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-2 mb-4 border-bottom font-weight-bold">About us</h6>
+ <div>
+ <p>Write a small text here to describe your blog or company.</p>
+ </div>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_sidebar_index_2"/>
+ </div>
+</template>
+
+<!-- (Option) Sidebar Blog: Follow Us -->
+<template id="opt_sidebar_blog_index_follow_us" name="Follow Us" priority="1" inherit_id="website_blog.sidebar_blog_index" active="True" customize_show="True">
+ <xpath expr="//div[@id='o_wblog_sidebar']" position="inside">
+ <div class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-2 mb-4 border-bottom font-weight-bold">Follow Us</h6>
+ <div class="o_wblog_social_links d-flex flex-wrap mx-n1">
+ <t t-set="classes" t-translation="off">bg-100 border mx-1 mb-2 rounded-circle d-flex align-items-center justify-content-center text-decoration-none</t>
+ <a t-if="website.social_facebook" t-att-href="website.social_facebook" aria-label="Facebook" title="Facebook" t-att-class="classes"><i class="fa fa-facebook-square text-facebook"/></a>
+ <a t-if="website.social_twitter" t-att-href="website.social_twitter" t-att-class="classes"><i class="fa fa-twitter text-twitter" aria-label="Twitter" title="Twitter"/></a>
+ <a t-if="website.social_linkedin" t-att-href="website.social_linkedin" t-att-class="classes"><i class="fa fa-linkedin text-linkedin" aria-label="LinkedIn" title="LinkedIn"/></a>
+ <a t-if="website.social_youtube" t-att-href="website.social_youtube" t-att-class="classes"><i class="fa fa-youtube-play text-youtube" aria-label="Youtube" title="Youtube"/></a>
+ <a t-if="website.social_github" t-att-href="website.social_github" t-att-class="classes"><i class="fa fa-github text-github" aria-label="Github" title="Github"/></a>
+ <a t-if="website.social_instagram" t-att-href="website.social_instagram" t-att-class="classes"><i class="fa fa-instagram text-instagram" aria-label="Instagram" title="Instagram"/></a>
+ <a t-if="blog" t-att-href="'/blog/%s/feed' % (blog.id)" t-att-class="classes"><i class="fa fa-rss-square" aria-label="RSS" title="RSS"/></a>
+ </div>
+ <t t-call="website_mail.follow" t-if="blog">
+ <t t-set="email" t-value="user_id.email"/>
+ <t t-set="object" t-value="blog"/>
+ <t t-set="div_class" t-value="'pt-2'"/>
+ </t>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_sidebar_index_3"/>
+ </xpath>
+</template>
+
+<!-- (Option) Sidebar Blog: Archives -->
+<template id="opt_sidebar_blog_index_archives" name="Archives" priority="2" inherit_id="website_blog.sidebar_blog_index" active="True" customize_show="True">
+ <xpath expr="//div[@id='o_wblog_sidebar']" position="inside">
+ <div class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-2 mb-4 border-bottom font-weight-bold">Archives</h6>
+
+ <t t-call="website_blog.date_selector"/>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_sidebar_index_4"/>
+ </xpath>
+</template>
+
+<!-- (Option) Sidebar Blog: Show tags -->
+<template id="opt_sidebar_blog_index_tags" name="Tags List" priority="3" inherit_id="website_blog.sidebar_blog_index" active="True" customize_show="True">
+ <xpath expr="//div[@id='o_wblog_sidebar']" position="inside">
+
+ <div t-if="other_tags or tag_category" class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-2 mb-4 border-bottom font-weight-bold">Tags</h6>
+ <div class="h5">
+ <t t-foreach="tag_category" t-as="nav_tag_category">
+ <t t-call="website_blog.tags_list">
+ <t t-set='categ_title' t-value="nav_tag_category.name"/>
+ <t t-set='tags' t-value='nav_tag_category.tag_ids' />
+ <t t-set="showInactive" t-value="True"/>
+ </t>
+ </t>
+ <t t-call="website_blog.tags_list">
+ <t t-set='hide_title' t-value='not len(tag_category)' />
+ <t t-set='categ_title'>Others</t>
+ <t t-set='tags' t-value='other_tags'/>
+ <t t-set="showInactive" t-value="True"/>
+ </t>
+ </div>
+ </div>
+
+ <div t-else="" groups="website.group_website_designer" class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-2 mb-4 border-bottom font-weight-bold">Tags</h6>
+ <em t-ignore="True" class="text-muted">No tags defined yet.</em>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_sidebar_index_5"/>
+ </xpath>
+</template>
+
+
+<!-- ====== Blog Post Sidebar ==================================================
+Display a sidebar beside the post content.
+============================================================================ -->
+<template id="blog_post_sidebar" name="Sidebar - Blog Post">
+ <div id="o_wblog_post_sidebar">
+ <div class="oe_structure" id="oe_structure_blog_post_sidebar_1"/>
+ </div>
+</template>
+
+
+<!-- (Option) Post Sidebar: Author avatar -->
+<template id="opt_blog_post_author_avatar_display" name="Author" inherit_id="website_blog.blog_post_sidebar" active="True" customize_show="True" priority="1">
+ <xpath expr="//div[@id='o_wblog_post_sidebar']" position="inside">
+ <div class="o_wblog_sidebar_block pb-5">
+ <t t-call="website_blog.post_author">
+ <t t-set="additionnal_classes" t-value="'h5 d-flex align-items-center'"/>
+ </t>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_post_sidebar_2"/>
+ </xpath>
+</template>
+
+<!-- (Option) Post Sidebar: Share Links Display -->
+<template id="opt_blog_post_share_links_display" name="Share Links" inherit_id="website_blog.blog_post_sidebar" active="True" customize_show="True" priority="2">
+ <xpath expr="//div[@id='o_wblog_post_sidebar']" position="inside">
+ <div class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-3 mb-4 border-bottom font-weight-bold">Share this post</h6>
+
+ <div class="o_wblog_social_links d-flex flex-wrap mx-n1">
+ <t t-set="classes" t-translation="off">bg-100 border mx-1 mb-2 rounded-circle d-flex align-items-center justify-content-center text-decoration-none</t>
+ <a href="#" aria-label="Facebook" title="Share on Facebook" t-attf-class="o_facebook #{classes}"><i class="fa fa-facebook-square text-facebook"/></a>
+ <a href="#" aria-label="Twitter" title="Share on Twitter" t-attf-class="o_twitter #{classes}"><i class="fa fa-twitter text-twitter" aria-label="Twitter" title="Twitter"/></a>
+ <a href="#" aria-label="LinkedIn" title="Share on LinkedIn" t-attf-class="o_linkedin #{classes}"><i class="fa fa-linkedin text-linkedin" aria-label="LinkedIn" title="LinkedIn"/></a>
+ </div>
+ </div>
+
+ <div class="oe_structure" id="oe_structure_blog_post_sidebar_3"/>
+ </xpath>
+</template>
+
+<!-- (Option) Post Sidebar: display tags -->
+<template id="opt_blog_post_tags_display" name="Tags" inherit_id="website_blog.blog_post_sidebar" active="True" customize_show="True" priority="3">
+ <xpath expr="//div[@id='o_wblog_post_sidebar']" position="inside">
+ <div class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-3 mb-4 border-bottom font-weight-bold">Tags</h6>
+ <t t-if="blog_post.tag_ids">
+ <div class="h5">
+ <t t-foreach="blog_post.tag_ids" t-as="one_tag">
+ <a class="badge border post_link" t-attf-href="#{blog_url(tag=one_tag.id)}" t-esc="one_tag.name"/>
+ </t>
+ </div>
+ </t>
+ <t t-else="">
+ <div class="mb-4 bg-100 py-2 px-3 border" groups="website.group_website_designer">
+ <h6 class="text-muted"><em>No tags defined</em></h6>
+ <a role="menuitem" t-attf-href="/web#view_type=form&amp;model=#{main_object._name}&amp;id=#{main_object.id}&amp;action=#{action}&amp;menu_id=#{menu or main_object.env.ref('website.menu_website_configuration').id}"
+ title='Edit in backend' id="edit-in-backend">Add some</a>
+ </div>
+ </t>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_post_sidebar_4"/>
+ </xpath>
+</template>
+
+<!-- (Option) Post Sidebar: display Blogs list -->
+<template id="opt_blog_post_blogs_display" name="Blogs List" inherit_id="website_blog.blog_post_sidebar" active="True" customize_show="True" priority="4">
+ <xpath expr="//div[@id='o_wblog_post_sidebar']" position="inside">
+ <div t-if="len(blogs) > 1" class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-3 mb-4 border-bottom font-weight-bold">Our blogs</h6>
+ <ul class="list-unstyled">
+ <li t-foreach="blogs" t-as="nav_blog" class="mb-2">
+ <a t-attf-href="#{blog_url(blog=nav_blog, tag=False, date_begin=False, date_end=False)}"><b t-field="nav_blog.name"/></a>
+ </li>
+ </ul>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_post_sidebar_5"/>
+ </xpath>
+</template>
+
+<!-- (Option) Post Sidebar: display Archive -->
+<template id="opt_blog_post_archive_display" name="Archive" inherit_id="website_blog.blog_post_sidebar" active="True" customize_show="True" priority="5">
+ <xpath expr="//div[@id='o_wblog_post_sidebar']" position="inside">
+ <div class="o_wblog_sidebar_block pb-5">
+ <h6 class="text-uppercase pb-3 mb-4 border-bottom font-weight-bold">Archive</h6>
+
+ <t t-call="website_blog.date_selector"/>
+ </div>
+ <div class="oe_structure" id="oe_structure_blog_post_sidebar_6"/>
+ </xpath>
+</template>
+
+</odoo>
diff --git a/addons/website_blog/views/website_blog_posts_loop.xml b/addons/website_blog/views/website_blog_posts_loop.xml
new file mode 100644
index 00000000..ec4872ed
--- /dev/null
+++ b/addons/website_blog/views/website_blog_posts_loop.xml
@@ -0,0 +1,235 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+<!-- ====== Template: Posts Loop ==========================================
+Loop through post and call sub-templates (tags, cover..) in different position
+according to the enabled options.
+============================================================================ -->
+<template id="posts_loop" name="Posts List">
+ <div id="o_wblog_posts_loop" t-att-class="'o_wblog_list_view' if opt_blog_list_view else ''">
+
+ <!-- Allow to filter post by published state. Visible only in edit-mode
+ and if both published/unpublished number is > 0 -->
+ <t t-if="state_info" t-set="state" t-value="state_info['state']"/>
+
+ <!-- Check for active options -->
+ <t t-set="opt_posts_loop_show_cover" t-value="is_view_active('website_blog.opt_posts_loop_show_cover')"/>
+
+ <div groups="website.group_website_designer" t-if="state_info and (state_info['published'] > 0 and state_info['unpublished'] > 0)">
+ <div class="bg-200 py-2 mb-4 alert alert-dismissable">
+ <button type="button" class="close" data-dismiss="alert" aria-label="Close">&#215;</button>
+ <span class="mr-1">Show:</span>
+ <div class="btn-group btn-group-sm">
+ <a t-attf-class="btn #{state == 'published' and 'btn-success' or 'btn-default bg-white border'}"
+ t-attf-href="#{state == 'published' and blog_url(state='') or blog_url(state='published')}">
+ <i t-attf-class="fa mr-1 #{state == 'published' and 'fa-check-square-o' or 'fa-square-o'}"/>
+ Published (<t t-esc="state_info['published']" />)
+ </a>
+ <a t-attf-class="btn #{state == 'unpublished' and 'btn-success' or 'btn-default bg-white border'}"
+ t-attf-href="#{state == 'unpublished' and blog_url(state='') or blog_url(state='unpublished')}">
+ <i t-attf-class="fa mr-1 #{state == 'unpublished' and 'fa-check-square-o' or 'fa-square-o'}"/>
+ Unpublished (<t t-esc="state_info['unpublished']" />)
+ </a>
+ </div>
+ <div class="pt-1 font-italic small">This box will not be visible to your visitors</div>
+ </div>
+ </div>
+
+ <div t-attf-class="row #{posts and not opt_blog_readable and 'mx-n2'}">
+ <!-- Filters -->
+ <div t-if="tag or date_begin or search" class="col-12 mb-3">
+ <div t-if="posts" class="h4 mb-3">
+ <t t-esc="len(posts)"/>
+ <t t-if="len(posts) &lt; 2">Article</t>
+ <t t-else="">Articles</t>
+ </div>
+ <span t-if="search" class="align-items-baseline border d-inline-flex pl-2 rounded mb-2">
+ <i class="fa fa-search mr-2 text-muted"/>
+ <t t-esc="search"/>
+ <a t-att-href="blog_url(search=False, tag=tag)" class="btn border-0 py-1 post_link">&#215;</a>
+ </span>
+ <t t-if="tag">
+ <!-- Show active tags with a category set -->
+ <t t-foreach="tag_category" t-as="nav_tag_category">
+ <t t-call="website_blog.tags_list">
+ <t t-set='tags' t-value='nav_tag_category.tag_ids' />
+ <t t-set='dismissibleBtn' t-value="True"/>
+ </t>
+ </t>
+
+ <!-- Show active tags without a category set -->
+ <t t-call="website_blog.tags_list">
+ <t t-set='tags' t-value='other_tags'/>
+ <t t-set='dismissibleBtn' t-value="True"/>
+ </t>
+ </t>
+ <span t-if="date_begin" class="align-items-baseline border d-inline-flex pl-2 rounded mb-2">
+ <i class="fa fa-calendar-o mr-2 text-muted"/>
+ <t t-esc="date_begin" t-options="{'widget': 'date', 'format': 'MMM yyyy'}"></t>
+ <a t-attf-href="#{blog_url(date_begin=False, date_end=False)}" class="btn border-0 py-1">&#215;</a>
+ </span>
+ <hr class="mt-2"/>
+ </div>
+
+ <!-- No blog post yet -->
+ <div t-if="not posts" class="col">
+ <t t-set="no_results_str">No results for "%s".</t>
+ <h2 t-if="search" t-esc="no_results_str % search" class="font-weight-bold"/>
+ <h2 t-else="">No blog post yet.</h2>
+ <div class="alert alert-info" groups="website.group_website_designer">
+ Click on "<b>New</b>" in the top-right corner to write your first blog post.
+ </div>
+ </div>
+
+ <!-- Posts -->
+
+ <!-- Define 'colWidth' qWeb variable, to be assigned later.
+ Adjust accordingly if sidebar and/or readability modes are active. -->
+ <t t-if="not opt_blog_list_view">
+ <t t-if="opt_blog_readable">
+ <t t-if="opt_blog_sidebar_show" t-set="colWidth" t-value="'col-md-6'"/>
+ <t t-else="" t-set="colWidth" t-value="'col-md-6 col-xl-4'"/>
+ </t>
+ <t t-else="">
+ <t t-if="opt_blog_sidebar_show" t-set="colWidth" t-value="'px-2 col-md-6 col-xl-4'"/>
+ <t t-else="" t-set="colWidth" t-value="'px-2 col-sm-6 col-lg-4 col-xl-3'"/>
+ </t>
+ </t>
+ <!-- Loop through posts: exclude the first one if already displayed as top banner -->
+ <t t-foreach="posts" t-as="blog_post">
+ <!-- Assign 'colWidth': 'col-12' is default for List-View and mobile -->
+ <div t-attf-class="pb-4 col-12 #{colWidth}">
+ <article t-attf-class="o_wblog_post position-relative #{'card h-100' if opt_blog_cards_design else ''}" name="blog_post">
+ <!-- List-View Design -->
+ <t t-if="opt_blog_list_view">
+ <div t-att-class="opt_blog_cards_design and 'card-body py-3'">
+ <t t-call="website_blog.post_heading"/>
+ </div>
+ <div t-if="not opt_blog_cards_design" class="py-2">
+ <t t-call="website_blog.post_info"></t>
+ </div>
+ <div t-if="opt_posts_loop_show_cover">
+ <t t-call="website_blog.post_cover_image"/>
+ </div>
+ <div t-if="is_view_active('website_blog.opt_posts_loop_show_teaser')" t-att-class="opt_blog_cards_design and 'card-body pt-0'">
+ <t t-call="website_blog.post_teaser"/>
+ </div>
+ <div t-if="opt_blog_cards_design" t-attf-class="opt_blog_cards_design and 'card-body pt-0 pb-2'}">
+ <t t-call="website_blog.post_info"></t>
+ </div>
+ <div t-else="" class="mt-3">
+ <a t-attf-href="/blog/#{slug(blog_post.blog_id)}/#{slug(blog_post)}" class="btn btn-primary">
+ Read more <i class="fa fa-chevron-right ml-2"/>
+ </a>
+ </div>
+ </t>
+ <!-- Grid-View Design -->
+ <t t-if="not opt_blog_list_view">
+ <t t-if="opt_posts_loop_show_cover" t-call="website_blog.post_cover_image"/>
+ <div t-att-class="opt_blog_cards_design and 'card-body px-2 py-0 mb-2'">
+ <t t-call="website_blog.post_heading"/>
+ <div t-if="is_view_active('website_blog.opt_posts_loop_show_teaser')">
+ <t t-call="website_blog.post_teaser"/>
+ </div>
+ </div>
+ <div t-attf-class="o_wblog_normalize_font #{'card-footer px-2 pb-2' if opt_blog_cards_design else 'pr-2 pb-2'}">
+ <t t-call="website_blog.post_info"></t>
+ </div>
+ </t>
+ <!-- Add 'unpublished' badge -->
+ <span t-if="not blog_post.website_published" class="bg-danger small py-1 px-2 position-absolute o_not_editable" style="top:0; right:0">unpublished</span>
+ </article>
+ </div>
+ <!-- List-View Design, add <hr> after post -->
+ <div t-if="opt_blog_list_view and not blog_post_last" class="col-12 mt-2 mb-5 px-2"><hr/></div>
+ </t>
+ </div>
+ </div>
+</template>
+
+
+<!-- ====== Sub-Template: Posts list : Posts Heading =================== -->
+<template id="post_heading">
+ <a t-attf-href="/blog/#{slug(blog_post.blog_id)}/#{slug(blog_post)}"
+ t-field="blog_post.name"
+ t-attf-class="d-block text-reset text-decoration-none o_blog_post_title my-0 #{'h3' if opt_blog_list_view else ('h5' if opt_blog_readable else 'h6')}">
+ Untitled Post
+ </a>
+
+ <div t-if="not opt_posts_loop_show_cover and is_view_active('website_blog.opt_posts_loop_show_author')" class="text-muted small mt-2">
+ by <span t-field="blog_post.author_id"/>
+ </div>
+</template>
+
+<!-- ====== Sub-Template: Posts list : Posts Info ======================= -->
+<template id="post_info">
+ <div class="d-flex small flex-wrap mb-1 w-100">
+ <div t-attf-class="d-flex flex-wrap align-items-center justify-content-between mx-n2 #{opt_blog_list_view and 'flex-grow-0 w-auto mw-100' or 'flex-grow-1' }">
+ <time t-field="blog_post.post_date" class="text-nowrap font-weight-bold px-2" t-options='{"format": "MMM d, yyyy"}'/>
+ <div t-if="is_view_active('website_blog.opt_posts_loop_show_stats')" class="px-2">
+ <b class="text-nowrap" title="Comments"><i class="fa fa-comment text-muted mr-1"/><t t-esc="len(blog_post.message_ids)"/></b>
+ <b class="text-nowrap pl-2" title="Views"><i class="fa fa-binoculars text-muted mr-1"/><t t-esc="blog_post.visits"/></b>
+ </div>
+ <b t-if="posts_list_show_parent_blog" class="text-nowrap text-truncate px-2">
+ <i class="fa fa-folder-open text-muted"/>
+ <a t-attf-href="/blog/#{slug(blog_post.blog_id)}" t-field="blog_post.blog_id"/>
+ </b>
+ </div>
+ </div>
+</template>
+
+<!-- ====== Sub-Template: Posts list : Posts Cover ====================== -->
+<template id="post_cover_image">
+ <t t-if="opt_blog_cards_design and not opt_blog_list_view" t-set="classes" t-value="'card-img-top mb-2'"/>
+ <t t-if="not opt_blog_cards_design and opt_blog_list_view" t-set="classes" t-value="'o_wblog_post_cover_nocard'"/>
+
+ <a t-attf-href="/blog/#{slug(blog_post.blog_id)}/#{slug(blog_post)}"
+ t-attf-class="text-decoration-none d-block #{classes or 'mb-2'}"
+ t-att-style="not blog_post.website_published and 'opacity:0.6;'">
+
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="blog_post"/>
+ <t t-set="additionnal_classes" t-value="'o_list_cover o_not_editable ' + (not opt_blog_cards_design and ' rounded overflow-hidden shadow mb-3' or '')"/>
+
+ <t t-if="is_view_active('website_blog.opt_posts_loop_show_author')" t-call="website_blog.post_author">
+ <t t-set="additionnal_classes" t-value="'o_wblog_post_list_author o_list_cover d-flex text-white w-100 o_not_editable ' + ('p-3 h5 m-0' if opt_blog_list_view else 'px-2 pb-2 pt-3') "/>
+ <t t-set="hide_date" t-value="True"/>
+ </t>
+ </t>
+ </a>
+</template>
+
+<!-- ====== Sub-Template: Posts list : Posts Teaser + Tags ============= -->
+<template id="post_teaser">
+ <a t-attf-href="/blog/#{slug(blog_post.blog_id)}/#{slug(blog_post)}" class="text-reset text-decoration-none">
+ <div t-if="opt_blog_list_view" t-field="blog_post.teaser" class="mt-2 o_wblog_read_text"/>
+ <div t-else="" t-field="blog_post.teaser" t-attf-class="mt-2 #{opt_blog_readable and 'o_wblog_normalize_font'}"/>
+ </a>
+
+ <!-- Tags -->
+ <div t-if="len(blog_post.tag_ids)" class="o_wblog_post_short_tag_section d-flex align-items-center flex-wrap pt-2">
+ <t t-foreach="blog_post.tag_ids" t-as="one_tag">
+ <a t-attf-href="#{blog_url(tag=tags_list(active_tag_ids, one_tag.id))}"
+ t-attf-class="badge mb-2 mr-1 text-truncate #{one_tag.id in active_tag_ids and 'badge-primary' or 'border'} post_link"
+ t-att-rel="len(active_tag_ids) and 'nofollow'"
+ t-esc="one_tag.name"/>
+ </t>
+ </div>
+</template>
+
+
+<!-- ====================== OPTIONS =========================== -->
+<!-- ==================================================================== -->
+<!-- (Option) Posts List: Show Covers -->
+<template id="opt_posts_loop_show_cover" name="Cover" inherit_id="website_blog.posts_loop" active="True" customize_show="True"/>
+
+<!-- (Option) Posts List: Show Author -->
+<template id="opt_posts_loop_show_author" name="Author" inherit_id="website_blog.posts_loop" active="True" customize_show="True"/>
+
+<!-- (Option) Posts List: Show Post Stats -->
+<template id="opt_posts_loop_show_stats" name="Comments/Views Stats" inherit_id="website_blog.posts_loop" active="False" customize_show="True"/>
+
+<!-- (Option) Posts List: Show Post Teaser -->
+<template id="opt_posts_loop_show_teaser" name="Teaser &amp; Tags" inherit_id="website_blog.posts_loop" active="True" customize_show="True"/>
+
+</odoo>
diff --git a/addons/website_blog/views/website_blog_templates.xml b/addons/website_blog/views/website_blog_templates.xml
new file mode 100644
index 00000000..5aee1d5d
--- /dev/null
+++ b/addons/website_blog/views/website_blog_templates.xml
@@ -0,0 +1,530 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+
+<template id="assets_wysiwyg" inherit_id="website.assets_wysiwyg" name="Blog posts snippet options">
+ <xpath expr="." position="inside">
+ <script type="text/javascript" src="/website_blog/static/src/snippets/s_latest_posts/options.js"/>
+ </xpath>
+</template>
+
+<template id="assets_editor" inherit_id="website.assets_editor" name="Blog Editor">
+ <xpath expr="." position="inside">
+ <script type="text/javascript" src="/website_blog/static/src/js/website_blog.editor.js"></script>
+ <script type="text/javascript" src="/website_blog/static/src/js/tours/website_blog.js"></script>
+ </xpath>
+</template>
+
+<template id="assets_frontend" inherit_id="website.assets_frontend" name="Blog Front-end assets">
+ <xpath expr="." position="inside">
+ <link rel="stylesheet" type="text/scss" href="/website_blog/static/src/scss/website_blog.scss"/>
+
+ <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>
+
+<!-- ====== Overall pages layout ===========================================
+============================================================================ -->
+<template id="index" name="Blog Navigation">
+ <t t-call="website.layout">
+ <div id="wrap" class="js_blog website_blog">
+ <t t-raw="0"/>
+
+ <!-- Droppable-area shared across all blog's pages -->
+ <t t-set="oe_structure_blog_footer_description">Visible in all blogs' pages</t>
+ <div class="oe_structure oe_empty"
+ id="oe_structure_blog_footer"
+ t-att-data-editor-sub-message="oe_structure_blog_footer_description"/>
+ </div>
+ </t>
+</template>
+
+<!-- ====== Blog(s) Index : Displaying a list of Blog Posts ===================
+Used by 'All blogs' and 'blog' (to share the same layout options) and to display
+list of filtered posts (by date or tag).
+============================================================================ -->
+<template id="blog_post_short" name="Blog Posts">
+ <t t-call="website_blog.index">
+ <t t-set="head">
+ <link t-if="blog" t-att-href="'/blog/%s/feed' % (blog.id)" type="application/atom+xml" rel="alternate" title="Atom Feed"/>
+ <meta t-if="active_tag_ids" name="robots" t-attf-content="none"/>
+ </t>
+
+ <!-- Check for active options: the stored value may be used in sub-templates too -->
+ <t t-set="opt_blog_cards_design" t-value="is_view_active('website_blog.opt_blog_cards_design')"/>
+ <t t-set="opt_blog_list_view" t-value="is_view_active('website_blog.opt_blog_list_view')"/>
+ <t t-set="opt_blog_readable" t-value="is_view_active('website_blog.opt_blog_readable')"/>
+ <t t-set="opt_blog_sidebar_show" t-value="is_view_active('website_blog.opt_blog_sidebar_show')"/>
+
+ <div id="o_wblog_blog_top">
+ <!-- Selectively display droppable-areas for 'all blogs' or single-blog pages -->
+ <t t-if="not tag and not date_begin">
+ <div id="o_wblog_blog_top_droppable">
+ <t t-if="blog">
+ <t t-set="oe_structure_blog_single_header_description">Edit the '<t t-esc="blog.name"/>' page header.</t>
+ <div t-field="blog.content"
+ class="oe_structure"
+ t-attf-id="oe_structure_blog_single_header_#{blog.id}"
+ t-att-data-editor-sub-message="oe_structure_blog_single_header_description"/>
+ </t>
+ <t t-elif="blogs">
+ <t t-set="oe_structure_blog_all_header_description">Edit the 'All Blogs' page header.</t>
+ <div class="oe_structure"
+ id="oe_structure_blog_all_header"
+ t-att-data-editor-sub-message="oe_structure_blog_all_header_description"/>
+ </t>
+ </div>
+ </t>
+ <t t-else="">
+ <!-- Droppable-area for filtered results (tags or date) -->
+ <t t-set="oe_structure_blog_filtered_header_description">Edit the 'Filter Results' page header.</t>
+ <div class="oe_structure"
+ id="oe_structure_blog_filtered_header"
+ t-att-data-editor-sub-message="oe_structure_blog_filtered_header_description"/>
+ </t>
+ </div>
+
+ <t t-call="website_blog.blogs_nav"/>
+
+ <section id="o_wblog_index_content" t-att-class="opt_blog_cards_design and 'o_wblog_page_cards_bg'">
+ <div class="container py-4">
+ <div t-attf-class="row #{opt_blog_sidebar_show and 'justify-content-between' or 'justify-content-center'}">
+ <div id="o_wblog_posts_loop_container" t-attf-class="col #{'o_container_small mx-0' if opt_blog_list_view else ''}">
+
+ <t t-call="website_blog.posts_loop">
+ <t t-if="not blog" t-set="posts_list_show_parent_blog" t-value="True"/>
+ </t>
+
+ <t t-call="website.pager" >
+ <t t-set="classname" t-valuef="justify-content-center"/>
+ <t t-set="extraLinkClass" t-valuef="post_link"/>
+ </t>
+ </div>
+ </div>
+ </div>
+ </section>
+ </t>
+</template>
+
+<!-- (Option) Blog: Show latest-post as top banner
+ Replace top-banner content with the latest published post
+-->
+<template id="opt_blog_cover_post" name="Top banner - Name / Latest Post" inherit_id="website_blog.blog_post_short" active="True" customize_show="True">
+ <xpath expr="//div[@id='o_wblog_blog_top_droppable']" position="replace">
+ <div t-if="first_post or blog" class="container">
+ <div class="row py-4">
+ <div t-attf-class="mb-3 mb-md-0 #{'col-md-5' if (not opt_blog_list_view and not opt_blog_sidebar_show) else 'col-md-6'}">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="blog or first_post"/>
+ <t t-set="additionnal_classes" t-value="'h-100 py-5 py-md-0 overflow-hidden rounded shadow'"/>
+ </t>
+ </div>
+ <div t-att-class="'col-md-7' if (not opt_blog_list_view and not opt_blog_sidebar_show) else 'col-md-6'">
+ <div class="container position-relative h-100 d-flex flex-column justify-content-around pt-1 pb-2">
+ <div t-attf-class="o_wblog_post_title #{'js_tweet' if opt_blog_post_select_to_tweet else ''} #{'js_comment' if opt_blog_post_select_to_comment else ''}">
+ <t t-if="blog">
+ <span t-field="blog.name" class="h1 d-block" placeholder="Blog's Title"/>
+ <div t-field="blog.subtitle" class="h4" placeholder="Subtitle"/>
+ </t>
+ <t t-else="first_post">
+ <div t-if="not date and not tag" class="h4 mb-3 bg-o-color-3 px-2 rounded-sm d-inline-block mr-auto">Latest</div>
+ <a t-attf-href="/blog/#{slug(first_post.blog_id)}/#{slug(first_post)}"
+ t-field="first_post.name" class="h1 d-block" t-att-data-blog-id="first_post.id" placeholder="Blog Post Title"/>
+ <div t-field="first_post.subtitle" class="h4" placeholder="Subtitle"/>
+
+ <div t-if="not blog" class="d-flex">
+ <div class="small mt-2 mb-3 mr-1">
+ in <i class="fa fa-folder-open text-muted"/> <a t-attf-href="#{blog_url(blog=first_post.blog_id)}" t-field="first_post.blog_id"/>
+ </div>
+ </div>
+ <div t-field="first_post.teaser" class="mb-4 lead" placeholder=""/>
+ <div>
+ <a t-attf-href="/blog/#{slug(first_post.blog_id)}/#{slug(first_post)}" class="btn btn-primary">Read more</a>
+ </div>
+ </t>
+ </div>
+ </div>
+ </div>
+ <div class="col-12 mt-3"> <hr/> </div>
+ </div>
+ </div>
+ </xpath>
+</template>
+
+<!-- (Option) Blog: Show latest-post as top banner : 'Full Width' design -->
+<template id="opt_blog_cover_post_fullwidth_design" name="Full-Width Cover" inherit_id="website_blog.opt_blog_cover_post" active="True" customize_show="True">
+ <xpath expr="//div[hasclass('container')]" position="replace">
+ <t t-if="blog or first_post" t-call="website.record_cover">
+ <t t-set="_record" t-value="blog or first_post"/>
+ <t t-set="use_filters" t-value="True"/>
+ <t t-set="use_text_align" t-value="True"/>
+ <t t-set="additionnal_classes" t-value="'o_wblog_post_page_cover o_record_has_cover cover_auto'"/>
+
+ <div class="container position-relative h-100 d-flex flex-column justify-content-around">
+ <div t-attf-class="o_wblog_post_title #{'js_tweet' if opt_blog_post_select_to_tweet else ''} #{'js_comment' if opt_blog_post_select_to_comment else ''}">
+ <div t-if="not date and not tag and not blog" class="h4 bg-o-color-3 px-2 d-inline-block rounded-sm">Latest</div>
+ <a t-if="not blog and first_post" t-attf-href="/blog/#{slug(first_post.blog_id)}/#{slug(first_post)}" t-att-title="first_post.name" class="text-white text-decoration-none">
+ <div t-field="first_post.name" id="o_wblog_post_name" t-att-data-blog-id="first_post.id" placeholder="Blog Post Title"/>
+ <div t-field="first_post.subtitle" id="o_wblog_post_subtitle" placeholder="Subtitle"/>
+ </a>
+ <span t-elif="blog" t-att-title="blog.name" class="text-white text-decoration-none">
+ <div t-field="blog.name" id="o_wblog_post_name" placeholder="Blog Title"/>
+ <div t-field="blog.subtitle" id="o_wblog_post_subtitle" placeholder="Blog Subtitle"/>
+ </span>
+
+ <div>
+ <span t-if="not blog and blog_post" class="text-white small mt-2 mb-3">
+ in <i class="fa fa-folder-open text-white-75"/><a t-attf-href="#{blog_url(blog=blog_post.blog_id)}" class="text-white" t-field="blog_post.blog_id"/>
+ </span>
+ <span t-else="">&amp;nbsp;</span>
+ </div>
+ </div>
+ </div>
+ </t>
+ </xpath>
+</template>
+
+
+<!-- (Option) Blog: Sidebar : Show -->
+<template id="opt_blog_sidebar_show" name="Show Sidebar" inherit_id="website_blog.blog_post_short" active="False" customize_show="True">
+ <xpath expr="//div[@id='o_wblog_posts_loop_container']" position="after">
+ <div t-if="opt_blog_list_view" class="border-right d-none d-lg-block" style="opacity: 0.5"/>
+ <div t-attf-class="col-12 col-md-3 d-flex #{opt_blog_list_view and 'col-lg-4' or 'ml-lg-5'}">
+ <t t-call="website_blog.sidebar_blog_index"/>
+ </div>
+ </xpath>
+</template>
+
+<!-- (Option) Blog: Posts List: Cards design
+ Wrap posts in a standard bts cards components
+-->
+<template id="opt_blog_cards_design" name="'Cards' Design" inherit_id="website_blog.blog_post_short" active="False" customize_show="True"/>
+
+
+<!-- (Option) Blog: Show Posts in list-view
+ Display post in a list rather than a grid
+-->
+<template id="opt_blog_list_view" name="List View" inherit_id="website_blog.blog_post_short" active="False" customize_show="True"/>
+
+<!-- (Option) Blog: Increase readability
+ Increase font-size, adapt layout
+-->
+<template id="opt_blog_readable" name="Increase Readability" inherit_id="website_blog.blog_post_short" active="True" customize_show="True"/>
+
+
+<!-- ====== Blog Post Complete Layout ==========================================
+============================================================================ -->
+<template id="website_blog.blog_post_complete" name="Blog Post" track="1">
+ <t t-call="website_blog.index">
+
+ <!-- Check for active options: the stored value may be used in sub-templates too -->
+ <t t-set="opt_blog_post_readable" t-value="is_view_active('website_blog.opt_blog_post_readable')"/>
+ <t t-set="opt_blog_post_sidebar" t-value="is_view_active('website_blog.opt_blog_post_sidebar')"/>
+ <t t-set="opt_blog_post_regular_cover" t-value="is_view_active('website_blog.opt_blog_post_regular_cover')"/>
+ <t t-set="opt_blog_post_breadcrumb" t-value="is_view_active('website_blog.opt_blog_post_breadcrumb')"/>
+ <t t-set="opt_blog_post_select_to_tweet" t-value="is_view_active('website_blog.opt_blog_post_select_to_tweet')"/>
+ <t t-set="opt_blog_post_select_to_comment" t-value="is_view_active('website_blog.opt_blog_post_select_to_comment')"/>
+
+ <section id="o_wblog_post_top">
+ <div id="title" class="blog_header" t-ignore="True">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="blog_post"/>
+ <t t-set="snippet_autofocus" t-value="True"/>
+ <t t-set="use_filters" t-value="True"/>
+ <t t-set="use_size" t-value="True"/>
+ <t t-set="display_opt_name">Blog Post Cover</t>
+ <t t-set="additionnal_classes" t-value="'o_wblog_post_page_cover'"/>
+
+
+ <div class="container text-center position-relative h-100 d-flex flex-column flex-grow-1 justify-content-around">
+ <div t-attf-class="o_wblog_post_title #{opt_blog_post_select_to_tweet and 'js_tweet'} #{opt_blog_post_select_to_comment and 'js_comment'}">
+ <div t-field="blog_post.name" id="o_wblog_post_name" class="o_editable_no_shadow" data-oe-expression="blog_post.name" t-att-data-blog-id="blog_post.id" placeholder="Blog Post Title"/>
+ <div t-field="blog_post.subtitle" id="o_wblog_post_subtitle" class="o_editable_no_shadow" placeholder="Subtitle"/>
+ </div>
+ <t t-set="resize_classes" t-value="set(json.loads(_record.cover_properties).get('resize_class', '').split(' '))"/>
+ <a t-if="{'o_full_screen_height', 'o_half_screen_height', 'cover_full', 'cover_mid'}.intersection(resize_classes)"
+ id="o_wblog_post_content_jump" href="#o_wblog_post_main"
+ class="css_editable_mode_hidden justify-content-center align-items-center rounded-circle mx-auto mb-5 text-decoration-none">
+ <i class="fa fa-angle-down fa-3x text-white" aria-label="To blog content" title="To blog content"/>
+ </a>
+ </div>
+ </t>
+ </div>
+ </section>
+
+ <section id="o_wblog_post_main" t-attf-class="container pt-4 pb-5 #{'anim' in request.params and 'o_wblog_post_main_transition'}">
+ <!-- Sidebar-enabled Layout -->
+ <div t-if="opt_blog_post_sidebar" t-attf-class="mx-auto #{opt_blog_post_readable and 'o_wblog_read_with_sidebar'}">
+ <div t-attf-class="d-flex flex-column flex-lg-row #{opt_blog_post_readable and 'justify-content-between'}">
+ <div id="o_wblog_post_content" t-attf-class="#{opt_blog_post_readable and 'o_container_small mx-0 w-100 flex-shrink-0' or 'w-lg-75'}">
+ <t t-call="website_blog.blog_post_content"/>
+ </div>
+ <div id="o_wblog_post_sidebar_col" t-attf-class="pl-lg-5 #{not opt_blog_post_readable and 'flex-grow-1 w-lg-25'}">
+ <t t-call="website_blog.blog_post_sidebar"/>
+ </div>
+ </div>
+ </div>
+
+ <!-- No-Sidebar Layout -->
+ <div t-if="not opt_blog_post_sidebar" t-attf-class="#{opt_blog_post_readable and 'o_container_small'}">
+ <div class="d-flex flex-column flex-lg-row">
+ <div id="o_wblog_post_content" t-attf-class=" #{opt_blog_post_readable and 'o_container_small w-100 flex-shrink-0'}">
+ <t t-call="website_blog.blog_post_content"/>
+ </div>
+ </div>
+ </div>
+ </section>
+ <section id="o_wblog_post_footer"/>
+ </t>
+</template>
+
+<!-- ====== Blog Post Content ==================================================
+============================================================================ -->
+<template id="blog_post_content" name="Blog post content">
+ <t t-if="opt_blog_post_breadcrumb and not opt_blog_post_regular_cover" t-call="website_blog.post_breadcrumbs">
+ <t t-set="additionnal_classes" t-value="'mb-3 bg-transparent'"></t>
+ </t>
+ <div t-field="blog_post.content"
+ data-editor-message="WRITE HERE OR DRAG BUILDING BLOCKS"
+ t-attf-class="o_wblog_post_content_field #{'js_tweet' if opt_blog_post_select_to_tweet else ''} #{'js_comment' if opt_blog_post_select_to_comment else ''} #{'o_wblog_read_text' if opt_blog_post_readable else ''}"/>
+
+ <div t-if="len(blogs) > 1 or len(blog_post.tag_ids) > 0" class="css_editable_mode_hidden text-muted">
+ <div t-if="len(blogs) > 1">in <a t-attf-href="#{blog_url(blog=blog_post.blog_id)}"><b t-field="blog.name"/></a></div>
+ <div t-if="len(blog_post.tag_ids) > 0">#
+ <t t-foreach="blog_post.tag_ids" t-as="one_tag">
+ <a class="badge border mr-1 post_link" t-attf-href="#{blog_url(tag=slug(one_tag), date_begin=False, date_end=False)}" t-esc="one_tag.name"/>
+ </t>
+ </div>
+ </div>
+</template>
+
+
+<!-- (Option) Post: Increase readability
+ Increase font-size, adapt content width
+-->
+<template id="opt_blog_post_readable" name="Increase Readability" inherit_id="website_blog.blog_post_complete" active="True" customize_show="True"/>
+
+<!-- (Option) Post: Show Sidebar
+ Show sidebar beside the post content
+-->
+<template id="opt_blog_post_sidebar" name="Show Sidebar" inherit_id="website_blog.blog_post_complete" active="False" customize_show="True"/>
+
+<!-- (Option) Post: Regular Cover
+ Use 'regular cover' design rather than the fullwidth one
+-->
+<template id="opt_blog_post_regular_cover" name="'Regular' Cover" inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
+ <xpath expr="//div[@id='title']" position="replace">
+ <div class="container">
+ <t t-set="readableClass" t-if="opt_blog_post_readable and opt_blog_post_sidebar" t-value="'o_wblog_read_with_sidebar mx-auto'"/>
+ <t t-set="readableClass" t-elif="opt_blog_post_readable" t-value="'container'"/>
+
+ <div id="title" t-attf-class="blog_header o_wblog_regular_cover_container #{readableClass}">
+
+ <t t-if="opt_blog_post_breadcrumb" t-call="website_blog.post_breadcrumbs">
+ <t t-set="additionnal_classes" t-value="'mt-4 mb-3 bg-transparent'"></t>
+ </t>
+
+ <div t-att-class="not opt_blog_post_breadcrumb and 'pt-4'">
+ <div t-attf-class="o_wblog_post_title mb-3 #{'js_tweet' if opt_blog_post_select_to_tweet else ''} #{'js_comment' if opt_blog_post_select_to_comment else ''}" t-ignore="False">
+ <div t-field="blog_post.name" id="o_wblog_post_name" data-oe-expression="blog_post.name" t-att-data-blog-id="blog_post.id" placeholder="Title"/>
+ <div t-field="blog_post.subtitle" id="o_wblog_post_subtitle" placeholder="Subtitle"/>
+ </div>
+ <div class="text-muted mb-2">
+ <i class="fa fa-clock-o fa-fw"/>
+ <span t-field="blog_post.post_date" class="text-muted" t-options='{"format": "d MMMM, yyyy"}'/>
+ <span>by
+ <t t-call="website_blog.post_author">
+ <t t-set="additionnal_classes" t-value="'d-inline-flex mr-2'"/>
+ <t t-set="hide_date" t-value="True"/>
+ </t>
+ </span>
+ <span t-if="len(blog_post.message_ids) > 0" class="text-nowrap pl-2 o_not_editable">|
+ <i class="fa fa-comment text-muted mr-1"/>
+ <a href="#discussion">
+ <t t-esc="len(blog_post.message_ids)"/>
+ <t t-if="len(blog_post.message_ids)>1">Comments</t>
+ <t t-else="">Comment</t>
+ </a>
+ </span>
+ <span t-elif="is_view_active('website_blog.opt_blog_post_comment')">| No comments yet</span>
+ </div>
+ </div>
+
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="blog_post"/>
+ <t t-set="additionnal_classes" t-value="'o_wblog_post_page_cover o_wblog_post_page_cover_regular rounded shadow overflow-hidden'"/>
+ <t t-set="use_size" t-value="True"/>
+ </t>
+ </div>
+ </div>
+ </xpath>
+</template>
+
+<!-- (Option) Post: Show Breadcrumb
+ Display navigation breadcrumbs before the post content
+-->
+<template id="opt_blog_post_breadcrumb" name="Show Breadcrumb" inherit_id="website_blog.blog_post_complete" active="True" customize_show="True"/>
+
+<!-- (Option) Post: Select text to Tweet
+ Allow to select text to tweet it
+-->
+<template id="opt_blog_post_select_to_tweet" name="Select to Tweet" inherit_id="website_blog.blog_post_complete" active="False" customize_show="True"/>
+
+<!-- (Option) Post: Comments
+ Enable comments
+-->
+<template id="opt_blog_post_comment" name="Allow Comments" inherit_id="website_blog.blog_post_complete" active="False" customize_show="True">
+ <xpath expr="//section[@id='o_wblog_post_main']" position="inside">
+ <t t-set="readableClass" t-if="opt_blog_post_readable and opt_blog_post_sidebar" t-value="'o_wblog_read_with_sidebar'"/>
+ <t t-set="readableClass" t-elif="opt_blog_post_readable" t-value="'o_container_small'"/>
+
+ <div class="container">
+ <div t-attf-class="mx-auto #{readableClass}">
+ <div id="o_wblog_post_comments" t-attf-class="pt-4 o_container_small">
+ <div groups="base.group_public" class="small mb-4">
+ <a t-attf-href="/web/login?redirect=/blog/{{slug(blog_post.blog_id)}}/{{slug(blog_post)}}#discussion" class="btn btn-sm btn-primary"><b>Sign in</b></a> to leave a comment
+ </div>
+ <t t-call="portal.message_thread">
+ <t t-set="object" t-value="blog_post"/>
+ </t>
+ </div>
+ </div>
+ </div>
+ </xpath>
+</template>
+
+<!-- (Option) Post: Comments: Select text to Comment
+ Allow to select text to comment it
+-->
+<template id="opt_blog_post_select_to_comment" name="Select to Comment" inherit_id="website_blog.opt_blog_post_comment" active="False" customize_show="True"/>
+
+<!-- (Option) Post : Read Next Article
+ Show 'read next' banner at the bottom of the page
+-->
+<template id="opt_blog_post_read_next" name="Read Next Article" inherit_id="website_blog.blog_post_complete" active="True" customize_show="True">
+ <xpath expr="//section[@id='o_wblog_post_footer']" position="inside">
+ <div t-if="next_post" class="mt-5">
+ <t t-if="opt_blog_post_regular_cover">
+ <t t-if="opt_blog_post_sidebar" t-set="readableClass" t-value="'o_wblog_read_with_sidebar'"/>
+ <t t-else="" t-set="readableClass" t-value="'o_container_small'"/>
+
+ <div class="container">
+ <div t-attf-class="mb-4 mx-auto #{ readableClass if opt_blog_post_readable else ''}">
+ <hr/>
+ <div class="d-flex text-right py-4">
+ <div class="flex-grow-1 pr-3">
+ <span class="bg-o-color-3 h6 d-inline-block py-1 px-2 rounded-sm">Read Next</span>
+ <a t-att-href="'/blog/' + slug(next_post.blog_id) + '/' + slug(next_post)" t-att-title="'Read next' + next_post.name">
+ <div t-field="next_post.name" id="o_wblog_post_name" t-att-data-blog-id="next_post.id" placeholder="Blog Post Title" class="h2"/>
+ <div t-field="next_post.subtitle" id="o_wblog_post_subtitle" placeholder="Subtitle" class="lead"/>
+ </a>
+ </div>
+ <a t-att-href="'/blog/' + slug(next_post.blog_id) + '/' + slug(next_post)" t-att-title="'Read next' + next_post.name" class="w-25">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="next_post"/>
+ <t t-set="additionnal_classes" t-value="'rounded shadow-sm overflow-hidden h-100'"/>
+ </t>
+ </a>
+ </div>
+ </div>
+ </div>
+ </t>
+ <t t-else="">
+ <div id="o_wblog_next_container" class="d-flex flex-column">
+ <t t-call="website.record_cover">
+ <t t-set="_record" t-value="next_post"/>
+ <t t-set="_cp" t-value="json.loads(_record.cover_properties)"/>
+ <t t-set="use_filters" t-value="True"/>
+ <t t-set="additionnal_classes" t-value="'o_wblog_post_page_cover o_wblog_post_page_cover_footer o_record_has_cover'"/>
+
+ <a id="o_wblog_next_post_info" class="d-none"
+ t-att-data-size="_cp.get('resize_class')"
+ t-att-data-url="'/blog/' + slug(next_post.blog_id) + '/' + slug(next_post) + '?anim'"/>
+
+ <t t-set="next_cover_is_full" t-value="bool({'o_full_screen_height', 'cover_full'}.intersection(_cp.get('resize_class', '').split(' ')))"/>
+ <t t-set="next_cover_is_auto" t-value="'cover_auto' in _cp.get('resize_class', '')"/>
+
+ <div class="container text-center position-relative h-100 d-flex flex-column flex-grow-1 justify-content-around">
+ <div t-attf-class="o_wblog_post_title">
+ <div t-field="next_post.name" id="o_wblog_post_name" t-att-data-blog-id="next_post.id" placeholder="Blog Post Title"/>
+ <div t-field="next_post.subtitle" id="o_wblog_post_subtitle" placeholder="Subtitle"/>
+ </div>
+
+ <div t-attf-class="o_wblog_toggle #{next_cover_is_full and 'mb-n5'}">
+ <span class="h4 d-inline-block py-1 px-2 rounded-sm text-white">
+ <i class="fa fa-angle-right fa-3x text-white" aria-label="Read next" title="Read Next"/>
+ </span>
+ </div>
+
+ <!-- Emulate the next post's cover's height. For non-auto covers,
+ the room that will be occupied by the 'scroll-down' link is temporary
+ occupied by the loader circle. For auto covers, an empty <div>
+ creates enought separation.
+ -->
+ <div t-if="not next_cover_is_auto" class="o_wblog_next_loader o_wblog_toggle justify-content-center align-items-center mx-auto position-relative d-none">
+ <div class="rounded-circle bg-black-50"/>
+ </div>
+ <div t-else="" class="o_wblog_next_fake_btn d-flex o_wblog_toggle"/>
+ </div>
+ </t>
+ </div>
+ </t>
+ </div>
+ </xpath>
+</template>
+
+<!-- ====== Technical Templates ============================================
+============================================================================ -->
+<!-- Duplicate post Action -->
+<template id="blog_edit_options" inherit_id="website.user_navbar" name="Edit Blog Options">
+ <xpath expr="//a[@id='edit-in-backend']" position="after">
+ <t groups="website.group_website_designer" t-if="main_object._name == 'blog.post'">
+ <form class="duplicate d-none" 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 if blog_post else None"/>
+ </form>
+ <a href="#" class="duplicate dropdown-item" onclick="$(this).prev('form').submit()">Duplicate</a>
+ </t>
+ </xpath>
+</template>
+
+<!-- User Navbar -->
+<template id="user_navbar_inherit_website_blog" inherit_id="website.user_navbar">
+ <xpath expr="//div[@id='o_new_content_menu_choices']//div[@name='module_website_blog']" position="attributes">
+ <attribute name="name"/>
+ <attribute name="t-att-data-module-id"/>
+ <attribute name="t-att-data-module-shortdesc"/>
+ <attribute name="groups">website.group_website_designer</attribute>
+ </xpath>
+</template>
+
+<!-- Atom Feed -->
+<template id="blog_feed">&lt;?xml version="1.0" encoding="utf-8"?&gt;
+<feed t-att-xmlns="'http://www.w3.org/2005/Atom'">
+ <title t-esc="blog.name"/>
+ <link t-att-href="'%s/blog/%s' % (base_url ,blog.id)"/>
+ <id t-esc="'%s/blog/%s' % (base_url, blog.id)"/>
+ <updated t-esc="str(posts[0].post_date).replace(' ', 'T') + 'Z' if posts else ''"/>
+ <entry t-foreach="posts" t-as="post">
+ <title t-esc="post.name"/>
+ <link t-att-href="'%s%s' % (base_url, post.website_url)"/>
+ <id t-esc="'%s%s' % (base_url, post.website_url)"/>
+ <author><name t-esc="post.sudo().author_id.name"/></author>
+ <summary t-esc="html2plaintext(post.teaser)"/>
+ <updated t-esc="str(post.post_date).replace(' ', 'T') + 'Z'"/>
+ </entry>
+</feed>
+</template>
+
+<!-- Chatter templates -->
+<template id="blog_post_template_new_post">
+ <p>A new post <t t-esc="post.name" /> has been published on the <t t-esc="object.name" /> blog. Click here to access the blog :</p>
+ <p style="margin-left: 30px; margin-top: 10 px; margin-bottom: 10px;">
+ <a t-attf-href="/blog/#{slug(object)}/#{slug(post)}"
+ style="padding: 5px 10px; font-size: 12px; line-height: 18px; color: #FFFFFF; border-color:#875A7B; text-decoration: none; display: inline-block; margin-bottom: 0px; font-weight: 400; text-align: center; vertical-align: middle; cursor: pointer;background-color: #875A7B; border: 1px solid #875A7B; border-radius:3px">
+ Access post
+ </a>
+ </p>
+</template>
+
+</odoo>
diff --git a/addons/website_blog/views/website_blog_views.xml b/addons/website_blog/views/website_blog_views.xml
new file mode 100644
index 00000000..c28c6cb6
--- /dev/null
+++ b/addons/website_blog/views/website_blog_views.xml
@@ -0,0 +1,274 @@
+<?xml version="1.0"?>
+<odoo>
+
+ <!-- Blog views -->
+ <record id="view_blog_blog_list" model="ir.ui.view">
+ <field name="name">blog.blog.list</field>
+ <field name="model">blog.blog</field>
+ <field name="arch" type="xml">
+ <tree string="Blogs">
+ <field name="name"/>
+ <field name="blog_post_count"/>
+ <field name="website_id" groups="website.group_multi_website"/>
+ <field name="active" invisible="1"/>
+ </tree>
+ </field>
+ </record>
+ <record id="view_blog_blog_form" model="ir.ui.view">
+ <field name="name">blog.blog.form</field>
+ <field name="model">blog.blog</field>
+ <field name="arch" type="xml">
+ <form string="Blog">
+ <sheet>
+ <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
+ <group>
+ <field name="active" invisible="1"/>
+ <field name="name"/>
+ <field name="subtitle"/>
+ <field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
+ </group>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" groups="base.group_user"/>
+ <field name="message_ids"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <!-- page list view -->
+ <record id="view_blog_post_list" model="ir.ui.view">
+ <field name="name">blog.post.list</field>
+ <field name="model">blog.post</field>
+ <field name="arch" type="xml">
+ <tree string="Blog Posts" multi_edit="1" sample="1">
+ <field name="name"/>
+ <field name="active" invisible="1"/>
+ <field name="author_id"/>
+ <field name="blog_id"/>
+ <field name="website_id" groups="website.group_multi_website"/>
+ <field name="is_published" string="Is Published" optional="hide"/>
+ <field name="visits" readonly="1"/>
+ <field name="create_uid" invisible="1"/>
+ <field name="write_uid"/>
+ <field name="write_date"/>
+ </tree>
+ </field>
+ </record>
+ <!-- page form view -->
+ <record id="view_blog_post_form" model="ir.ui.view">
+ <field name="name">blog.post.form</field>
+ <field name="model">blog.post</field>
+ <field name="arch" type="xml">
+ <form string="Blog Post">
+ <sheet>
+ <div class="oe_button_box" name="button_box" attrs="{'invisible': [('active', '=', False)]}">
+ <field name="is_published" widget="website_redirect_button"/>
+ </div>
+ <widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
+ <group name="blog_details">
+ <field name="blog_id"/>
+ <field name="active" invisible="1"/>
+ <field name="name" placeholder="Blog Post Title"/>
+ <field name="subtitle" placeholder="Blog Subtitle"/>
+ <field name="tag_ids" widget="many2many_tags"/>
+ <field name="website_id" groups="website.group_multi_website"/>
+ </group>
+ <group name="publishing_details" string="Publishing Options">
+ <field name="author_id"/>
+ <field name="create_date" groups="base.group_no_one"/>
+ <field name="post_date"/>
+ <field name="write_uid"/>
+ <field name="write_date"/>
+ </group>
+ <notebook>
+ <page name="seo" string="SEO" groups="base.group_no_one">
+ <group name="default_opengraph">
+ <field name="website_meta_title" string="Meta Title"/>
+ <field name="website_meta_description" string="Meta Description"/>
+ <field name="website_meta_keywords" string="Meta Keywords" help="Separate every keyword with a comma"/>
+ </group>
+ </page>
+ </notebook>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" groups="base.group_user"/>
+ <field name="message_ids"/>
+ </div>
+ </form>
+ </field>
+ </record>
+ <!-- page search view -->
+ <record id="view_blog_post_search" model="ir.ui.view">
+ <field name="name">blog.post.search</field>
+ <field name="model">blog.post</field>
+ <field name="arch" type="xml">
+ <search string="Blog Post">
+ <filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
+ <field name="name" string="Content" filter_domain="['|', ('name','ilike',self), ('content','ilike',self)]"/>
+ <field name="write_uid"/>
+ <field name="blog_id"/>
+ <group expand="0" string="Group By">
+ <filter string="Blog" name="group_by_blog" domain="[]" context="{'group_by': 'blog_id'}"/>
+ <filter string="Author" name="group_by_author" domain="[]" context="{'group_by': 'create_uid'}"/>
+ <filter string="Last Contributor" name="last_contributor" domain="[]" context="{'group_by': 'write_uid'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <record id="blog_post_view_kanban" model="ir.ui.view">
+ <field name="name">blog.post.kanban</field>
+ <field name="model">blog.post</field>
+ <field name="arch" type="xml">
+ <kanban class="o_kanban_mobile" sample="1">
+ <field name="name"/>
+ <field name="blog_id"/>
+ <field name="author_id"/>
+ <field name="post_date"/>
+ <templates>
+ <t t-name="kanban-box">
+ <div class="oe_kanban_global_click">
+ <div class="row mb4">
+ <strong class="col-8">
+ <span t-esc="record.name.value"/>
+ </strong>
+ <strong class="col-4 text-right">
+ <span t-esc="record.blog_id.value"/>
+ </strong>
+ <div class="col-8">
+ <i class="fa fa-clock-o" role="img" aria-label="Post date" title="Post date"/><span t-esc="record.post_date.value"/>
+ </div>
+ <div class="col-4 text-right">
+ <img t-if="record.author_id.raw_value"
+ t-att-title="record.author_id.value"
+ t-att-alt="record.author_id.value"
+ class="oe_kanban_avatar o_image_24_cover"
+ t-att-src="kanban_image('res.partner', 'image_128', record.author_id.raw_value)"/>
+ </div>
+ </div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <!-- page action -->
+ <record id="action_blog_post" model="ir.actions.act_window">
+ <field name="name">Blog Posts</field>
+ <field name="res_model">blog.post</field>
+ <field name="view_mode">tree,form,kanban</field>
+ <field name="view_id" ref="view_blog_post_list"/>
+ <field name="search_view_id" ref="view_blog_post_search"/>
+ <field name="help" type="html">
+ <p class="o_view_nocontent_smiling_face">
+ Create a new blog post
+ </p>
+ </field>
+ </record>
+
+ <record id="blog_blog_view_search" model="ir.ui.view">
+ <field name="name">blog.blog.search</field>
+ <field name="model">blog.blog</field>
+ <field name="arch" type="xml">
+ <search string="Blog">
+ <field name="name"/>
+ <filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="action_blog_blog" model="ir.actions.act_window">
+ <field name="name">Blogs</field>
+ <field name="res_model">blog.blog</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <record id="blog_tag_tree" model="ir.ui.view">
+ <field name="name">blog_tag_tree</field>
+ <field name="model">blog.tag</field>
+ <field name="arch" type="xml">
+ <tree string="Tag List">
+ <field name="name"/>
+ <field name="category_id"/>
+ <field name="post_ids"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="blog_tag_form" model="ir.ui.view">
+ <field name="name">blog_tag_form</field>
+ <field name="model">blog.tag</field>
+ <field name="arch" type="xml">
+ <form string="Tag Form">
+ <sheet>
+ <group>
+ <field name="name"/>
+ <field name="category_id"/>
+ </group>
+ <label for="post_ids" string="Used in: "/>
+ <field name="post_ids"/>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="action_tags" model="ir.actions.act_window">
+ <field name="name">Blog Tags</field>
+ <field name="res_model">blog.tag</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="blog_tag_tree"/>
+ </record>
+
+ <record id="blog_tag_category_form" model="ir.ui.view">
+ <field name="name">blog_tag_category_form</field>
+ <field name="model">blog.tag.category</field>
+ <field name="arch" type="xml">
+ <form string="Tag Category Form">
+ <sheet>
+ <group>
+ <field name="name"/>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="blog_tag_category_tree" model="ir.ui.view">
+ <field name="name">blog_tag_category_tree</field>
+ <field name="model">blog.tag.category</field>
+ <field name="arch" type="xml">
+ <tree string="Tag Categories">
+ <field name="name"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="action_tag_category" model="ir.actions.act_window">
+ <field name="name">Tag Category</field>
+ <field name="res_model">blog.tag.category</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="blog_tag_category_tree"/>
+ </record>
+
+ <menuitem name="Blogs"
+ id="menu_website_blog_root"
+ sequence="20"
+ parent="website.menu_website_configuration"
+ groups="website.group_website_designer"
+ action="action_blog_post"/>
+
+ <menuitem name="Blogs"
+ id="menu_website_blog_root_global"
+ sequence="100"
+ parent="website.menu_website_global_configuration"
+ groups="website.group_website_designer"/>
+
+ <menuitem id="menu_website_blog_tag_category_global" parent="menu_website_blog_root_global"
+ name="Tag Categories" action="action_tag_category" sequence="50" />
+
+ <menuitem id="menu_blog_tag_global" parent="menu_website_blog_root_global" name="Tags" action="action_tags" sequence="40" />
+
+ <menuitem id="menu_blog_global" parent="menu_website_blog_root_global" name="Blogs" action="action_blog_blog" sequence="20"/>
+</odoo>