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 @@
+
+