Create a products widget in editor

[MAILPOET-1709]
This commit is contained in:
Pavel Dohnal
2019-03-14 14:45:02 +01:00
committed by M. Shull
parent 2a774e24b2
commit 1fd64330d2
13 changed files with 981 additions and 2 deletions

View File

@@ -118,7 +118,7 @@ App.on('before:start', function beforeAppStart(BeforeStartApp) {
BeforeStartApp.registerWidget({ BeforeStartApp.registerWidget({
name: 'footer', name: 'footer',
widgetView: Module.FooterWidgetView, widgetView: Module.FooterWidgetView,
priority: 99, priority: 100,
}); });
}); });

View File

@@ -118,7 +118,7 @@ App.on('before:start', function beforeAppStart(BeforeStartApp) {
BeforeStartApp.registerWidget({ BeforeStartApp.registerWidget({
name: 'header', name: 'header',
widgetView: Module.HeaderWidgetView, widgetView: Module.HeaderWidgetView,
priority: 98, priority: 99,
}); });
}); });

View File

@@ -0,0 +1,607 @@
/**
* Posts block.
*
* This block works slightly differently compared to any other block.
* The difference is that once the user changes settings of this block,
* it will be removed and replaced with other blocks. So this block will
* not appear in the final JSON structure, it serves only as a placeholder
* for posts, that will be comprised of container, image, button and text blocks
*
* This block depends on blocks.button and blocks.divider for block model and
* block settings view.
*/
import Backbone from 'backbone';
import Marionette from 'backbone.marionette';
import Radio from 'backbone.radio';
import _ from 'underscore';
import jQuery from 'jquery';
import MailPoet from 'mailpoet';
import App from 'newsletter_editor/App';
import CommunicationComponent from 'newsletter_editor/components/communication';
import BaseBlock from 'newsletter_editor/blocks/base';
import ButtonBlock from 'newsletter_editor/blocks/button';
import DividerBlock from 'newsletter_editor/blocks/divider';
import 'select2';
var Module = {};
var base = BaseBlock;
var PostsDisplayOptionsSettingsView;
var SinglePostSelectionSettingsView;
var EmptyPostSelectionSettingsView;
var PostSelectionSettingsView;
var PostsSelectionCollectionView;
Module.PostsBlockModel = base.BlockModel.extend({
stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
defaults: function productsModelDefaults() {
return this._getDefaults({
type: 'posts',
withLayout: true,
amount: '10',
offset: 0,
contentType: 'post', // 'post'|'page'|'mailpoet_page'
postStatus: 'publish', // 'draft'|'pending'|'private'|'publish'|'future'
terms: [], // List of category and tag objects
search: '', // Search keyword term
inclusionType: 'include', // 'include'|'exclude'
displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true
imageFullWidth: false, // true|false
titlePosition: 'abovePost', // 'abovePost'|'aboveExcerpt'
featuredImagePosition: 'centered', // 'centered'|'right'|'left'|'alternate'|'none'
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: 'Author:',
showCategories: 'no', // 'no'|'aboveText'|'belowText'
categoriesPrecededBy: 'Categories:',
readMoreType: 'link', // 'link'|'button'
readMoreText: 'Read more', // 'link'|'button'
readMoreButton: {
text: 'Read more',
url: '[postLink]',
},
sortBy: 'newest', // 'newest'|'oldest',
showDivider: true, // true|false
divider: {},
_selectedPosts: [],
_availablePosts: [],
_transformedPosts: new (App.getBlockTypeModel('container'))(),
}, App.getConfig().get('blockDefaults.posts'));
},
relations: function relations() {
return {
readMoreButton: App.getBlockTypeModel('button'),
divider: App.getBlockTypeModel('divider'),
_selectedPosts: Backbone.Collection,
_availablePosts: Backbone.Collection,
_transformedPosts: App.getBlockTypeModel('container'),
};
},
initialize: function initialize() {
var POST_REFRESH_DELAY_MS = 500;
var refreshAvailablePosts = _.debounce(
this.fetchAvailablePosts.bind(this),
POST_REFRESH_DELAY_MS
);
var refreshTransformedPosts = _.debounce(
this._refreshTransformedPosts.bind(this),
POST_REFRESH_DELAY_MS
);
// Attach Radio.Requests API primarily for highlighting
_.extend(this, Radio.Requests);
this.fetchAvailablePosts();
this.on('change', this._updateDefaults, this);
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', refreshAvailablePosts);
this.on('loadMorePosts', this._loadMorePosts, this);
this.listenTo(this.get('_selectedPosts'), 'add remove reset', refreshTransformedPosts);
this.on('change:displayType change:titleFormat change:featuredImagePosition change:titleAlignment change:titleIsLink change:imageFullWidth change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:showDivider change:titlePosition', refreshTransformedPosts);
this.listenTo(this.get('readMoreButton'), 'change', refreshTransformedPosts);
this.listenTo(this.get('divider'), 'change', refreshTransformedPosts);
this.on('insertSelectedPosts', this._insertSelectedPosts, this);
},
fetchAvailablePosts: function fetchAvailablePosts() {
var that = this;
this.set('offset', 0);
CommunicationComponent.getPosts(this.toJSON()).done(function getPostsDone(posts) {
that.get('_availablePosts').reset(posts);
that.get('_selectedPosts').reset(); // Empty out the collection
that.trigger('change:_availablePosts');
}).fail(function getPostsFail() {
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchAvailablePosts'));
});
},
_loadMorePosts: function loadMorePosts() {
var that = this;
var postCount = this.get('_availablePosts').length;
var nextOffset = this.get('offset') + Number(this.get('amount'));
if (postCount === 0 || postCount < nextOffset) {
// No more posts to load
return false;
}
this.set('offset', nextOffset);
this.trigger('loadingMorePosts');
CommunicationComponent.getPosts(this.toJSON()).done(function getPostsDone(posts) {
that.get('_availablePosts').add(posts);
that.trigger('change:_availablePosts');
}).fail(function getPostsFail() {
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchAvailablePosts'));
}).always(function getPostsAlways() {
that.trigger('morePostsLoaded');
});
return true;
},
_refreshTransformedPosts: function refreshTransformedPosts() {
var that = this;
var data = this.toJSON();
data.posts = this.get('_selectedPosts').pluck('ID');
if (data.posts.length === 0) {
this.get('_transformedPosts').get('blocks').reset();
return;
}
CommunicationComponent.getTransformedPosts(data).done(function getTransformedPostsDone(posts) {
that.get('_transformedPosts').get('blocks').reset(posts, { parse: true });
}).fail(function getTransformedPostsFail() {
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchRenderedPosts'));
});
},
_insertSelectedPosts: function insertSelectedPosts() {
var data = this.toJSON();
var index = this.collection.indexOf(this);
var collection = this.collection;
data.posts = this.get('_selectedPosts').pluck('ID');
if (data.posts.length === 0) return;
CommunicationComponent.getTransformedPosts(data).done(function getTransformedPostsDone(posts) {
collection.add(JSON.parse(JSON.stringify(posts)), { at: index });
}).fail(function getTransformedPostsFail() {
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchRenderedPosts'));
});
},
});
Module.PostsBlockView = base.BlockView.extend({
className: 'mailpoet_block mailpoet_posts_block mailpoet_droppable_block',
getTemplate: function getTemplate() { return window.templates.productsBlock; },
modelEvents: {}, // Forcefully disable all events
regions: _.extend({
postsRegion: '.mailpoet_posts_container',
}, base.BlockView.prototype.regions),
onDragSubstituteBy: function onDragSubstituteBy() { return Module.PostsWidgetView; },
initialize: function initialize() {
base.BlockView.prototype.initialize.apply(this, arguments);
this.toolsView = new Module.PostsBlockToolsView({ model: this.model });
this.model.reply('blockView', this.notifyAboutSelf, this);
},
onRender: function onRender() {
var ContainerView;
var renderOptions;
if (!this.getRegion('toolsRegion').hasView()) {
this.showChildView('toolsRegion', this.toolsView);
}
this.trigger('showSettings');
ContainerView = App.getBlockTypeView('container');
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay'),
};
this.showChildView('postsRegion', new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
},
notifyAboutSelf: function notifyAboutSelf() {
return this;
},
onBeforeDestroy: function onBeforeDestroy() {
this.model.stopReplying('blockView', this.notifyAboutSelf, this);
},
});
Module.PostsBlockToolsView = base.BlockToolsView.extend({
getSettingsView: function getSettingsView() { return Module.PostsBlockSettingsView; },
});
Module.PostsBlockSettingsView = base.BlockSettingsView.extend({
getTemplate: function getTemplate() { return window.templates.productsBlockSettings; },
regions: {
selectionRegion: '.mailpoet_settings_posts_selection',
displayOptionsRegion: '.mailpoet_settings_posts_display_options',
},
events: {
'click .mailpoet_settings_posts_show_display_options': 'switchToDisplayOptions',
'click .mailpoet_settings_posts_show_post_selection': 'switchToPostSelection',
'click .mailpoet_settings_posts_insert_selected': 'insertPosts',
},
templateContext: function templateContext() {
return {
model: this.model.toJSON(),
};
},
initialize: function initialize() {
this.model.trigger('startEditing');
this.selectionView = new PostSelectionSettingsView({ model: this.model });
this.displayOptionsView = new PostsDisplayOptionsSettingsView({ model: this.model });
},
onRender: function onRender() {
var that = this;
this.model.request('blockView');
this.showChildView('selectionRegion', this.selectionView);
this.showChildView('displayOptionsRegion', this.displayOptionsView);
MailPoet.Modal.panel({
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
onCancel: function onCancel() {
// Self destroy the block if the user closes settings modal
that.model.destroy();
},
});
// Inform child views that they have been attached to document
this.selectionView.triggerMethod('attach');
this.displayOptionsView.triggerMethod('attach');
},
switchToDisplayOptions: function switchToDisplayOptions() {
// Switch content view
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_closed');
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_closed');
// Switch controls
this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden');
this.$('.mailpoet_settings_posts_show_post_selection').removeClass('mailpoet_hidden');
},
switchToPostSelection: function switchToPostSelection() {
// Switch content view
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_closed');
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_closed');
// Switch controls
this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden');
this.$('.mailpoet_settings_posts_show_display_options').removeClass('mailpoet_hidden');
},
insertPosts: function insertPosts() {
this.model.trigger('insertSelectedPosts');
this.model.destroy();
this.close();
},
});
PostsSelectionCollectionView = Marionette.CollectionView.extend({
className: 'mailpoet_post_scroll_container',
childView: function childView() { return SinglePostSelectionSettingsView; },
emptyView: function emptyView() { return EmptyPostSelectionSettingsView; },
childViewOptions: function childViewOptions() {
return {
blockModel: this.blockModel,
};
},
initialize: function initialize(options) {
this.blockModel = options.blockModel;
},
events: {
scroll: 'onPostsScroll',
},
onPostsScroll: function onPostsScroll(event) {
var $postsBox = jQuery(event.target);
if ($postsBox.scrollTop() + $postsBox.innerHeight() >= $postsBox[0].scrollHeight) {
// Load more posts if scrolled to bottom
this.blockModel.trigger('loadMorePosts');
}
},
});
PostSelectionSettingsView = Marionette.View.extend({
getTemplate: function getTemplate() {
return window.templates.postSelectionProductsBlockSettings;
},
regions: {
posts: '.mailpoet_post_selection_container',
},
events: function events() {
return {
'change .mailpoet_settings_posts_content_type': _.partial(this.changeField, 'contentType'),
'change .mailpoet_posts_post_status': _.partial(this.changeField, 'postStatus'),
'input .mailpoet_posts_search_term': _.partial(this.changeField, 'search'),
};
},
modelEvents: {
'change:offset': function changeOffset(model, value) {
// Scroll posts view to top if settings are changed
if (value === 0) {
this.$('.mailpoet_post_scroll_container').scrollTop(0);
}
},
loadingMorePosts: function loadingMorePosts() {
this.$('.mailpoet_post_selection_loading').css('visibility', 'visible');
},
morePostsLoaded: function morePostsLoaded() {
this.$('.mailpoet_post_selection_loading').css('visibility', 'hidden');
},
},
templateContext: function templateContext() {
return {
model: this.model.toJSON(),
};
},
onRender: function onRender() {
var postsView;
// Dynamically update available post types
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
postsView = new PostsSelectionCollectionView({
collection: this.model.get('_availablePosts'),
blockModel: this.model,
});
this.showChildView('posts', postsView);
},
onAttach: function onAttach() {
var that = this;
this.$('.mailpoet_posts_categories_and_tags').select2({
multiple: true,
allowClear: true,
placeholder: MailPoet.I18n.t('categoriesAndTags'),
ajax: {
data: function data(params) {
return {
term: params.term,
page: params.page || 1,
};
},
transport: function transport(options, success, failure) {
var taxonomies;
var termsPromise;
var promise = CommunicationComponent.getTaxonomies(
that.model.get('contentType')
).then(function getTerms(tax) {
taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched
termsPromise = CommunicationComponent.getTerms({
search: options.data.term,
page: options.data.page,
taxonomies: _.keys(taxonomies),
}).then(function mapTerms(terms) {
return {
taxonomies: taxonomies,
terms: terms,
};
});
return termsPromise;
});
promise.then(success);
promise.fail(failure);
return promise;
},
processResults: function processResults(data) {
// Transform taxonomies and terms into select2 compatible format
return {
results: _.map(
data.terms,
function results(item) {
return _.defaults({
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
id: item.term_id,
}, item);
}
),
pagination: {
more: data.terms.length === 100,
},
};
},
},
}).on({
'select2:select': function select2Select(event) {
var terms = that.model.get('terms');
terms.add(event.params.data);
// Reset whole model in order for change events to propagate properly
that.model.set('terms', terms.toJSON());
},
'select2:unselect': function select2Unselect(event) {
var terms = that.model.get('terms');
terms.remove(event.params.data);
// Reset whole model in order for change events to propagate properly
that.model.set('terms', terms.toJSON());
},
}).trigger('change');
},
changeField: function changeField(field, event) {
this.model.set(field, jQuery(event.target).val());
},
_updateContentTypes: function updateContentTypes(postTypes) {
var select = this.$('.mailpoet_settings_posts_content_type');
var selectedValue = this.model.get('contentType');
select.find('option').remove();
_.each(postTypes, function postTypesMap(type) {
select.append(jQuery('<option>', {
value: type.name,
text: type.label,
}));
});
select.val(selectedValue);
},
});
EmptyPostSelectionSettingsView = Marionette.View.extend({
getTemplate: function getTemplate() { return window.templates.emptyPostProductsBlockSettings; },
});
SinglePostSelectionSettingsView = Marionette.View.extend({
getTemplate: function getTemplate() { return window.templates.singlePostProductsBlockSettings; },
events: function events() {
return {
'change .mailpoet_select_post_checkbox': 'postSelectionChange',
};
},
templateContext: function templateContext() {
return {
model: this.model.toJSON(),
index: this._index,
};
},
initialize: function initialize(options) {
this.blockModel = options.blockModel;
},
postSelectionChange: function postSelectionChange(event) {
var checkBox = jQuery(event.target);
var selectedPostsCollection = this.blockModel.get('_selectedPosts');
if (checkBox.prop('checked')) {
selectedPostsCollection.add(this.model);
} else {
selectedPostsCollection.remove(this.model);
}
},
});
PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
getTemplate: function getTemplate() {
return window.templates.displayOptionsProductsBlockSettings;
},
events: function events() {
return {
'click .mailpoet_posts_select_button': 'showButtonSettings',
'click .mailpoet_posts_select_divider': 'showDividerSettings',
'change .mailpoet_posts_read_more_type': 'changeReadMoreType',
'change .mailpoet_posts_display_type': 'changeDisplayType',
'change .mailpoet_posts_title_format': 'changeTitleFormat',
'change .mailpoet_posts_title_as_links': _.partial(this.changeBoolField, 'titleIsLink'),
'change .mailpoet_posts_show_divider': _.partial(this.changeBoolField, 'showDivider'),
'input .mailpoet_posts_show_amount': _.partial(this.changeField, 'amount'),
'change .mailpoet_posts_content_type': _.partial(this.changeField, 'contentType'),
'change .mailpoet_posts_include_or_exclude': _.partial(this.changeField, 'inclusionType'),
'change .mailpoet_posts_title_alignment': _.partial(this.changeField, 'titleAlignment'),
'change .mailpoet_posts_image_full_width': _.partial(this.changeBoolField, 'imageFullWidth'),
'change .mailpoet_posts_featured_image_position': _.partial(this.changeField, 'featuredImagePosition'),
'change .mailpoet_posts_show_author': _.partial(this.changeField, 'showAuthor'),
'input .mailpoet_posts_author_preceded_by': _.partial(this.changeField, 'authorPrecededBy'),
'change .mailpoet_posts_show_categories': _.partial(this.changeField, 'showCategories'),
'input .mailpoet_posts_categories': _.partial(this.changeField, 'categoriesPrecededBy'),
'input .mailpoet_posts_read_more_text': _.partial(this.changeField, 'readMoreText'),
'change .mailpoet_posts_sort_by': _.partial(this.changeField, 'sortBy'),
'change .mailpoet_automated_latest_content_title_position': _.partial(this.changeField, 'titlePosition'),
};
},
templateContext: function templateContext() {
return {
model: this.model.toJSON(),
};
},
showButtonSettings: function showButtonSettings() {
var buttonModule = ButtonBlock;
(new buttonModule.ButtonBlockSettingsView({
model: this.model.get('readMoreButton'),
renderOptions: {
displayFormat: 'subpanel',
hideLink: true,
hideApplyToAll: true,
},
})).render();
},
showDividerSettings: function showDividerSettings() {
var dividerModule = DividerBlock;
(new dividerModule.DividerBlockSettingsView({
model: this.model.get('divider'),
renderOptions: {
displayFormat: 'subpanel',
hideApplyToAll: true,
},
})).render();
},
changeReadMoreType: function changeReadMoreType(event) {
var value = jQuery(event.target).val();
if (value === 'link') {
this.$('.mailpoet_posts_read_more_text').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_select_button').addClass('mailpoet_hidden');
} else if (value === 'button') {
this.$('.mailpoet_posts_read_more_text').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_select_button').removeClass('mailpoet_hidden');
}
this.changeField('readMoreType', event);
},
changeDisplayType: function changeDisplayType(event) {
var value = jQuery(event.target).val();
if (value === 'titleOnly') {
this.$('.mailpoet_posts_title_as_list').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_full_width_option').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_separator').addClass('mailpoet_hidden');
} else {
this.$('.mailpoet_posts_title_as_list').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_full_width_option').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_image_separator').removeClass('mailpoet_hidden');
// Reset titleFormat if it was set to List when switching away from displayType=titleOnly
if (this.model.get('titleFormat') === 'ul') {
this.model.set('titleFormat', 'h1');
this.$('.mailpoet_posts_title_format').val(['h1']);
this.$('.mailpoet_posts_title_as_link').removeClass('mailpoet_hidden');
}
}
if (value === 'excerpt') {
this.$('.mailpoet_posts_featured_image_position_container').removeClass('mailpoet_hidden');
} else {
this.$('.mailpoet_posts_featured_image_position_container').addClass('mailpoet_hidden');
}
this.changeField('displayType', event);
},
changeTitleFormat: function changeTitleFormat(event) {
var value = jQuery(event.target).val();
if (value === 'ul') {
this.$('.mailpoet_posts_non_title_list_options').addClass('mailpoet_hidden');
this.model.set('titleIsLink', true);
this.$('.mailpoet_posts_title_as_link').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_links').val(['true']);
} else {
this.$('.mailpoet_posts_non_title_list_options').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_link').removeClass('mailpoet_hidden');
}
this.changeField('titleFormat', event);
},
});
Module.PostsWidgetView = base.WidgetView.extend({
className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block',
getTemplate: function getTemplate() { return window.templates.productsInsertion; },
behaviors: {
DraggableBehavior: {
cloneOriginal: true,
drop: function drop() {
return new Module.PostsBlockModel({}, { parse: true });
},
},
},
});
App.on('before:start', function beforeStartApp(BeforeStartApp) {
BeforeStartApp.registerBlockType('products', {
blockModel: Module.PostsBlockModel,
blockView: Module.PostsBlockView,
});
BeforeStartApp.registerWidget({
name: 'products',
widgetView: Module.PostsWidgetView,
priority: 98,
});
});
export default Module;

