diff options
| author | DramixDw <rve@odoo.com> | 2020-07-08 14:23:39 +0000 |
|---|---|---|
| committer | qsm-odoo <qsm@odoo.com> | 2020-07-31 10:32:22 +0000 |
| commit | bdcc1ea9e729dc0e3c0bc28462db97e569119cda (patch) | |
| tree | e6d6d41d90c71818e811b9cbf1de79c3f71b2b3c /addons | |
| parent | ea009585e1d14041fe19f1e6e190b286e96c0681 (diff) | |
[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>
Diffstat (limited to 'addons')
5 files changed, 304 insertions, 9 deletions
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 @@ +<?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> 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 @@ </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> 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. <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> |
