From bdcc1ea9e729dc0e3c0bc28462db97e569119cda Mon Sep 17 00:00:00 2001 From: DramixDw Date: Wed, 8 Jul 2020 14:23:39 +0000 Subject: [PATCH] [IMP] website_blog: add tag from frontend (editor left panel) Previously, you could only add a tag on a blog post from the backend. Here we're adding a way to add them from the left-panel so that it's easier for the user to add them. The long term goal will not be to reimplement complex view of the backend. This is actually already past the limit: we will need a way to instantiate views from the backend in the frontend in the future. Part of https://github.com/odoo/odoo/pull/52759 task-2267693 closes odoo/odoo#52759 Signed-off-by: Quentin Smetz (qsm) Co-authored-by: qsm-odoo --- .../static/src/scss/wysiwyg_snippets.scss | 16 ++ .../static/src/js/website_blog.editor.js | 253 +++++++++++++++++- .../static/src/xml/website_blog_tag.xml | 19 ++ .../website_blog/views/snippets/snippets.xml | 19 ++ .../views/website_blog_components.xml | 6 +- 5 files changed, 304 insertions(+), 9 deletions(-) create mode 100644 addons/website_blog/static/src/xml/website_blog_tag.xml diff --git a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss index a65d73f87c4..ad11b201fde 100644 --- a/addons/web_editor/static/src/scss/wysiwyg_snippets.scss +++ b/addons/web_editor/static/src/scss/wysiwyg_snippets.scss @@ -893,6 +893,22 @@ body.editor_enable.editor_has_snippets { margin-right: $o-we-sidebar-content-field-label-spacing; } } + + .o_we_tag { + display: inline-block; + padding: $o-we-sidebar-content-field-label-spacing; + border: $o-we-item-border-width solid $o-we-item-border-color; + border-radius: 5px; + background-color: $o-we-bg-darkest; + + + .fa { + margin-left: 0; + } + } + .o_we_tag_wrapper { + display: inline-block; + margin: ($o-we-sidebar-content-field-label-spacing * 1.5) $o-we-sidebar-content-field-label-spacing 0 0; + } } > .o_we_invisible_el_panel { diff --git a/addons/website_blog/static/src/js/website_blog.editor.js b/addons/website_blog/static/src/js/website_blog.editor.js index 202c0f6cd0f..1229b1e6500 100644 --- a/addons/website_blog/static/src/js/website_blog.editor.js +++ b/addons/website_blog/static/src/js/website_blog.editor.js @@ -62,23 +62,91 @@ odoo.define('website_blog.editor', function (require) { 'use strict'; require('web.dom_ready'); -var core = require('web.core'); -var options = require('web_editor.snippets.options'); +const {qweb, _t} = require('web.core'); +const options = require('web_editor.snippets.options'); var WysiwygMultizone = require('web_editor.wysiwyg.multizone'); -var _t = core._t; - if (!$('.website_blog').length) { return Promise.reject("DOM doesn't contain '.website_blog'"); } +const NEW_TAG_PREFIX = 'new-blog-tag-'; + WysiwygMultizone.include({ + custom_events: Object.assign({}, WysiwygMultizone.prototype.custom_events, { + 'set_blog_post_updated_tags': '_onSetBlogPostUpdatedTags', + }), + /** * @override */ - start: function () { + init() { + this._super(...arguments); + this.blogTagsPerBlogPost = {}; + }, + /** + * @override + */ + async start() { + await this._super(...arguments); $('.js_tweet, .js_comment').off('mouseup').trigger('mousedown'); - return this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @override + */ + async save() { + const ret = await this._super(...arguments); + await this._saveBlogTags(); // Note: important to be called after save otherwise cleanForSave is not called before + return ret; + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * Saves the blog tags in the database. + * + * @private + */ + async _saveBlogTags() { + for (const [key, tags] of Object.entries(this.blogTagsPerBlogPost)) { + const proms = tags.filter(tag => typeof tag.id === 'string').map(tag => { + return this._rpc({ + model: 'blog.tag', + method: 'create', + args: [{ + 'name': tag.name, + }], + }); + }); + const createdIDs = await Promise.all(proms); + + await this._rpc({ + model: 'blog.post', + method: 'write', + args: [parseInt(key), { + 'tag_ids': [[6, 0, tags.filter(tag => typeof tag.id === 'number').map(tag => tag.id).concat(createdIDs)]], + }], + }); + } + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + * @param {OdooEvent} ev + */ + _onSetBlogPostUpdatedTags: function (ev) { + this.blogTagsPerBlogPost[ev.data.blogPostID] = ev.data.tags; }, }); @@ -125,4 +193,177 @@ options.registry.CoverProperties.include({ $coverAuto.children('div').text(isRegularCover ? _t("Tiny") : this._coverAutoOriginalLabel); }, }); + +options.registry.BlogPostTagSelection = options.Class.extend({ + xmlDependencies: (options.Class.prototype.xmlDependencies || []) + .concat(['/website_blog/static/src/xml/website_blog_tag.xml']), + + /** + * @override + */ + async willStart() { + const _super = this._super.bind(this); + + this.blogPostID = parseInt(this.$target[0].dataset.blogId); + this.isEditingTags = false; + const tags = await this._rpc({ + model: 'blog.tag', + method: 'search_read', + args: [[], ['id', 'name', 'post_ids']], + }); + this.allTagsByID = {}; + this.tagIDs = []; + for (const tag of tags) { + this.allTagsByID[tag.id] = tag; + if (tag['post_ids'].includes(this.blogPostID)) { + this.tagIDs.push(tag.id); + } + } + + return _super(...arguments); + }, + /** + * @override + */ + cleanForSave() { + if (this.isEditingTags) { + this._notifyUpdatedTags(); + } + }, + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * @see this.selectClass for params + */ + editTagList(previewMode, widgetValue, params) { + this.isEditingTags = true; + this.rerender = true; + }, + /** + * Send changes that will be saved in the database. + * + * @see this.selectClass for params + */ + saveTagList(previewMode, widgetValue, params) { + this.isEditingTags = false; + this.rerender = true; + this._notifyUpdatedTags(); + }, + /** + * @see this.selectClass for params + */ + setNewTagName(previewMode, widgetValue, params) { + this.newTagName = widgetValue; + }, + /** + * @see this.selectClass for params + */ + confirmNew(previewMode, widgetValue, params) { + if (!this.newTagName) { + return; + } + const existing = Object.values(this.allTagsByID).some(tag => tag.name.toLowerCase() === this.newTagName.toLowerCase()); + if (existing) { + return this.displayNotification({ + type: 'warning', + message: _t("This tag already exists"), + }); + } + const newTagID = _.uniqueId(NEW_TAG_PREFIX); + this.allTagsByID[newTagID] = { + 'id': newTagID, + 'name': this.newTagName, + }; + this.tagIDs.push(newTagID); + this.newTagName = ''; + this.rerender = true; + }, + /** + * @see this.selectClass for params + */ + addTag(previewMode, widgetValue, params) { + const tagID = parseInt(widgetValue); + this.tagIDs.push(tagID); + this.rerender = true; + }, + /** + * @see this.selectClass for params + */ + removeTag(previewMode, widgetValue, params) { + this.tagIDs = this.tagIDs.filter(tagID => (`${tagID}` !== widgetValue)); + if (widgetValue.startsWith(NEW_TAG_PREFIX)) { + delete this.allTagsByID[widgetValue]; + } + this.rerender = true; + }, + + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @override + */ + async updateUI() { + if (this.rerender) { + this.rerender = false; + await this._rerenderXML(); + return; + } + return this._super(...arguments); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + async _computeWidgetVisibility(widgetName, params) { + if (['blog_existing_tag_opt', 'new_tag_input_opt', 'new_tag_button_opt', 'save_tags_opt'].includes(widgetName)) { + return this.isEditingTags; + } + if (widgetName === 'edit_tags_opt') { + return !this.isEditingTags; + } + if (params.optionsPossibleValues['removeTag']) { + return this.isEditingTags; + } + return this._super(...arguments); + }, + /** + * @private + */ + _notifyUpdatedTags() { + this.trigger_up('set_blog_post_updated_tags', { + blogPostID: this.blogPostID, + tags: this.tagIDs.map(tagID => this.allTagsByID[tagID]), + }); + }, + /** + * @override + */ + async _renderCustomXML(uiFragment) { + const $tagList = $(uiFragment.querySelector('.o_wblog_tag_list')); + for (const tagID of this.tagIDs) { + const tag = this.allTagsByID[tagID]; + $tagList.append(qweb.render('website_blog.TagListItem', { + tag: tag, + })); + } + const $select = $(uiFragment.querySelector('we-select[data-name="blog_existing_tag_opt"]')); + for (const [key, tag] of Object.entries(this.allTagsByID)) { + if (this.tagIDs.includes(parseInt(key))) { + continue; + } + $select.prepend(qweb.render('website_blog.TagSelectItem', { + tag: tag, + })); + } + }, +}); }); diff --git a/addons/website_blog/static/src/xml/website_blog_tag.xml b/addons/website_blog/static/src/xml/website_blog_tag.xml new file mode 100644 index 00000000000..3d5431455d0 --- /dev/null +++ b/addons/website_blog/static/src/xml/website_blog_tag.xml @@ -0,0 +1,19 @@ + + + + +
+ + +
+
+ + + + + + + +
diff --git a/addons/website_blog/views/snippets/snippets.xml b/addons/website_blog/views/snippets/snippets.xml index 38ad1ab3bd7..579a4b6ca7f 100644 --- a/addons/website_blog/views/snippets/snippets.xml +++ b/addons/website_blog/views/snippets/snippets.xml @@ -7,4 +7,23 @@ + diff --git a/addons/website_blog/views/website_blog_components.xml b/addons/website_blog/views/website_blog_components.xml index 0abb7caf72a..0dddb8f2016 100644 --- a/addons/website_blog/views/website_blog_components.xml +++ b/addons/website_blog/views/website_blog_components.xml @@ -234,8 +234,8 @@ Display a sidebar beside the post content.
Tags
- Edit + Edit
@@ -246,7 +246,7 @@ Display a sidebar beside the post content.
Tags
-
No tags definied
+
No tags defined
Add some