diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/website_mass_mailing/static/src | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/website_mass_mailing/static/src')
5 files changed, 602 insertions, 0 deletions
diff --git a/addons/website_mass_mailing/static/src/img/snippets_thumbs/s_newsletter_block.svg b/addons/website_mass_mailing/static/src/img/snippets_thumbs/s_newsletter_block.svg new file mode 100644 index 00000000..cb0edfae --- /dev/null +++ b/addons/website_mass_mailing/static/src/img/snippets_thumbs/s_newsletter_block.svg @@ -0,0 +1,71 @@ +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="82" height="60" viewBox="0 0 82 60"> + <defs> + <linearGradient id="linearGradient-1" x1="0%" x2="100%" y1="44.579%" y2="55.421%"> + <stop offset="0%" stop-color="#00A09D"/> + <stop offset="100%" stop-color="#00E2FF"/> + </linearGradient> + <rect id="path-2" width="22" height="2" x="33" y="7"/> + <filter id="filter-3" width="104.5%" height="200%" x="-2.3%" y="-25%" filterUnits="objectBoundingBox"> + <feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feComposite in="shadowOffsetOuter1" in2="SourceAlpha" operator="out" result="shadowOffsetOuter1"/> + <feColorMatrix in="shadowOffsetOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.4 0"/> + </filter> + <path id="path-4" d="M13 8v10a1 1 0 0 0 1 1h14a1 1 0 0 0 1-1V8a1 1 0 0 0-1-1H14a1 1 0 0 0-1 1zm7.445 3.63L15 8h12l-5.445 3.63a1 1 0 0 1-1.11 0zM14 18V8.5l6.428 4.485a1 1 0 0 0 1.144 0L28 8.5V18H14z"/> + <filter id="filter-5" width="106.2%" height="116.7%" x="-3.1%" y="-4.2%" filterUnits="objectBoundingBox"> + <feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feComposite in="shadowOffsetOuter1" in2="SourceAlpha" operator="out" result="shadowOffsetOuter1"/> + <feColorMatrix in="shadowOffsetOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.4 0"/> + </filter> + <rect id="path-6" width="13" height="1" x="33" y="12"/> + <filter id="filter-7" width="107.7%" height="300%" x="-3.8%" y="-50%" filterUnits="objectBoundingBox"> + <feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feComposite in="shadowOffsetOuter1" in2="SourceAlpha" operator="out" result="shadowOffsetOuter1"/> + <feColorMatrix in="shadowOffsetOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"/> + </filter> + <rect id="path-8" width="10" height="1" x="33" y="15"/> + <filter id="filter-9" width="110%" height="300%" x="-5%" y="-50%" filterUnits="objectBoundingBox"> + <feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feComposite in="shadowOffsetOuter1" in2="SourceAlpha" operator="out" result="shadowOffsetOuter1"/> + <feColorMatrix in="shadowOffsetOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.2 0"/> + </filter> + <path id="path-10" d="M72 23.571V22.43a.55.55 0 0 0-.17-.402.55.55 0 0 0-.401-.17h-2.286v-2.286a.55.55 0 0 0-.17-.401.55.55 0 0 0-.402-.17H67.43a.55.55 0 0 0-.402.17.55.55 0 0 0-.17.401v2.286h-2.286a.55.55 0 0 0-.401.17.55.55 0 0 0-.17.402v1.142a.55.55 0 0 0 .17.402.55.55 0 0 0 .401.17h2.286v2.286a.55.55 0 0 0 .17.401.55.55 0 0 0 .402.17h1.142a.55.55 0 0 0 .402-.17.55.55 0 0 0 .17-.401v-2.286h2.286a.55.55 0 0 0 .401-.17.55.55 0 0 0 .17-.402zM75 23c0 1.27-.313 2.441-.939 3.514a6.969 6.969 0 0 1-2.547 2.547A6.848 6.848 0 0 1 68 30a6.848 6.848 0 0 1-3.514-.939 6.969 6.969 0 0 1-2.547-2.547A6.848 6.848 0 0 1 61 23c0-1.27.313-2.441.939-3.514a6.969 6.969 0 0 1 2.547-2.547A6.848 6.848 0 0 1 68 16c1.27 0 2.441.313 3.514.939a6.969 6.969 0 0 1 2.547 2.547A6.848 6.848 0 0 1 75 23z"/> + <filter id="filter-12" width="107.1%" height="114.3%" x="-3.6%" y="-3.6%" filterUnits="objectBoundingBox"> + <feOffset dy="1" in="SourceAlpha" result="shadowOffsetOuter1"/> + <feComposite in="shadowOffsetOuter1" in2="SourceAlpha" operator="out" result="shadowOffsetOuter1"/> + <feColorMatrix in="shadowOffsetOuter1" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.4 0"/> + </filter> + </defs> + <g fill="none" fill-rule="evenodd" class="snippets_thumbs"> + <g class="s_newsletter_block"> + <rect width="82" height="60" class="bg"/> + <g class="group" transform="translate(0 16)"> + <g fill="url(#linearGradient-1)" class="image_1" opacity=".4"> + <rect width="82" height="27" class="rectangle"/> + </g> + <g class="rectangle"> + <use fill="#000" filter="url(#filter-3)" xlink:href="#path-2"/> + <use fill="#FFF" fill-opacity=".95" xlink:href="#path-2"/> + </g> + <g class="shape"> + <use fill="#000" filter="url(#filter-5)" xlink:href="#path-4"/> + <use fill="#FFF" fill-opacity=".95" xlink:href="#path-4"/> + </g> + <g class="combined_shape"> + <use fill="#000" filter="url(#filter-7)" xlink:href="#path-6"/> + <use fill="#FFF" fill-opacity=".8" xlink:href="#path-6"/> + </g> + <g class="combined_shape"> + <use fill="#000" filter="url(#filter-9)" xlink:href="#path-8"/> + <use fill="#FFF" fill-opacity=".8" xlink:href="#path-8"/> + </g> + <mask id="mask-11" fill="#fff"> + <use xlink:href="#path-10"/> + </mask> + <g class="plus_circle"> + <use fill="#000" filter="url(#filter-12)" xlink:href="#path-10"/> + <use fill="#FFF" fill-opacity=".95" xlink:href="#path-10"/> + </g> + </g> + </g> + </g> +</svg> diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js new file mode 100644 index 00000000..5a46b980 --- /dev/null +++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.editor.js @@ -0,0 +1,216 @@ +odoo.define('website_mass_mailing.editor', function (require) { +'use strict'; + +var core = require('web.core'); +var rpc = require('web.rpc'); +var WysiwygMultizone = require('web_editor.wysiwyg.multizone'); +var WysiwygTranslate = require('web_editor.wysiwyg.multizone.translate'); +var options = require('web_editor.snippets.options'); +var wUtils = require('website.utils'); + +const qweb = core.qweb; +var _t = core._t; + + +options.registry.mailing_list_subscribe = options.Class.extend({ + popup_template_id: "editor_new_mailing_list_subscribe_button", + popup_title: _t("Add a Newsletter Subscribe Button"), + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * Allows to select mailing list. + * + * @see this.selectClass for parameters + */ + select_mailing_list: function (previewMode, value) { + var self = this; + var def = wUtils.prompt({ + 'id': this.popup_template_id, + 'window_title': this.popup_title, + 'select': _t("Newsletter"), + 'init': function (field, dialog) { + return rpc.query({ + model: 'mailing.list', + method: 'name_search', + args: ['', [['is_public', '=', true]]], + context: self.options.recordInfo.context, + }).then(function (data) { + $(dialog).find('.btn-primary').prop('disabled', !data.length); + var list_id = self.$target.attr("data-list-id"); + $(dialog).on('show.bs.modal', function () { + if (list_id !== "0"){ + $(dialog).find('select').val(list_id); + }; + }); + return data; + }); + }, + }); + def.then(function (result) { + self.$target.attr("data-list-id", result.val); + }); + return def; + }, + /** + * @override + */ + onBuilt: function () { + var self = this; + this._super(); + this.select_mailing_list('click').guardedCatch(function () { + self.getParent()._onRemoveClick($.Event( "click" )); + }); + }, +}); + +options.registry.recaptchaSubscribe = options.Class.extend({ + xmlDependencies: ['/google_recaptcha/static/src/xml/recaptcha.xml'], + + /** + * Toggle the recaptcha legal terms + */ + toggleRecaptchaLegal: function (previewMode, value, params) { + const recaptchaLegalEl = this.$target[0].querySelector('.o_recaptcha_legal_terms'); + if (recaptchaLegalEl) { + recaptchaLegalEl.remove(); + } else { + const template = document.createElement('template'); + template.innerHTML = qweb.render("google_recaptcha.recaptcha_legal_terms"); + this.$target[0].appendChild(template.content.firstElementChild); + } + }, + + //---------------------------------------------------------------------- + // Private + //---------------------------------------------------------------------- + + /** + * @override + */ + _computeWidgetState: function (methodName, params) { + switch (methodName) { + case 'toggleRecaptchaLegal': + return !this.$target[0].querySelector('.o_recaptcha_legal_terms') || ''; + } + return this._super(...arguments); + }, +}); + +options.registry.newsletter_popup = options.registry.mailing_list_subscribe.extend({ + popup_template_id: "editor_new_mailing_list_subscribe_popup", + popup_title: _t("Add a Newsletter Subscribe Popup"), + + /** + * @override + */ + start: function () { + this.$target.on('hidden.bs.modal.newsletter_popup_option', () => { + this.trigger_up('snippet_option_visibility_update', {show: false}); + }); + return this._super(...arguments); + }, + /** + * @override + */ + onTargetShow: function () { + // Open the modal + this.$target.data('quick-open', true); + return this._refreshPublicWidgets(); + }, + /** + * @override + */ + onTargetHide: function () { + // Close the modal + const $modal = this.$('.modal'); + if ($modal.length && $modal.is('.modal_shown')) { + $modal.modal('hide'); + } + }, + /** + * @override + */ + cleanForSave: function () { + var self = this; + var content = this.$target.data('content'); + if (content) { + this.trigger_up('get_clean_html', { + $layout: $('<div/>').html(content), + callback: function (html) { + self.$target.data('content', html); + }, + }); + } + this._super.apply(this, arguments); + }, + /** + * @override + */ + destroy: function () { + this.$target.off('.newsletter_popup_option'); + this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Options + //-------------------------------------------------------------------------- + + /** + * @override + */ + select_mailing_list: function () { + var self = this; + return this._super.apply(this, arguments).then(function () { + self.$target.data('quick-open', true); + self.$target.removeData('content'); + return self._refreshPublicWidgets(); + }); + }, +}); + +WysiwygMultizone.include({ + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @override + */ + _saveElement: function (outerHTML, recordInfo, editable) { + var self = this; + var defs = [this._super.apply(this, arguments)]; + var $popups = $(editable).find('.o_newsletter_popup'); + _.each($popups, function (popup) { + var $popup = $(popup); + var content = $popup.data('content'); + if (content) { + defs.push(self._rpc({ + route: '/website_mass_mailing/set_content', + params: { + 'newsletter_id': parseInt($popup.attr('data-list-id')), + 'content': content, + }, + })); + } + }); + return Promise.all(defs); + }, +}); + +WysiwygTranslate.include({ + /** + * @override + */ + start: function () { + this.$target.on('click.newsletter_popup_option', '.o_edit_popup', function (ev) { + alert(_t('Website popups can only be translated through mailing list configuration in the Email Marketing app.')); + }); + this._super.apply(this, arguments); + }, +}); + +}); diff --git a/addons/website_mass_mailing/static/src/js/website_mass_mailing.js b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js new file mode 100644 index 00000000..8abb5052 --- /dev/null +++ b/addons/website_mass_mailing/static/src/js/website_mass_mailing.js @@ -0,0 +1,278 @@ +odoo.define('mass_mailing.website_integration', function (require) { +"use strict"; + +var config = require('web.config'); +var core = require('web.core'); +const dom = require('web.dom'); +var Dialog = require('web.Dialog'); +var utils = require('web.utils'); +var publicWidget = require('web.public.widget'); +const session = require('web.session'); + +// FIXME the 14.0 was released with this but without the google_recaptcha +// module being added as a dependency of the website_mass_mailing module. This +// is to be fixed in master of course but in stable, we'll have to use a +// workaround. +// const {ReCaptcha} = require('google_recaptcha.ReCaptchaV3'); + +var _t = core._t; + +publicWidget.registry.subscribe = publicWidget.Widget.extend({ + selector: ".js_subscribe", + disabledInEditableMode: false, + read_events: { + 'click .js_subscribe_btn': '_onSubscribeClick', + }, + + /** + * @constructor + */ + init: function () { + this._super(...arguments); + const ReCaptchaService = odoo.__DEBUG__.services['google_recaptcha.ReCaptchaV3']; + this._recaptcha = ReCaptchaService && new ReCaptchaService.ReCaptcha() || null; + }, + /** + * @override + */ + willStart: function () { + if (this._recaptcha) { + this._recaptcha.loadLibs(); + } + return this._super(...arguments); + }, + /** + * @override + */ + start: function () { + var self = this; + var def = this._super.apply(this, arguments); + + if (!this._recaptcha && this.editableMode && session.is_admin) { + this.displayNotification({ + type: 'info', + message: _t("Do you want to install Google reCAPTCHA to secure your newsletter subscriptions?"), + sticky: true, + buttons: [{text: _t("Install now"), primary: true, click: async () => { + dom.addButtonLoadingEffect($('.o_notification .btn-primary')[0]); + + const record = await this._rpc({ + model: 'ir.module.module', + method: 'search_read', + domain: [['name', '=', 'google_recaptcha']], + fields: ['id'], + limit: 1, + }); + await this._rpc({ + model: 'ir.module.module', + method: 'button_immediate_install', + args: [[record[0]['id']]], + }); + + this.displayNotification({ + type: 'info', + message: _t("Google reCAPTCHA is now installed! You can configure it from your website settings."), + sticky: true, + buttons: [{text: _t("Website settings"), primary: true, click: async () => { + window.open('/web#action=website.action_website_configuration', '_blank'); + }}], + }); + }}], + }); + } + + this.$popup = this.$target.closest('.o_newsletter_modal'); + if (this.$popup.length) { + // No need to check whether the user subscribed or not if the input + // is in a popup as the popup won't open if he did subscribe. + return def; + } + + var always = function (data) { + var isSubscriber = data.is_subscriber; + self.$('.js_subscribe_btn').prop('disabled', isSubscriber); + self.$('input.js_subscribe_email') + .val(data.email || "") + .prop('disabled', isSubscriber); + // Compat: remove d-none for DBs that have the button saved with it. + self.$target.removeClass('d-none'); + self.$('.js_subscribe_btn').toggleClass('d-none', !!isSubscriber); + self.$('.js_subscribed_btn').toggleClass('d-none', !isSubscriber); + }; + return Promise.all([def, this._rpc({ + route: '/website_mass_mailing/is_subscriber', + params: { + 'list_id': this.$target.data('list-id'), + }, + }).then(always).guardedCatch(always)]); + }, + + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * @private + */ + _onSubscribeClick: async function () { + var self = this; + var $email = this.$(".js_subscribe_email:visible"); + + if ($email.length && !$email.val().match(/.+@.+/)) { + this.$target.addClass('o_has_error').find('.form-control').addClass('is-invalid'); + return false; + } + this.$target.removeClass('o_has_error').find('.form-control').removeClass('is-invalid'); + let tokenObj = null; + if (this._recaptcha) { + tokenObj = await this._recaptcha.getToken('website_mass_mailing_subscribe'); + if (tokenObj.error) { + self.displayNotification({ + type: 'danger', + title: _t("Error"), + message: tokenObj.error, + sticky: true, + }); + return false; + } + } + const params = { + 'list_id': this.$target.data('list-id'), + 'email': $email.length ? $email.val() : false, + }; + if (this._recaptcha) { + params['recaptcha_token_response'] = tokenObj.token; + } + this._rpc({ + route: '/website_mass_mailing/subscribe', + params: params, + }).then(function (result) { + let toastType = result.toast_type; + if (toastType === 'success') { + self.$(".js_subscribe_btn").addClass('d-none'); + self.$(".js_subscribed_btn").removeClass('d-none'); + self.$('input.js_subscribe_email').prop('disabled', !!result); + if (self.$popup.length) { + self.$popup.modal('hide'); + } + } + self.displayNotification({ + type: toastType, + title: toastType === 'success' ? _t('Success') : _t('Error'), + message: result.toast_content, + sticky: true, + }); + }); + }, +}); + +publicWidget.registry.newsletter_popup = publicWidget.Widget.extend({ + selector: ".o_newsletter_popup", + disabledInEditableMode: false, + + /** + * @override + */ + start: function () { + var self = this; + var defs = [this._super.apply(this, arguments)]; + this.websiteID = this._getContext().website_id; + this.listID = parseInt(this.$target.attr('data-list-id')); + if (!this.listID || (utils.get_cookie(_.str.sprintf("newsletter-popup-%s-%s", this.listID, this.websiteID)) && !self.editableMode)) { + return Promise.all(defs); + } + if (this.$target.data('content') && this.editableMode) { + // To avoid losing user changes. + this._dialogInit(this.$target.data('content')); + this.$target.removeData('quick-open'); + this.massMailingPopup.open(); + } else { + defs.push(this._rpc({ + route: '/website_mass_mailing/get_content', + params: { + newsletter_id: self.listID, + }, + }).then(function (data) { + self._dialogInit(data.popup_content, data.email || ''); + if (!self.editableMode && !data.is_subscriber) { + if (config.device.isMobile) { + setTimeout(function () { + self._showBanner(); + }, 5000); + } else { + $(document).on('mouseleave.open_popup_event', self._showBanner.bind(self)); + } + } else { + $(document).off('mouseleave.open_popup_event'); + } + // show popup after choosing a newsletter + if (self.$target.data('quick-open')) { + self.massMailingPopup.open(); + self.$target.removeData('quick-open'); + } + })); + } + + return Promise.all(defs); + }, + /** + * @override + */ + destroy: function () { + if (this.massMailingPopup) { + this.massMailingPopup.close(); + } + this._super.apply(this, arguments); + }, + + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- + + /** + * @param {string} content + * @private + */ + _dialogInit: function (content, email) { + var self = this; + this.massMailingPopup = new Dialog(this, { + technical: false, + $content: $('<div/>').html(content), + $parentNode: this.$target, + backdrop: !this.editableMode, + dialogClass: 'p-0' + (this.editableMode ? ' oe_structure oe_empty' : ''), + renderFooter: false, + size: 'medium', + }); + this.massMailingPopup.opened().then(function () { + var $modal = self.massMailingPopup.$modal; + $modal.find('header button.close').on('mouseup', function (ev) { + ev.stopPropagation(); + }); + $modal.addClass('o_newsletter_modal'); + $modal.find('.oe_structure').attr('data-editor-message', _t('DRAG BUILDING BLOCKS HERE')); + $modal.find('.modal-dialog').addClass('modal-dialog-centered'); + $modal.find('.js_subscribe').data('list-id', self.listID) + .find('input.js_subscribe_email').val(email); + self.trigger_up('widgets_start_request', { + editableMode: self.editableMode, + $target: $modal, + }); + }); + this.massMailingPopup.on('closed', this, function () { + var $modal = self.massMailingPopup.$modal; + if ($modal) { // The dialog might have never been opened + self.$el.data('content', $modal.find('.modal-body').html()); + } + }); + }, + /** + * @private + */ + _showBanner: function () { + this.massMailingPopup.open(); + utils.set_cookie(_.str.sprintf("newsletter-popup-%s-%s", this.listID, this.websiteID), true); + $(document).off('mouseleave.open_popup_event'); + }, +}); +}); diff --git a/addons/website_mass_mailing/static/src/scss/website_mass_mailing_popup.scss b/addons/website_mass_mailing/static/src/scss/website_mass_mailing_popup.scss new file mode 100644 index 00000000..425d9e1a --- /dev/null +++ b/addons/website_mass_mailing/static/src/scss/website_mass_mailing_popup.scss @@ -0,0 +1,22 @@ +.o_newsletter_modal { + + .modal-header { + padding: 0; + border: none; + } + .modal-title { + display: none; + } + .close { + z-index: $zindex-modal; + @include o-position-absolute(0, 0); + width: $font-size-lg * 2; + height: $font-size-lg * 2; + line-height: $font-size-lg * 2; + margin: 0; + padding: 0; + @include o-bg-color(color-yiq(o-color('primary')), o-color('primary'), $with-extras: false); + box-shadow: $box-shadow-sm; + opacity: 1; + } +} diff --git a/addons/website_mass_mailing/static/src/xml/website_mass_mailing.xml b/addons/website_mass_mailing/static/src/xml/website_mass_mailing.xml new file mode 100644 index 00000000..9fd80fe5 --- /dev/null +++ b/addons/website_mass_mailing/static/src/xml/website_mass_mailing.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates id="template" xml:space="preserve"> +<t t-name="website_mass_mailing.edition.wrapper"> + <div class="modal fade show d-block o_newsletter_modal"> + <div role="dialog" class="modal-dialog modal-dialog-centered"> + <div class="modal-content"> + <header class="modal-header"> + <button type="button" class="close" aria-label="Close" tabindex="-1">×</button> + </header> + <div id="wrapper" class="modal-body p-0 oe_structure oe_empty"></div> + </div> + </div> + </div> +</t> +</templates> |