View File

@@ -45,4 +45,5 @@ import 'newsletter_editor/blocks/header.js'; // side effect - calls App.on()
import 'newsletter_editor/blocks/automatedLatestContent.js'; // side effect - calls App.on() import 'newsletter_editor/blocks/automatedLatestContent.js'; // side effect - calls App.on()
import 'newsletter_editor/blocks/automatedLatestContentLayout.js'; // side effect - calls App.on() import 'newsletter_editor/blocks/automatedLatestContentLayout.js'; // side effect - calls App.on()
import 'newsletter_editor/blocks/posts.js'; // side effect - calls App.on() import 'newsletter_editor/blocks/posts.js'; // side effect - calls App.on()
import 'newsletter_editor/blocks/products.js'; // side effect - calls App.on()
import 'newsletter_editor/blocks/social.js'; // side effect - calls App.on() import 'newsletter_editor/blocks/social.js'; // side effect - calls App.on()

View File

@@ -153,6 +153,34 @@
'newsletter_editor_template_posts_settings_single_post', 'newsletter_editor_template_posts_settings_single_post',
'newsletter/templates/blocks/posts/settingsSinglePost.hbs' 'newsletter/templates/blocks/posts/settingsSinglePost.hbs'
) %> ) %>
<%= partial(
'newsletter_editor_template_products_block',
'newsletter/templates/blocks/products/block.hbs'
) %>
<%= partial(
'newsletter_editor_template_products_widget',
'newsletter/templates/blocks/products/widget.hbs'
) %>
<%= partial(
'newsletter_editor_template_products_settings',
'newsletter/templates/blocks/products/settings.hbs'
) %>
<%= partial(
'newsletter_editor_template_products_settings_display_options',
'newsletter/templates/blocks/products/settingsDisplayOptions.hbs'
) %>
<%= partial(
'newsletter_editor_template_products_settings_selection',
'newsletter/templates/blocks/products/settingsSelection.hbs'
) %>
<%= partial(
'newsletter_editor_template_products_settings_selection_empty',
'newsletter/templates/blocks/products/settingsSelectionEmpty.hbs'
) %>
<%= partial(
'newsletter_editor_template_products_settings_single_post',
'newsletter/templates/blocks/products/settingsSinglePost.hbs'
) %>
<%= partial( <%= partial(
'newsletter_editor_template_social_block', 'newsletter_editor_template_social_block',
'newsletter/templates/blocks/social/block.hbs' 'newsletter/templates/blocks/social/block.hbs'
@@ -599,6 +627,28 @@
jQuery('#newsletter_editor_template_posts_settings_display_options').html() jQuery('#newsletter_editor_template_posts_settings_display_options').html()
), ),
productsBlock: Handlebars.compile(
jQuery('#newsletter_editor_template_products_block').html()
),
productsInsertion: Handlebars.compile(
jQuery('#newsletter_editor_template_products_widget').html()
),
productsBlockSettings: Handlebars.compile(
jQuery('#newsletter_editor_template_products_settings').html()
),
postSelectionProductsBlockSettings: Handlebars.compile(
jQuery('#newsletter_editor_template_products_settings_selection').html()
),
emptyPostProductsBlockSettings: Handlebars.compile(
jQuery('#newsletter_editor_template_products_settings_selection_empty').html()
),
singlePostProductsBlockSettings: Handlebars.compile(
jQuery('#newsletter_editor_template_products_settings_single_post').html()
),
displayOptionsProductsBlockSettings: Handlebars.compile(
jQuery('#newsletter_editor_template_products_settings_display_options').html()
),
textBlock: Handlebars.compile( textBlock: Handlebars.compile(
jQuery('#newsletter_editor_template_text_block').html() jQuery('#newsletter_editor_template_text_block').html()
), ),

