diff --git a/addons/website_blog/controllers/main.py b/addons/website_blog/controllers/main.py index 8adb6c5b9e6..f16f6ed761b 100644 --- a/addons/website_blog/controllers/main.py +++ b/addons/website_blog/controllers/main.py @@ -293,3 +293,8 @@ class WebsiteBlog(http.Controller): if not post_id: return False return request.env['blog.post'].browse(int(post_id)).write({'cover_properties': json.dumps(cover_properties)}) + + @http.route(['/blog/render_latest_posts'], type='json', auth='public', website=True) + def render_latest_posts(self, template, domain, limit=None, order='published_date desc'): + posts = request.env['blog.post'].search(domain, limit=limit, order=order) + return request.env.ref(template).render({'posts': posts}) diff --git a/addons/website_blog/static/src/img/s_latest_posts.jpg b/addons/website_blog/static/src/img/s_latest_posts.jpg new file mode 100644 index 00000000000..87d14400e43 Binary files /dev/null and b/addons/website_blog/static/src/img/s_latest_posts.jpg differ diff --git a/addons/website_blog/static/src/img/s_latest_posts_big_picture.jpg b/addons/website_blog/static/src/img/s_latest_posts_big_picture.jpg new file mode 100644 index 00000000000..38d2ca15a39 Binary files /dev/null and b/addons/website_blog/static/src/img/s_latest_posts_big_picture.jpg differ diff --git a/addons/website_blog/static/src/js/s_latest_posts_editor.js b/addons/website_blog/static/src/js/s_latest_posts_editor.js new file mode 100644 index 00000000000..10052de87b7 --- /dev/null +++ b/addons/website_blog/static/src/js/s_latest_posts_editor.js @@ -0,0 +1,106 @@ +odoo.define('website_blog.s_latest_posts_editor', function (require) { +'use strict'; + +var core = require('web.core'); +var sOptions = require('web_editor.snippets.options'); + +var _t = core._t; + +sOptions.registry.js_get_posts_limit = sOptions.Class.extend({ + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * @see this.selectClass for parameters + */ + postsLimit: function (previewMode, value, $opt) { + value = parseInt(value); + this.$target.attr('data-posts-limit', value).data('postsLimit', value); + this.trigger_up('animation_start_demand', { + editableMode: true, + $target: this.$target, + }); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _setActive: function () { + var self = this; + this._super.apply(this, arguments); + this.$('[data-posts-limit]').addBack('[data-posts-limit]') + .removeClass('active') + .filter(function () { + return (self.$target.data('postsLimit') || 3) == $(this).data('postsLimit'); + }) + .addClass('active'); + }, +}); + +sOptions.registry.js_get_posts_selectBlog = sOptions.Class.extend({ + /** + * @override + */ + start: function () { + var self = this; + + var def = this._rpc({ + model: 'blog.blog', + method: 'search_read', + args: [[], ['name', 'id']], + }).then(function (blogs) { + var $menu = self.$el.find('[data-filter-by-blog-id="0"]').parent(); + _.each(blogs, function (blog) { + $menu.append($('', { + class: 'dropdown-item', + 'data-filter-by-blog-id': blog.id, + 'data-no-preview': 'true', + text: blog.name, + })); + }); + }); + + return $.when(this._super.apply(this, arguments), def); + }, + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * @see this.selectClass for parameters + */ + filterByBlogId: function (previewMode, value, $opt) { + value = parseInt(value); + this.$target.attr('data-filter-by-blog-id', value).data('filterByBlogId', value); + this.trigger_up('animation_start_demand', { + editableMode: true, + $target: this.$target, + }); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _setActive: function () { + var self = this; + this._super.apply(this, arguments); + this.$('[data-filter-by-blog-id]').addBack('[data-filter-by-blog-id]') + .removeClass('active') + .filter(function () { + return (self.$target.data('filterByBlogId') || 0) == $(this).data('filterByBlogId'); + }) + .addClass('active'); + }, +}); +}); diff --git a/addons/website_blog/static/src/js/s_latest_posts_frontend.js b/addons/website_blog/static/src/js/s_latest_posts_frontend.js new file mode 100644 index 00000000000..f1b8ea4f23d --- /dev/null +++ b/addons/website_blog/static/src/js/s_latest_posts_frontend.js @@ -0,0 +1,142 @@ +odoo.define('website_blog.s_latest_posts_frontend', function (require) { +'use strict'; + +var core = require('web.core'); +var sAnimation = require('website.content.snippets.animation'); + +var _t = core._t; + +sAnimation.registry.js_get_posts = sAnimation.Class.extend({ + selector : '.js_get_posts', + + /** + * @override + */ + start: function () { + var self = this; + var limit = self.$target.data('postsLimit') || 3; + var blogID = self.$target.data('filterByBlogId'); + var template = self.$target.data('template') || 'website_blog.s_latest_posts_list_template'; + var loading = self.$target.data('loading'); + + this.$target.empty(); // Compatibility with db that saved content inside by mistake + this.$target.attr('contenteditable', 'False'); // Prevent user edition + + var domain = [['website_published', '=', true]]; + if (blogID) { + domain.push(['blog_id', '=', parseInt(blogID)]); + } + + var def = $.Deferred(); + this._rpc({ + route: '/blog/render_latest_posts', + params: { + template: template, + domain: domain, + limit: limit, + }, + }).then(function (posts) { + var $posts = $(posts).filter('.s_latest_posts_post'); + if (!$posts.length) { + self.$target.append($('
', {class: 'col-md-6 offset-md-3'}) + .append($('', { + class: 'alert alert-warning alert-dismissible text-center', + text: _t("No blog post was found. Make sure your posts are published."), + }))); + return; + } + + if (loading && loading === true) { + // Perform an intro animation + self._showLoading($posts); + } else { + self.$target.html($posts); + } + }, function (e) { + if (self.editableMode) { + self.$target.append($('', { + class: 'text-danger', + text: _t("An error occured with this latest posts block. If the problem persists, please consider deleting it and adding a new one"), + })); + } + }).always(def.resolve.bind(def)); + return $.when(this._super.apply(this, arguments), def); + }, + /** + * @override + */ + destroy: function () { + this.$target.empty(); + this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @private + */ + _showLoading: function ($posts) { + var self = this; + + _.each($posts, function (post) { + var $post = $(post); + var $loadingContainer = $post.find('.loading_container'); + var $thumb = $post.find('.thumb .o_blog_cover_image'); + var $progress = $('', { + class: 'progress js-loading', + }).append($('', { + class: 'progress-bar', + role: 'progressbar', + 'aria-valuenow': '0', + 'aria-valuemin': '0', + 'aria-valuemax': '100', + css: { + width: 0, + }, + })); + + // If can't find loading container or thumb inside the post, then they are the post itself + if (!$loadingContainer.length) { + $loadingContainer = $post; + } + if (!$thumb.length) { + $thumb = $post; + } + + $post.addClass('js-loading'); + $progress.appendTo($loadingContainer); + $post.appendTo(self.$target); + + var m = $thumb.css('background-image').match(/url\(["']?(.+)["']?\)/); + var bg = m ? m[1] : 'none'; + var loaded = false; + + var $bar = $progress.find('.progress-bar'); + $bar.css('width', '50%').attr('aria-valuenow', '50'); + + var $dummyImg = $('