[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) <qsm@odoo.com> Co-authored-by: qsm-odoo <qsm@odoo.com>
This commit is contained in:
committed by
qsm-odoo
co-authored by
qsm-odoo
parent
ea009585e1
commit
bdcc1ea9e7
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
}));
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
|
||||
<t t-name="website_blog.TagListItem">
|
||||
<div class="o_we_tag_wrapper">
|
||||
<span t-raw="tag.name" class="o_we_tag"/>
|
||||
<we-button class="fa fa-minus"
|
||||
t-att-data-remove-tag="tag.id"
|
||||
data-no-preview="true"/>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website_blog.TagSelectItem">
|
||||
<we-button t-att-data-add-tag="tag.id" data-no-preview="true">
|
||||
<t t-raw="tag.name"/>
|
||||
</we-button>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -7,4 +7,23 @@
|
||||
</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">
|
||||
<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" data-confirm-new="" data-no-preview="true"/>
|
||||
</we-row>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -234,8 +234,8 @@ Display a sidebar beside the post content.
|
||||
<div t-if="blog_post.tag_ids" class="o_wblog_sidebar_block pb-5">
|
||||
<div class="d-flex justify-content-between mb-4 border-bottom">
|
||||
<h6 class="text-uppercase pb-2 font-weight-bold">Tags</h6>
|
||||
<a role="menuitem" t-attf-href="/web#view_type=form&model=#{main_object._name}&id=#{main_object.id}&action=#{action}&menu_id=#{menu or main_object.env.ref('website.menu_website_configuration').id}"
|
||||
title='Edit in backend' id="edit-in-backend">Edit</a>
|
||||
<a role="menuitem" t-attf-href="/blog/#{slug(blog_post.blog_id)}/post/#{slug(blog_post)}/?enable_editor=1"
|
||||
title='Edit tag list' id="edit-tag-list">Edit</a>
|
||||
</div>
|
||||
<div class="h5">
|
||||
<t t-foreach="blog_post.tag_ids" t-as="one_tag">
|
||||
@@ -246,7 +246,7 @@ Display a sidebar beside the post content.
|
||||
<div t-else="" groups="website.group_website_designer" class="o_wblog_sidebar_block pb-5">
|
||||
<h6 class="text-uppercase pb-3 mb-4 border-bottom font-weight-bold">Tags</h6>
|
||||
<div class="mb-4 bg-100 py-2 px-3 border">
|
||||
<h6 class="text-muted"><em>No tags definied</em></h6>
|
||||
<h6 class="text-muted"><em>No tags defined</em></h6>
|
||||
<a role="menuitem" t-attf-href="/web#view_type=form&model=#{main_object._name}&id=#{main_object.id}&action=#{action}&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>
|
||||
|
||||
Reference in New Issue
Block a user