View File

@@ -0,0 +1,3 @@
<div class="mailpoet_tools"></div>
<div class="mailpoet_posts_container"></div>
<div class="mailpoet_block_highlight"></div>

View File

@@ -0,0 +1,10 @@
<h3><%= __('Post selection') %></h3>
<div class="mailpoet_settings_posts_selection"></div>
<div class="mailpoet_settings_posts_display_options mailpoet_closed"></div>
<div class="mailpoet_settings_posts_controls">
<div class="mailpoet_form_field">
<a href="javascript:;" class="mailpoet_settings_posts_show_post_selection mailpoet_hidden"><%= __('Back to selection') %></a>
<a href="javascript:;" class="mailpoet_settings_posts_show_display_options"><%= __('Display options') %></a>
</div>
<input type="button" class="button button-primary mailpoet_settings_posts_insert_selected" value="<%= __('Insert selected') | escape('html_attr') %>" />
</div>

View File

@@ -0,0 +1,264 @@
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_display_type" class="mailpoet_posts_display_type" value="excerpt" {{#ifCond model.displayType '==' 'excerpt'}}CHECKED{{/ifCond}}/>
<%= __('Excerpt') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_display_type" class="mailpoet_posts_display_type" value="full" {{#ifCond model.displayType '==' 'full'}}CHECKED{{/ifCond}}/>
<%= __('Full post') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_display_type" class="mailpoet_posts_display_type" value="titleOnly" {{#ifCond model.displayType '==' 'titleOnly'}}CHECKED{{/ifCond}} />
<%= __('Title only') %>
</label>
</div>
</div>
<hr class="mailpoet_separator" />
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Title Format') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_format" class="mailpoet_posts_title_format" value="h1" {{#ifCond model.titleFormat '==' 'h1'}}CHECKED{{/ifCond}}/>
<%= __('Heading 1') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_format" class="mailpoet_posts_title_format" value="h2" {{#ifCond model.titleFormat '==' 'h2'}}CHECKED{{/ifCond}}/>
<%= __('Heading 2') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_format" class="mailpoet_posts_title_format" value="h3" {{#ifCond model.titleFormat '==' 'h3'}}CHECKED{{/ifCond}}/>
<%= __('Heading 3') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option mailpoet_posts_title_as_list {{#ifCond model.titleFormat '!=' 'titleOnly'}}mailpoet_hidden{{/ifCond}}">
<label>
<input type="radio" name="mailpoet_posts_title_format" class="mailpoet_posts_title_format" value="ul" {{#ifCond model.titleFormat '==' 'ul'}}CHECKED{{/ifCond}}/>
<%= __('Show as list') %>
</label>
</div>
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Title Alignment') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_alignment" class="mailpoet_posts_title_alignment" value="left" {{#ifCond model.titleAlignment '==' 'left'}}CHECKED{{/ifCond}} />
<%= __('Left') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_alignment" class="mailpoet_posts_title_alignment" value="center" {{#ifCond model.titleAlignment '==' 'center'}}CHECKED{{/ifCond}} />
<%= __('Center') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_alignment" class="mailpoet_posts_title_alignment" value="right" {{#ifCond model.titleAlignment '==' 'right'}}CHECKED{{/ifCond}} />
<%= __('Right') %>
</label>
</div>
</div>
<div class="mailpoet_form_field mailpoet_posts_title_as_link {{#ifCond model.titleFormat '===' 'ul'}}mailpoet_hidden{{/ifCond}}">
<div class="mailpoet_form_field_title"><%= __('Make the post title into a link') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_as_links" class="mailpoet_posts_title_as_links" value="true" {{#if model.titleIsLink}}CHECKED{{/if}}/>
<%= __('Yes') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_title_as_links" class="mailpoet_posts_title_as_links" value="false" {{#unless model.titleIsLink}}CHECKED{{/unless}}/>
<%= __('No') %>
</label>
</div>
</div>
<hr class="mailpoet_separator mailpoet_automated_latest_content_title_position_separator {{#ifCond model.displayType '!==' 'excerpt'}}mailpoet_hidden{{/ifCond}}" />
<div class="mailpoet_form_field mailpoet_automated_latest_content_title_position {{#ifCond model.displayType '!==' 'excerpt'}}mailpoet_hidden{{/ifCond}}">
<div class="mailpoet_form_field_title"><%= _x('Title position', 'Setting in the email designer to position the blog post title') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_automated_latest_content_title_position" class="mailpoet_automated_latest_content_title_position" value="abovePost" {{#ifCond model.titlePosition '!=' 'aboveExcerpt'}}CHECKED{{/ifCond}}/>
<%= _x('Above the post', 'Display the post title above the post block') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_automated_latest_content_title_position" class="mailpoet_automated_latest_content_title_position" value="aboveExcerpt" {{#ifCond model.titlePosition '==' 'aboveExcerpt'}}CHECKED{{/ifCond}}/>
<%= _x('Above the excerpt text', 'Display the post title above the post excerpt') %>
</label>
</div>
</div>
<hr class="mailpoet_separator mailpoet_posts_image_separator {{#ifCond model.displayType '===' 'titleOnly'}}mailpoet_hidden{{/ifCond}}" />
<div class="mailpoet_posts_non_title_list_options {{#ifCond model.displayType '==' 'titleOnly'}}{{#ifCond model.titleFormat '==' 'ul'}}mailpoet_hidden{{/ifCond}}{{/ifCond}}">
<div class="mailpoet_form_field mailpoet_posts_featured_image_position_container {{#ifCond model.displayType '!==' 'excerpt'}}mailpoet_hidden{{/ifCond}}">
<div class="mailpoet_form_field_title"><%= __('Featured image position') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_featured_image_position" class="mailpoet_posts_featured_image_position" value="centered" {{#ifCond model.featuredImagePosition '==' 'centered' }}CHECKED{{/ifCond}}/>
<%= __('Centered') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_featured_image_position" class="mailpoet_posts_featured_image_position" value="left" {{#ifCond model.featuredImagePosition '==' 'left' }}CHECKED{{/ifCond}}/>
<%= __('Left') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_featured_image_position" class="mailpoet_posts_featured_image_position" value="right" {{#ifCond model.featuredImagePosition '==' 'right' }}CHECKED{{/ifCond}}/>
<%= __('Right') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_featured_image_position" class="mailpoet_posts_featured_image_position" value="alternate" {{#ifCond model.featuredImagePosition '==' 'alternate' }}CHECKED{{/ifCond}}/>
<%= __('Alternate') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_featured_image_position" class="mailpoet_posts_featured_image_position" value="none" {{#ifCond model.featuredImagePosition '==' 'none' }}CHECKED{{/ifCond}}/>
<%= __('None') %>
</label>
</div>
</div>
<div class="mailpoet_form_field mailpoet_posts_image_full_width_option {{#ifCond model.displayType '==' 'titleOnly'}}mailpoet_hidden{{/ifCond}}">
<div class="mailpoet_form_field_title"><%= __('Image width') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="imageFullWidth" class="mailpoet_posts_image_full_width" value="true" {{#if model.imageFullWidth}}CHECKED{{/if}}/>
<%= __('Full width') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="imageFullWidth" class="mailpoet_posts_image_full_width" value="false" {{#unless model.imageFullWidth}}CHECKED{{/unless}}/>
<%= __('Padded') %>
</label>
</div>
</div>
<hr class="mailpoet_separator" />
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Show author') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_author" class="mailpoet_posts_show_author" value="no" {{#ifCond model.showAuthor '==' 'no'}}CHECKED{{/ifCond}}/>
<%= __('No') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_author" class="mailpoet_posts_show_author" value="aboveText" {{#ifCond model.showAuthor '==' 'aboveText'}}CHECKED{{/ifCond}}/>
<%= __('Above text') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_author" class="mailpoet_posts_show_author" value="belowText" {{#ifCond model.showAuthor '==' 'belowText'}}CHECKED{{/ifCond}}/>
<%= __('Below text') %><br />
</label>
</div>
<div class="mailpoet_form_field_title mailpoet_form_field_title_small"><%= __('Preceded by:') %></div>
<div class="mailpoet_form_field_input_option mailpoet_form_field_block">
<input type="text" class="mailpoet_input mailpoet_input_full mailpoet_posts_author_preceded_by" value="{{ model.authorPrecededBy }}" />
</div>
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Show categories') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_categories" class="mailpoet_posts_show_categories" value="no" {{#ifCond model.showCategories '==' 'no'}}CHECKED{{/ifCond}}/>
<%= __('No') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_categories" class="mailpoet_posts_show_categories" value="aboveText" {{#ifCond model.showCategories '==' 'aboveText'}}CHECKED{{/ifCond}}/>
<%= __('Above text') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_categories" class="mailpoet_posts_show_categories" value="belowText" {{#ifCond model.showCategories '==' 'belowText'}}CHECKED{{/ifCond}}/>
<%= __('Below text') %>
</label>
</div>
<div class="mailpoet_form_field_title mailpoet_form_field_title_small"><%= __('Preceded by:') %></div>
<div class="mailpoet_form_field_input_option mailpoet_form_field_block">
<input type="text" class="mailpoet_input mailpoet_input_full mailpoet_posts_categories" value="{{ model.categoriesPrecededBy }}" />
</div>
</div>
<hr class="mailpoet_separator" />
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title mailpoet_form_field_title_small"><%= __('"Read more" text') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_read_more_type" class="mailpoet_posts_read_more_type" value="link" {{#ifCond model.readMoreType '==' 'link'}}CHECKED{{/ifCond}}/>
<%= __('Link') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_read_more_type" class="mailpoet_posts_read_more_type" value="button" {{#ifCond model.readMoreType '==' 'button'}}CHECKED{{/ifCond}}/>
<%= __('Button') %>
</label>
</div>
<div class="mailpoet_form_field_input_option mailpoet_form_field_block">
<input type="text" class="mailpoet_input mailpoet_input_full mailpoet_posts_read_more_text {{#ifCond model.readMoreType '!=' 'link'}}mailpoet_hidden{{/ifCond}}" value="{{ model.readMoreText }}" />
</div>
<div class="mailpoet_form_field_input_option mailpoet_form_field_block">
<a href="javascript:;" class="mailpoet_posts_select_button {{#ifCond model.readMoreType '!=' 'button'}}mailpoet_hidden{{/ifCond}}"><%= __('Design a button') %></a>
</div>
</div>
<hr class="mailpoet_separator" />
</div>
<div class="mailpoet_posts_non_title_list_options {{#ifCond model.displayType '==' 'titleOnly'}}{{#ifCond model.titleFormat '==' 'ul'}}mailpoet_hidden{{/ifCond}}{{/ifCond}}">
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title mailpoet_form_field_title_small mailpoet_form_field_title_inline"><%= __('Show divider between posts') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_divider" class="mailpoet_posts_show_divider" value="true" {{#if model.showDivider}}CHECKED{{/if}}/>
<%= __('Yes') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_posts_show_divider"class="mailpoet_posts_show_divider" value="false" {{#unless model.showDivider}}CHECKED{{/unless}}/>
<%= __('No') %>
</label>
</div>
<div class="mailpoet_form_field_input_option">
<a href="javascript:;" class="mailpoet_posts_select_divider"><%= __('Select divider') %></a>
</div>
</div>
</div>

View File

@@ -0,0 +1,28 @@
<div class="mailpoet_settings_posts_selection_controls">
<div class="mailpoet_post_selection_filter_row">
<input type="text" name="" class="mailpoet_input mailpoet_input_full mailpoet_posts_search_term" value="{{model.search}}" placeholder="<%= __('Search...') %>" />
</div>
<div class="mailpoet_post_selection_filter_row"><select name="mailpoet_posts_content_type" class="mailpoet_select mailpoet_select_half_width mailpoet_settings_posts_content_type">
<option value="post" {{#ifCond model.contentType '==' 'post'}}SELECTED{{/ifCond}}><%= __('Posts') %></option>
<option value="page" {{#ifCond model.contentType '==' 'page'}}SELECTED{{/ifCond}}><%= __('Pages') %></option>
<option value="mailpoet_page" {{#ifCond model.contentType '==' 'mailpoet_page'}}SELECTED{{/ifCond}}><%= __('MailPoet pages') %></option>
</select><select class="mailpoet_select mailpoet_select_half_width mailpoet_posts_post_status">
<option value="publish" {{#ifCond model.postStatus '==' 'publish'}}SELECTED{{/ifCond}}><%= __('Published') %></option>
<option value="future" {{#ifCond model.postStatus '==' 'future'}}SELECTED{{/ifCond}}><%= __('Scheduled') %></option>
<option value="draft" {{#ifCond model.postStatus '==' 'draft'}}SELECTED{{/ifCond}}><%= __('Draft') %></option>
<option value="pending" {{#ifCond model.postStatus '==' 'pending'}}SELECTED{{/ifCond}}><%= __('Pending Review') %></option>
<option value="private" {{#ifCond model.postStatus '==' 'private'}}SELECTED{{/ifCond}}><%= __('Private') %></option>
</select></div>
<div class="mailpoet_post_selection_filter_row">
<select class="mailpoet_select mailpoet_posts_categories_and_tags" multiple="multiple">
{{#each model.terms}}
<option value="{{ id }}" selected="selected">{{ text }}</option>
{{/each}}
</select>
</div>
</div>
<div class="mailpoet_post_selection_container">
</div>
<div class="mailpoet_post_selection_loading" style="visibility: hidden;">
<%= __('Loading posts...') %>
</div>

View File

@@ -0,0 +1 @@
<%= __('No posts available') %>

View File

@@ -0,0 +1,6 @@
<div class="mailpoet_settings_posts_single_post">
<label>
<input id="mailpoet_select_post_{{ index }}" class="mailpoet_select_post_checkbox" type="checkbox" class="checkbox" value="" name="post_selection">
{{#ellipsis model.post_title 40 '...'}}{{/ellipsis}}
</label>
</div>

View File

@@ -0,0 +1,4 @@
<div class="mailpoet_widget_icon">
<%= source('newsletter/templates/svg/block-icons/product.svg') %>
</div>
<div class="mailpoet_widget_title"><%= __('Products') %></div>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg viewBox="320 237.161 844.61 844.73" xmlns="http://www.w3.org/2000/svg">
<path d="M712.312 495.688c1.823-3.419 2.506-6.838 2.278-10.94-.455-5.245-2.736-9.575-7.066-12.994-4.331-3.42-9.117-5.016-14.361-4.562-6.611.458-11.627 3.65-15.044 10.033-14.134 25.757-24.165 67.475-30.092 125.374-8.661-21.883-15.956-47.644-21.655-77.961-2.508-13.449-8.661-19.83-18.691-19.147-6.839.455-12.54 5.015-17.101 13.678l-49.921 95.058c-8.206-33.052-15.956-73.401-23.022-121.045-1.595-11.856-8.206-17.326-19.833-16.411-6.383.455-11.169 2.732-14.361 7.065-3.191 4.101-4.559 9.346-3.645 15.273 13.448 85.481 25.986 143.157 37.611 173.02 4.559 10.939 9.8 16.184 15.956 15.728 9.575-.684 20.972-13.904 34.421-39.665 7.067-14.589 18.009-36.473 32.827-65.65 12.309 43.084 29.178 75.453 50.378 97.111 5.927 6.154 12.081 8.889 18.009 8.433 5.244-.455 9.345-3.191 12.083-8.206 2.278-4.332 3.191-9.349 2.733-15.045-1.366-20.743.686-49.695 6.382-86.854 5.926-38.296 13.222-65.879 22.114-82.293zM984.263 564.531c.229-17.779-3.646-32.599-10.941-44.907-8.205-14.361-20.289-23.022-36.471-26.443-4.333-.912-8.436-1.367-12.312-1.367-21.885 0-39.663 11.396-53.569 34.193-11.855 19.376-17.78 40.804-17.78 64.284 0 17.552 3.646 32.596 10.94 45.135 8.207 14.36 20.289 23.022 36.473 26.442 4.331.913 8.434 1.369 12.311 1.369 22.111 0 39.892-11.397 53.57-34.194 11.852-19.605 17.779-41.032 17.779-64.512zM944.6 582.766v.002c-3.191 15.044-8.891 26.215-17.327 33.736-6.609 5.929-12.764 8.435-18.462 7.294-5.473-1.139-10.032-5.925-13.45-14.815-2.738-7.066-4.103-14.134-4.103-20.745 0-5.697.455-11.397 1.595-16.639 2.053-9.348 5.928-18.467 12.081-27.127 7.523-11.172 15.503-15.728 23.708-14.134 5.472 1.139 10.032 5.927 13.45 14.817 2.735 7.065 4.103 14.132 4.103 20.743 0 5.928-.455 11.625-1.595 16.868zM794.148 493.181c-4.33-.912-8.433-1.367-12.311-1.367-21.883 0-39.664 11.396-53.568 34.193-11.854 19.376-17.781 40.804-17.781 64.284 0 17.552 3.647 32.596 10.941 45.135 8.206 14.36 20.289 23.022 36.472 26.442 4.333.913 8.435 1.369 12.312 1.369 22.111 0 39.892-11.397 53.569-34.194 11.855-19.605 17.78-41.032 17.78-64.512 0-17.779-3.646-32.599-10.941-44.907-8.207-14.361-20.517-23.022-36.473-26.443zm7.522 89.585v.002c-3.192 15.044-8.893 26.215-17.325 33.736-6.61 5.929-12.766 8.435-18.465 7.294-5.471-1.139-10.029-5.925-13.451-14.815-2.733-7.066-4.1-14.134-4.1-20.745 0-5.697.454-11.397 1.594-16.639 2.053-9.348 5.928-18.467 12.083-27.127 7.522-11.172 15.501-15.728 23.706-14.134 5.472 1.139 10.032 5.927 13.45 14.817 2.737 7.065 4.103 14.132 4.103 20.743.229 5.928-.456 11.625-1.595 16.868z"/>
<path d="M 320.95 238.875 L 320.95 1080.766 L 882.21 1080.766 L 1162.84 800.135 L 1162.84 238.875 L 320.95 238.875 Z M 828.787 784.994 L 729.974 729.973 L 513.781 729.973 C 484.895 729.973 461.511 706.59 461.511 677.702 L 461.511 503.464 C 461.281 474.805 484.662 451.192 513.553 451.192 L 970.01 451.192 C 998.899 451.192 1022.282 474.576 1022.282 503.464 L 1022.282 677.701 C 1022.282 706.59 998.898 729.972 970.01 729.972 L 806.32 729.972 L 828.787 784.994 Z M 882.21 1010.608 L 882.21 800.135 L 1092.684 800.135 L 882.21 1010.608 Z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB