Add a method to bulk update ALC blocks in newsletter editor
This commit is contained in:
@ -13,6 +13,7 @@ define([
|
|||||||
'newsletter_editor/blocks/divider',
|
'newsletter_editor/blocks/divider',
|
||||||
'newsletter_editor/components/communication',
|
'newsletter_editor/components/communication',
|
||||||
'mailpoet',
|
'mailpoet',
|
||||||
|
'backbone.supermodel',
|
||||||
'underscore',
|
'underscore',
|
||||||
'jquery'
|
'jquery'
|
||||||
], function(
|
], function(
|
||||||
@ -22,6 +23,7 @@ define([
|
|||||||
DividerBlock,
|
DividerBlock,
|
||||||
CommunicationComponent,
|
CommunicationComponent,
|
||||||
MailPoet,
|
MailPoet,
|
||||||
|
SuperModel,
|
||||||
_,
|
_,
|
||||||
jQuery
|
jQuery
|
||||||
) {
|
) {
|
||||||
@ -31,6 +33,36 @@ define([
|
|||||||
var Module = {},
|
var Module = {},
|
||||||
base = BaseBlock;
|
base = BaseBlock;
|
||||||
|
|
||||||
|
Module.ALCSupervisor = SuperModel.extend({
|
||||||
|
initialize: function() {
|
||||||
|
this.listenTo(App.getChannel(), 'automatedLatestContentRefresh', this.refresh);
|
||||||
|
},
|
||||||
|
refresh: function() {
|
||||||
|
var models = App.findModels(function(model) {
|
||||||
|
return model.get('type') === 'automatedLatestContent';
|
||||||
|
}) || [];
|
||||||
|
|
||||||
|
if (models.length === 0) return;
|
||||||
|
var blocks = _.map(models, function(model) {
|
||||||
|
return model.toJSON();
|
||||||
|
});
|
||||||
|
|
||||||
|
CommunicationComponent.getBulkTransformedPosts({
|
||||||
|
blocks: blocks,
|
||||||
|
}).then(_.partial(this.refreshBlocks, models));
|
||||||
|
},
|
||||||
|
refreshBlocks: function(models, renderedBlocks) {
|
||||||
|
_.each(
|
||||||
|
_.zip(models, renderedBlocks),
|
||||||
|
function(args) {
|
||||||
|
var model = args[0],
|
||||||
|
contents = args[1];
|
||||||
|
model.trigger('refreshPosts', contents);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
Module.AutomatedLatestContentBlockModel = base.BlockModel.extend({
|
Module.AutomatedLatestContentBlockModel = base.BlockModel.extend({
|
||||||
stale: ['_container'],
|
stale: ['_container'],
|
||||||
defaults: function() {
|
defaults: function() {
|
||||||
@ -72,34 +104,32 @@ define([
|
|||||||
},
|
},
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
base.BlockView.prototype.initialize.apply(this, arguments);
|
base.BlockView.prototype.initialize.apply(this, arguments);
|
||||||
this.fetchPosts();
|
|
||||||
this.on('change:amount change:contentType change:terms change:inclusionType 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:sortBy change:showDivider', this._scheduleFetchPosts, this);
|
this.on('change:amount change:contentType change:terms change:inclusionType 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:sortBy change:showDivider', this._scheduleFetchPosts, this);
|
||||||
this.listenTo(this.get('readMoreButton'), 'change', this._scheduleFetchPosts);
|
this.listenTo(this.get('readMoreButton'), 'change', this._scheduleFetchPosts);
|
||||||
this.listenTo(this.get('divider'), 'change', this._scheduleFetchPosts);
|
this.listenTo(this.get('divider'), 'change', this._scheduleFetchPosts);
|
||||||
},
|
this.on('add remove update reset', function(model, collection, options) {
|
||||||
fetchPosts: function() {
|
App.getChannel().trigger('automatedLatestContentRefresh');
|
||||||
var that = this;
|
|
||||||
CommunicationComponent.getTransformedPosts(this.toJSON()).done(function(content) {
|
|
||||||
that.get('_container').get('blocks').reset(content, {parse: true});
|
|
||||||
that.trigger('postsChanged');
|
|
||||||
}).fail(function(error) {
|
|
||||||
MailPoet.Notice.error(MailPoet.I18n.t('failedToFetchRenderedPosts'));
|
|
||||||
});
|
});
|
||||||
|
this.on('refreshPosts', this.updatePosts, this);
|
||||||
|
},
|
||||||
|
updatePosts: function(posts) {
|
||||||
|
this.get('_container.blocks').reset(posts, {parse: true});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Batch more changes during a specific time, instead of fetching
|
* Batch more changes during a specific time, instead of fetching
|
||||||
* ALC posts on each model change
|
* ALC posts on each model change
|
||||||
*/
|
*/
|
||||||
_scheduleFetchPosts: function() {
|
_scheduleFetchPosts: function() {
|
||||||
var timeout = 500,
|
var TIMEOUT = 500,
|
||||||
that = this;
|
that = this;
|
||||||
if (this._fetchPostsTimer !== undefined) {
|
if (this._fetchPostsTimer !== undefined) {
|
||||||
clearTimeout(this._fetchPostsTimer);
|
clearTimeout(this._fetchPostsTimer);
|
||||||
}
|
}
|
||||||
this._fetchPostsTimer = setTimeout(function() {
|
this._fetchPostsTimer = setTimeout(function() {
|
||||||
that.fetchPosts();
|
//that.fetchPosts();
|
||||||
|
App.getChannel().trigger('automatedLatestContentRefresh');
|
||||||
that._fetchPostsTimer = undefined;
|
that._fetchPostsTimer = undefined;
|
||||||
}, timeout);
|
}, TIMEOUT);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -363,5 +393,10 @@ define([
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
App.on('start', function() {
|
||||||
|
App._ALCSupervisor = new Module.ALCSupervisor();
|
||||||
|
App._ALCSupervisor.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
return Module;
|
return Module;
|
||||||
});
|
});
|
||||||
|
@ -40,6 +40,9 @@ define([
|
|||||||
// Remove stale attributes from resulting JSON object
|
// Remove stale attributes from resulting JSON object
|
||||||
return _.omit(SuperModel.prototype.toJSON.call(this), this.stale);
|
return _.omit(SuperModel.prototype.toJSON.call(this), this.stale);
|
||||||
},
|
},
|
||||||
|
getChildren: function() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Module.BlockView = AugmentedView.extend({
|
Module.BlockView = AugmentedView.extend({
|
||||||
|
@ -65,6 +65,13 @@ define([
|
|||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
|
getChildren: function() {
|
||||||
|
var models = this.get('blocks').map(function(model, index, list) {
|
||||||
|
return [model, model.getChildren()];
|
||||||
|
});
|
||||||
|
|
||||||
|
return _.flatten(models);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
Module.ContainerBlockView = Marionette.CompositeView.extend({
|
Module.ContainerBlockView = Marionette.CompositeView.extend({
|
||||||
|
@ -62,6 +62,13 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Module.getBulkTransformedPosts = function(options) {
|
||||||
|
return Module._query({
|
||||||
|
action: 'getBulkTransformedPosts',
|
||||||
|
options: options,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
Module.saveNewsletter = function(options) {
|
Module.saveNewsletter = function(options) {
|
||||||
return MailPoet.Ajax.post({
|
return MailPoet.Ajax.post({
|
||||||
endpoint: 'newsletters',
|
endpoint: 'newsletters',
|
||||||
|
@ -60,6 +60,11 @@ define([
|
|||||||
return Module.newsletter;
|
return Module.newsletter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Module.findModels = function(predicate) {
|
||||||
|
var blocks = App._contentContainer.getChildren();
|
||||||
|
return _.filter(blocks, predicate);
|
||||||
|
};
|
||||||
|
|
||||||
App.on('before:start', function(options) {
|
App.on('before:start', function(options) {
|
||||||
// Expose block methods globally
|
// Expose block methods globally
|
||||||
App.registerBlockType = Module.registerBlockType;
|
App.registerBlockType = Module.registerBlockType;
|
||||||
@ -68,6 +73,7 @@ define([
|
|||||||
App.toJSON = Module.toJSON;
|
App.toJSON = Module.toJSON;
|
||||||
App.getBody = Module.getBody;
|
App.getBody = Module.getBody;
|
||||||
App.getNewsletter = Module.getNewsletter;
|
App.getNewsletter = Module.getNewsletter;
|
||||||
|
App.findModels = Module.findModels;
|
||||||
|
|
||||||
Module.newsletter = new Module.NewsletterModel(_.omit(_.clone(options.newsletter), ['body']));
|
Module.newsletter = new Module.NewsletterModel(_.omit(_.clone(options.newsletter), ['body']));
|
||||||
});
|
});
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -43,4 +43,22 @@ class AutomatedLatestContent {
|
|||||||
$posts = $this->ALC->getPosts($args);
|
$posts = $this->ALC->getPosts($args);
|
||||||
return $this->ALC->transformPosts($args, $posts);
|
return $this->ALC->transformPosts($args, $posts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBulkTransformedPosts($args) {
|
||||||
|
$alc = new \MailPoet\Newsletter\AutomatedLatestContent();
|
||||||
|
|
||||||
|
$used_posts = array();
|
||||||
|
$rendered_posts = array();
|
||||||
|
|
||||||
|
foreach ($args['blocks'] as $block) {
|
||||||
|
$posts = $alc->getPosts($block, $used_posts);
|
||||||
|
$rendered_posts[] = $alc->transformPosts($block, $posts);
|
||||||
|
|
||||||
|
foreach ($posts as $post) {
|
||||||
|
$used_posts[] = $post->ID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $rendered_posts;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,71 @@
|
|||||||
define([
|
define([
|
||||||
'newsletter_editor/App',
|
'newsletter_editor/App',
|
||||||
'newsletter_editor/blocks/automatedLatestContent',
|
'newsletter_editor/blocks/automatedLatestContent',
|
||||||
|
'newsletter_editor/blocks/container',
|
||||||
'amd-inject-loader!newsletter_editor/blocks/automatedLatestContent',
|
'amd-inject-loader!newsletter_editor/blocks/automatedLatestContent',
|
||||||
'newsletter_editor/components/communication',
|
'newsletter_editor/components/communication',
|
||||||
], function(EditorApplication, AutomatedLatestContentBlock, AutomatedLatestContentInjector, CommunicationComponent) {
|
], function(
|
||||||
|
EditorApplication,
|
||||||
|
AutomatedLatestContentBlock,
|
||||||
|
ContainerBlock,
|
||||||
|
AutomatedLatestContentInjector,
|
||||||
|
CommunicationComponent
|
||||||
|
) {
|
||||||
|
|
||||||
|
describe('Automated Latest Content Supervisor', function() {
|
||||||
|
var model;
|
||||||
|
beforeEach(function() {
|
||||||
|
model = new AutomatedLatestContentBlock.ALCSupervisor();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fetches posts in bulk from the server', function() {
|
||||||
|
global.stubChannel(EditorApplication);
|
||||||
|
EditorApplication.findModels = sinon.stub().returns([new Backbone.SuperModel()]);
|
||||||
|
|
||||||
|
var mock = sinon.mock({ getBulkTransformedPosts: function() {} })
|
||||||
|
.expects('getBulkTransformedPosts').once().returns(jQuery.Deferred());
|
||||||
|
|
||||||
|
var module = AutomatedLatestContentInjector({
|
||||||
|
'newsletter_editor/components/communication': {
|
||||||
|
getBulkTransformedPosts: mock
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var model = new module.ALCSupervisor();
|
||||||
|
model.refresh();
|
||||||
|
|
||||||
|
mock.verify();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('refreshes posts for given blocks', function() {
|
||||||
|
var block1 = new Backbone.SuperModel(),
|
||||||
|
block2 = new Backbone.SuperModel(),
|
||||||
|
postsSet1 = [
|
||||||
|
{ type: 'customTypeOne' },
|
||||||
|
],
|
||||||
|
postsSet2 = [
|
||||||
|
{ type: 'customTypeTwo' },
|
||||||
|
{ type: 'customTypeTwo' },
|
||||||
|
],
|
||||||
|
mock1 = sinon.mock(block1),
|
||||||
|
mock2 = sinon.mock(block2);
|
||||||
|
|
||||||
|
mock1.expects('trigger').once().withArgs('refreshPosts', postsSet1);
|
||||||
|
mock2.expects('trigger').once().withArgs('refreshPosts', postsSet2);
|
||||||
|
|
||||||
|
model.refreshBlocks([block1, block2], [postsSet1, postsSet2]);
|
||||||
|
|
||||||
|
mock1.verify();
|
||||||
|
mock2.verify();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Automated latest content', function () {
|
describe('Automated latest content', function () {
|
||||||
describe('model', function () {
|
describe('model', function () {
|
||||||
var model, module;
|
var model, module;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
module = AutomatedLatestContentInjector({
|
module = AutomatedLatestContentBlock;
|
||||||
'newsletter_editor/components/communication': {
|
|
||||||
getTransformedPosts: function() {
|
|
||||||
return jQuery.Deferred();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -28,6 +77,7 @@ define([
|
|||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
delete EditorApplication.getChannel;
|
delete EditorApplication.getChannel;
|
||||||
|
delete EditorApplication.getBlockTypeModel;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has automatedLatestContent type', function () {
|
it('has automatedLatestContent type', function () {
|
||||||
@ -192,19 +242,25 @@ define([
|
|||||||
expect(model.get('divider.styles.block.backgroundColor')).to.equal('#456789');
|
expect(model.get('divider.styles.block.backgroundColor')).to.equal('#456789');
|
||||||
expect(model.get('divider.styles.block.padding')).to.equal('38px');
|
expect(model.get('divider.styles.block.padding')).to.equal('38px');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('accepts displayable posts', function() {
|
||||||
|
EditorApplication.getBlockTypeModel = sinon.stub().returns(ContainerBlock.ContainerBlockModel);
|
||||||
|
var model = new (module.AutomatedLatestContentBlockModel)();
|
||||||
|
|
||||||
|
model.updatePosts([{
|
||||||
|
type: 'someCustomType',
|
||||||
|
}]);
|
||||||
|
|
||||||
|
expect(model.get('_container.blocks').size()).to.equal(1);
|
||||||
|
expect(model.get('_container.blocks').first().get('type')).to.equal('someCustomType');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('block view', function () {
|
describe('block view', function () {
|
||||||
var model, view, module;
|
var model, view, module;
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
module = AutomatedLatestContentInjector({
|
module = AutomatedLatestContentBlock;
|
||||||
'newsletter_editor/components/communication': {
|
|
||||||
getTransformedPosts: function() {
|
|
||||||
return jQuery.Deferred();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@ -232,9 +288,6 @@ define([
|
|||||||
before(function() {
|
before(function() {
|
||||||
module = AutomatedLatestContentInjector({
|
module = AutomatedLatestContentInjector({
|
||||||
'newsletter_editor/components/communication': {
|
'newsletter_editor/components/communication': {
|
||||||
getTransformedPosts: function() {
|
|
||||||
return jQuery.Deferred();
|
|
||||||
},
|
|
||||||
getPostTypes: function() {
|
getPostTypes: function() {
|
||||||
return jQuery.Deferred();
|
return jQuery.Deferred();
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('has a specified font family', function () {
|
it('has a specified font family', function () {
|
||||||
expect(view.$('.mailpoet_editor_button').css('font-family')).to.equal(model.get('styles.block.fontFamily'));
|
expect(view.$('.mailpoet_editor_button').css('font-family')).to.contain(model.get('styles.block.fontFamily'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('has a specified font size', function () {
|
it('has a specified font size', function () {
|
||||||
|
Reference in New Issue
Block a user