diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 3b3b6f8fd86..417d0daabb2 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -453,7 +453,7 @@ var SnippetEditor = Widget.extend({ this, val.base_target ? this.$target.find(val.base_target).eq(0) : this.$target, this.$el, - val.data, + _.extend({snippetName: this._getName()}, val.data), this.options ); var key = optionName || _.uniqueId('option'); @@ -813,7 +813,7 @@ var SnippetsMenu = Widget.extend({ if (!$target.closest('body > *').length) { return; } - if ($target.closest('#web_editor-top-edit, #oe_snippets, #oe_manipulators, .o_technical_modal, .oe_drop_zone').length) { + if ($target.closest('#web_editor-top-edit, #oe_snippets, #oe_manipulators, .o_technical_modal, .oe_drop_zone, .o_notification_manager').length) { return; } this._activateSnippet($target); diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index c3ea729d28d..cb94093c731 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -1547,13 +1547,31 @@ options.registry.topMenuColor = options.registry.colorpicker.extend({ /** * Handles the edition of snippet's anchor name. */ -options.registry.anchorName = options.Class.extend({ +options.registry.anchor = options.Class.extend({ xmlDependencies: ['/website/static/src/xml/website.editor.xml'], //-------------------------------------------------------------------------- // Public //-------------------------------------------------------------------------- + /** + * @override + */ + start: function () { + // Generate anchor and copy it to clipboard on click, show the tooltip on success + this.$button = this.$el.find('we-button'); + const clipboard = new ClipboardJS(this.$button[0], {text: () => this._getAnchorLink()}); + clipboard.on('success', () => { + const anchor = decodeURIComponent(this._getAnchorLink()); + this.displayNotification({ + title: _t("Copied !"), + message: _.str.sprintf(_t("The anchor has been copied to your clipboard.
Link: %s"), anchor), + buttons: [{text: _t("edit"), click: () => this.openAnchorDialog()}], + }); + }); + + return this._super.apply(this, arguments); + }, /** * @override */ @@ -1568,20 +1586,19 @@ options.registry.anchorName = options.Class.extend({ }, //-------------------------------------------------------------------------- - // Options + // Private //-------------------------------------------------------------------------- - /** * @see this.selectClass for parameters */ openAnchorDialog: function (previewMode, value, $opt) { var self = this; var buttons = [{ - text: _t("Save"), + text: _t("Save & copy"), classes: 'btn-primary', click: function () { var $input = this.$('.o_input_anchor_name'); - var anchorName = $input.val().trim().replace(/\s/g, '_'); + var anchorName = self._text2Anchor($input.val()); if (self.$target[0].id === anchorName) { // If the chosen anchor name is already the one used by the // element, close the dialog and do nothing else @@ -1589,15 +1606,13 @@ options.registry.anchorName = options.Class.extend({ return; } - var isValid = /^[\w-]+$/.test(anchorName); - var alreadyExists = isValid && $('#' + anchorName).length > 0; - var anchorOK = isValid && !alreadyExists; - this.$('.o_anchor_not_valid').toggleClass('d-none', isValid); + const alreadyExists = !!document.getElementById(anchorName); this.$('.o_anchor_already_exists').toggleClass('d-none', !alreadyExists); - $input.toggleClass('is-invalid', !anchorOK); - if (anchorOK) { + $input.toggleClass('is-invalid', alreadyExists); + if (!alreadyExists) { self._setAnchorName(anchorName); this.close(); + self.$button[0].click(); } }, }, { @@ -1618,16 +1633,11 @@ options.registry.anchorName = options.Class.extend({ new Dialog(this, { title: _t("Link Anchor"), $content: $(qweb.render('website.dialog.anchorName', { - currentAnchor: this.$target.attr('id'), + currentAnchor: decodeURIComponent(this.$target.attr('id')), })), buttons: buttons, }).open(); }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - /** * @private * @param {String} value @@ -1643,6 +1653,35 @@ options.registry.anchorName = options.Class.extend({ } this.$target.trigger('content_changed'); }, + /** + * Returns anchor text. + * + * @private + * @returns {string} + */ + _getAnchorLink: function () { + if (!this.$target[0].id) { + const $titles = this.$target.find('h1, h2, h3, h4, h5, h6'); + const title = $titles.length > 0 ? $titles[0].innerText : this.data.snippetName; + const anchorName = this._text2Anchor(title); + let n = ''; + while (document.getElementById(anchorName + n)) { + n = (n || 1) + 1; + } + this._setAnchorName(anchorName + n); + } + return `#${this.$target[0].id}`; + }, + /** + * Creates a safe id/anchor from text. + * + * @private + * @param {string} text + * @returns {string} + */ + _text2Anchor: function (text) { + return encodeURIComponent(text.trim().replace(/\s+/g, '-')); + }, }); /** diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml index 9f8a2e302fc..31ffb28704d 100644 --- a/addons/website/static/src/xml/website.editor.xml +++ b/addons/website/static/src/xml/website.editor.xml @@ -115,7 +115,6 @@
-

The chosen name is not valid (use only a-Z A-Z 0-9 - _)

The chosen name already exists

diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index e496d088f50..955babf1637 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -1352,13 +1352,12 @@ -
+ data-no-preview="true"/>