diff --git a/RoboFile.php b/RoboFile.php
index 7f314f0a04..43e6f73501 100644
--- a/RoboFile.php
+++ b/RoboFile.php
@@ -153,7 +153,7 @@ class RoboFile extends \Robo\Tasks {
return $this->_exec('./tasks/transifex_init.sh');
}
- function testUnit($opts=['file' => null, 'xml' => false, 'multisite' => false]) {
+ function testUnit(array $opts=['file' => null, 'xml' => false, 'multisite' => false, 'debug' => false]) {
$this->loadEnv();
$command = 'vendor/bin/codecept run unit -c codeception.unit.yml';
@@ -170,6 +170,10 @@ class RoboFile extends \Robo\Tasks {
$command .= ' --xml';
}
+ if($opts['debug']) {
+ $command .= ' --debug';
+ }
+
return $this->_exec($command);
}
diff --git a/assets/js/src/newsletter_editor/behaviors/MediaManagerBehavior.js b/assets/js/src/newsletter_editor/behaviors/MediaManagerBehavior.js
new file mode 100644
index 0000000000..1cd9e309fa
--- /dev/null
+++ b/assets/js/src/newsletter_editor/behaviors/MediaManagerBehavior.js
@@ -0,0 +1,313 @@
+/* eslint-disable func-names */
+/**
+ * Media manager behaviour
+ *
+ * Adds a media manager integration with the view
+ */
+define([
+ 'backbone.marionette',
+ 'underscore',
+ 'newsletter_editor/behaviors/BehaviorsLookup',
+ 'jquery'
+], function (Marionette, _, BehaviorsLookup, jQuery) {
+ var BL = BehaviorsLookup;
+
+ BL.MediaManagerBehavior = Marionette.Behavior.extend({
+ ui: {
+ 'select-image': '.mailpoet_field_image_select_image',
+ 'address-input': '.mailpoet_field_image_address'
+ },
+ events: {
+ 'click @ui.select-image': 'showMediaManager',
+ 'input @ui.address-input': 'changeAddress'
+ },
+ initialize: function () {
+ if (this.view.options.showImageManager) {
+ this.showMediaManager();
+ }
+ },
+ changeAddress: function (event) {
+ var src = jQuery(event.target).val();
+ var image = new Image();
+
+ if (!src && this.options.onSelect) {
+ this.view[this.options.onSelect]({
+ src: null,
+ width: null,
+ height: null
+ });
+ return;
+ }
+
+ image.onload = function () {
+ if (this.options.onSelect) {
+ this.view[this.options.onSelect]({
+ src: src,
+ width: image.naturalWidth + 'px',
+ height: image.naturalHeight + 'px'
+ });
+ }
+ }.bind(this);
+
+ image.src = src;
+ },
+ showMediaManager: function () {
+ var that = this;
+ var MediaManager;
+ var theFrame;
+ if (this._mediaManager) {
+ this._mediaManager.resetSelections();
+ this._mediaManager.open();
+ return;
+ }
+
+ MediaManager = window.wp.media.view.MediaFrame.Select.extend({
+
+ initialize: function () {
+ window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
+
+ _.defaults(this.options, {
+ multiple: true,
+ editing: false,
+ state: 'insert'
+ });
+
+ this.createSelection();
+ this.createStates();
+ this.bindHandlers();
+ this.createIframeStates();
+
+ // Hide title
+ this.$el.addClass('hide-title');
+ },
+
+ resetSelections: function () {
+ this.state().get('selection').reset();
+ },
+
+ createQuery: function (options) {
+ var query = window.wp.media.query(options);
+ return query;
+ },
+
+ createStates: function () {
+ var options = this.options;
+
+ // Add the default states.
+ this.states.add([
+ // Main states.
+ new window.wp.media.controller.Library({
+ id: 'insert',
+ title: 'Add images',
+ priority: 20,
+ toolbar: 'main-insert',
+ filterable: 'image',
+ library: this.createQuery(options.library),
+ multiple: options.multiple ? 'reset' : false,
+ editable: false,
+
+ // If the user isn't allowed to edit fields,
+ // can they still edit it locally?
+ allowLocalEdits: false,
+
+ // Show the attachment display settings.
+ displaySettings: false,
+ // Update user settings when users adjust the
+ // attachment display settings.
+ displayUserSettings: false
+ })
+ ]);
+
+ if (window.wp.media.view.settings.post.featuredImageId) {
+ this.states.add(new window.wp.media.controller.FeaturedImage());
+ }
+ },
+
+ bindHandlers: function () {
+ var handlers;
+ // from Select
+ this.on('router:create:browse', this.createRouter, this);
+ this.on('router:render:browse', this.browseRouter, this);
+ this.on('content:create:browse', this.browseContent, this);
+ this.on('content:render:upload', this.uploadContent, this);
+ this.on('toolbar:create:select', this.createSelectToolbar, this);
+
+ this.on('menu:create:gallery', this.createMenu, this);
+ this.on('toolbar:create:main-insert', this.createToolbar, this);
+ this.on('toolbar:create:main-gallery', this.createToolbar, this);
+ this.on('toolbar:create:main-embed', this.mainEmbedToolbar, this);
+
+ this.on('updateExcluded', this.browseContent, this);
+
+ handlers = {
+ content: {
+ embed: 'embedContent',
+ 'edit-selection': 'editSelectionContent'
+ },
+ toolbar: {
+ 'main-insert': 'mainInsertToolbar'
+ }
+ };
+
+ _.each(handlers, function (regionHandlers, region) {
+ _.each(regionHandlers, function (callback, handler) {
+ this.on(region + ':render:' + handler, this[callback], this);
+ }, this);
+ }, this);
+ },
+
+ uploadContent: function () {
+ window.wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
+ this.$el.addClass('hide-toolbar');
+ },
+
+ // Content
+ embedContent: function () {
+ var view = new window.wp.media.view.Embed({
+ controller: this,
+ model: this.state()
+ }).render();
+
+ this.content.set(view);
+ view.url.focus();
+ },
+
+ editSelectionContent: function () {
+ var state = this.state();
+ var selection = state.get('selection');
+ var view;
+
+ view = new window.wp.media.view.AttachmentsBrowser({
+ controller: this,
+ collection: selection,
+ selection: selection,
+ model: state,
+ sortable: true,
+ search: false,
+ dragInfo: true,
+
+ AttachmentView: window.wp.media.view.Attachment.EditSelection
+ }).render();
+
+ view.toolbar.set('backToLibrary', {
+ text: 'Return to library',
+ priority: -100,
+
+ click: function () {
+ this.controller.content.mode('browse');
+ }
+ });
+
+ // Browse our library of attachments.
+ this.content.set(view);
+ },
+
+ // Toolbars
+ selectionStatusToolbar: function (view) {
+ var editable = this.state().get('editable');
+
+ view.set('selection', new window.wp.media.view.Selection({
+ controller: this,
+ collection: this.state().get('selection'),
+ priority: -40,
+
+ // If the selection is editable, pass the callback to
+ // switch the content mode.
+ editable: editable && function () {
+ this.controller.content.mode('edit-selection');
+ }
+ }).render());
+ },
+
+ mainInsertToolbar: function (view) {
+ var controller = this;
+
+ this.selectionStatusToolbar(view);
+
+ view.set('insert', {
+ style: 'primary',
+ priority: 80,
+ text: 'Select Image',
+ requires: { selection: true },
+
+ click: function () {
+ var state = controller.state();
+ var selection = state.get('selection');
+
+ controller.close();
+ state.trigger('insert', selection).reset();
+ }
+ });
+ },
+
+ mainEmbedToolbar: function (toolbar) {
+ var tbar = toolbar;
+ tbar.view = new window.wp.media.view.Toolbar.Embed({
+ controller: this,
+ text: 'Add images'
+ });
+ }
+
+ });
+
+ theFrame = new MediaManager({
+ id: 'mailpoet-media-manager',
+ frame: 'select',
+ title: 'Select image',
+ editing: false,
+ multiple: false,
+ library: {
+ type: 'image'
+ },
+ displaySettings: false,
+ button: {
+ text: 'Select'
+ }
+ });
+ this._mediaManager = theFrame;
+
+ this._mediaManager.on('insert', function () {
+ // Append media manager image selections to Images tab
+ var selection = theFrame.state().get('selection');
+ selection.each(function (attachment) {
+ var sizes = attachment.get('sizes');
+ // Following advice from Becs, the target width should
+ // be a double of one column width to render well on
+ // retina screen devices
+ var targetImageWidth = 1320;
+
+ // Pick the width that is closest to target width
+ var increasingByWidthDifference = _.sortBy(
+ _.keys(sizes),
+ function (size) {
+ return Math.abs(targetImageWidth - sizes[size].width);
+ }
+ );
+ var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
+ var imagesOfBestWidth = _.filter(
+ _.values(sizes),
+ function (size) { return size.width === bestWidth; }
+ );
+
+ // Maximize the height if there are multiple images with same width
+ var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
+
+ if (that.options.onSelect) {
+ that.view[that.options.onSelect]({
+ height: mainSize.height + 'px',
+ width: mainSize.width + 'px',
+ src: mainSize.url,
+ alt: (attachment.get('alt') !== '' && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title')
+ });
+ }
+ });
+ });
+ this._mediaManager.open();
+ },
+ onBeforeDestroy: function () {
+ if (typeof this._mediaManager === 'object') {
+ this._mediaManager.remove();
+ }
+ }
+ });
+});
diff --git a/assets/js/src/newsletter_editor/blocks/container.js b/assets/js/src/newsletter_editor/blocks/container.js
index 0843fee878..0c86112b19 100644
--- a/assets/js/src/newsletter_editor/blocks/container.js
+++ b/assets/js/src/newsletter_editor/blocks/container.js
@@ -40,6 +40,10 @@ define([
return this._getDefaults({
type: 'container',
orientation: 'vertical',
+ image: {
+ src: null,
+ display: 'scale'
+ },
styles: {
block: {
backgroundColor: 'transparent'
@@ -239,16 +243,19 @@ define([
});
Module.ContainerBlockSettingsView = base.BlockSettingsView.extend({
+ behaviors: _.extend({}, base.BlockSettingsView.prototype.behaviors, {
+ MediaManagerBehavior: {
+ onSelect: 'onImageSelect'
+ }
+ }),
getTemplate: function () { return window.templates.containerBlockSettings; },
events: function () {
return {
'change .mailpoet_field_container_background_color': _.partial(this.changeColorField, 'styles.block.backgroundColor'),
- 'click .mailpoet_done_editing': 'close'
+ 'click .mailpoet_done_editing': 'close',
+ 'change .mailpoet_field_display_type': 'changeDisplayType'
};
},
- regions: {
- columnsSettingsRegion: '.mailpoet_container_columns_settings'
- },
initialize: function () {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
@@ -256,8 +263,14 @@ define([
collection: this.model.get('blocks')
});
},
- onRender: function () {
- this.showChildView('columnsSettingsRegion', this._columnsSettingsView);
+ changeDisplayType: function (event) {
+ this.model.get('image').set('display', event.target.value);
+ this.model.trigger('change');
+ },
+ onImageSelect: function (image) {
+ this.model.set('image.src', image.src);
+ this.model.trigger('change');
+ this.render();
}
});
diff --git a/assets/js/src/newsletter_editor/blocks/image.js b/assets/js/src/newsletter_editor/blocks/image.js
index 2b9c69660c..1a84011b4f 100644
--- a/assets/js/src/newsletter_editor/blocks/image.js
+++ b/assets/js/src/newsletter_editor/blocks/image.js
@@ -75,6 +75,11 @@ define([
});
Module.ImageBlockSettingsView = base.BlockSettingsView.extend({
+ behaviors: _.extend({}, base.BlockSettingsView.prototype.behaviors, {
+ MediaManagerBehavior: {
+ onSelect: 'onImageSelect'
+ }
+ }),
onRender: function () {
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-full-width'), {
tooltipId: 'tooltip-editor-full-width',
@@ -89,11 +94,9 @@ define([
events: function () {
return {
'input .mailpoet_field_image_link': _.partial(this.changeField, 'link'),
- 'input .mailpoet_field_image_address': 'changeAddress',
'input .mailpoet_field_image_alt_text': _.partial(this.changeField, 'alt'),
'change .mailpoet_field_image_full_width': _.partial(this.changeBoolCheckboxField, 'fullWidth'),
'change .mailpoet_field_image_alignment': _.partial(this.changeField, 'styles.block.textAlign'),
- 'click .mailpoet_field_image_select_another_image': 'showMediaManager',
'click .mailpoet_done_editing': 'close',
'input .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
'change .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
@@ -120,284 +123,14 @@ define([
this.$('.mailpoet_field_image_width').val(width);
this.$('.mailpoet_field_image_width_input').val(width);
},
- initialize: function (options) {
- base.BlockSettingsView.prototype.initialize.apply(this, arguments);
-
- if (options.showImageManager) {
- this.showMediaManager();
- }
- },
- showMediaManager: function () {
- var that = this;
- var MediaManager;
- var theFrame;
- if (this._mediaManager) {
- this._mediaManager.resetSelections();
- this._mediaManager.open();
- return;
- }
-
- MediaManager = window.wp.media.view.MediaFrame.Select.extend({
-
- initialize: function () {
- window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
-
- _.defaults(this.options, {
- multiple: true,
- editing: false,
- state: 'insert'
- });
-
- this.createSelection();
- this.createStates();
- this.bindHandlers();
- this.createIframeStates();
-
- // Hide title
- this.$el.addClass('hide-title');
- },
-
- resetSelections: function () {
- this.state().get('selection').reset();
- },
-
- createQuery: function (options) {
- var query = window.wp.media.query(options);
- return query;
- },
-
- createStates: function () {
- var options = this.options;
-
- // Add the default states.
- this.states.add([
- // Main states.
- new window.wp.media.controller.Library({
- id: 'insert',
- title: 'Add images',
- priority: 20,
- toolbar: 'main-insert',
- filterable: 'image',
- library: this.createQuery(options.library),
- multiple: options.multiple ? 'reset' : false,
- editable: false,
-
- // If the user isn't allowed to edit fields,
- // can they still edit it locally?
- allowLocalEdits: false,
-
- // Show the attachment display settings.
- displaySettings: false,
- // Update user settings when users adjust the
- // attachment display settings.
- displayUserSettings: false
- })
- ]);
-
- if (window.wp.media.view.settings.post.featuredImageId) {
- this.states.add(new window.wp.media.controller.FeaturedImage());
- }
- },
-
- bindHandlers: function () {
- var handlers;
- // from Select
- this.on('router:create:browse', this.createRouter, this);
- this.on('router:render:browse', this.browseRouter, this);
- this.on('content:create:browse', this.browseContent, this);
- this.on('content:render:upload', this.uploadContent, this);
- this.on('toolbar:create:select', this.createSelectToolbar, this);
-
- this.on('menu:create:gallery', this.createMenu, this);
- this.on('toolbar:create:main-insert', this.createToolbar, this);
- this.on('toolbar:create:main-gallery', this.createToolbar, this);
- this.on('toolbar:create:main-embed', this.mainEmbedToolbar, this);
-
- this.on('updateExcluded', this.browseContent, this);
-
- handlers = {
- content: {
- embed: 'embedContent',
- 'edit-selection': 'editSelectionContent'
- },
- toolbar: {
- 'main-insert': 'mainInsertToolbar'
- }
- };
-
- _.each(handlers, function (regionHandlers, region) {
- _.each(regionHandlers, function (callback, handler) {
- this.on(region + ':render:' + handler, this[callback], this);
- }, this);
- }, this);
- },
-
- uploadContent: function () {
- window.wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
- this.$el.addClass('hide-toolbar');
- },
-
- // Content
- embedContent: function () {
- var view = new window.wp.media.view.Embed({
- controller: this,
- model: this.state()
- }).render();
-
- this.content.set(view);
- view.url.focus();
- },
-
- editSelectionContent: function () {
- var state = this.state();
- var selection = state.get('selection');
- var view;
-
- view = new window.wp.media.view.AttachmentsBrowser({
- controller: this,
- collection: selection,
- selection: selection,
- model: state,
- sortable: true,
- search: false,
- dragInfo: true,
-
- AttachmentView: window.wp.media.view.Attachment.EditSelection
- }).render();
-
- view.toolbar.set('backToLibrary', {
- text: 'Return to library',
- priority: -100,
-
- click: function () {
- this.controller.content.mode('browse');
- }
- });
-
- // Browse our library of attachments.
- this.content.set(view);
- },
-
- // Toolbars
- selectionStatusToolbar: function (view) {
- var editable = this.state().get('editable');
-
- view.set('selection', new window.wp.media.view.Selection({
- controller: this,
- collection: this.state().get('selection'),
- priority: -40,
-
- // If the selection is editable, pass the callback to
- // switch the content mode.
- editable: editable && function () {
- this.controller.content.mode('edit-selection');
- }
- }).render());
- },
-
- mainInsertToolbar: function (view) {
- var controller = this;
-
- this.selectionStatusToolbar(view);
-
- view.set('insert', {
- style: 'primary',
- priority: 80,
- text: 'Select Image',
- requires: { selection: true },
-
- click: function () {
- var state = controller.state();
- var selection = state.get('selection');
-
- controller.close();
- state.trigger('insert', selection).reset();
- }
- });
- },
-
- mainEmbedToolbar: function (toolbar) {
- var tbar = toolbar;
- tbar.view = new window.wp.media.view.Toolbar.Embed({
- controller: this,
- text: 'Add images'
- });
- }
-
- });
-
- theFrame = new MediaManager({
- id: 'mailpoet-media-manager',
- frame: 'select',
- title: 'Select image',
- editing: false,
- multiple: false,
- library: {
- type: 'image'
- },
- displaySettings: false,
- button: {
- text: 'Select'
- }
- });
- this._mediaManager = theFrame;
-
- this._mediaManager.on('insert', function () {
- // Append media manager image selections to Images tab
- var selection = theFrame.state().get('selection');
- selection.each(function (attachment) {
- var sizes = attachment.get('sizes');
- // Following advice from Becs, the target width should
- // be a double of one column width to render well on
- // retina screen devices
- var targetImageWidth = 1320;
-
- // Pick the width that is closest to target width
- var increasingByWidthDifference = _.sortBy(
- _.keys(sizes),
- function (size) { return Math.abs(targetImageWidth - sizes[size].width); }
- );
- var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
- var imagesOfBestWidth = _.filter(
- _.values(sizes),
- function (size) { return size.width === bestWidth; }
- );
-
- // Maximize the height if there are multiple images with same width
- var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
-
- that.model.set({
- height: mainSize.height + 'px',
- width: mainSize.width + 'px',
- src: mainSize.url,
- alt: (attachment.get('alt') !== '' && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title')
- });
- // Rerender settings view due to changes from outside of settings view
- that.render();
- });
- });
-
- this._mediaManager.open();
- },
- changeAddress: function (event) {
- var src = jQuery(event.target).val();
- var image = new Image();
-
- image.onload = function () {
- this.model.set({
- src: src,
- width: image.naturalWidth + 'px',
- height: image.naturalHeight + 'px'
- });
- }.bind(this);
-
- image.src = src;
- },
- onBeforeDestroy: function () {
- base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
- if (typeof this._mediaManager === 'object') {
- this._mediaManager.remove();
+ onImageSelect: function (image) {
+ if (image.src === null) {
+ this.model.set({ src: '' });
+ } else {
+ this.model.set(image);
}
+ // Rerender settings view due to changes from outside of settings view
+ this.render();
}
});
diff --git a/lib/Newsletter/Renderer/Columns/Renderer.php b/lib/Newsletter/Renderer/Columns/Renderer.php
index c44ef533cf..55aa72c8ed 100644
--- a/lib/Newsletter/Renderer/Columns/Renderer.php
+++ b/lib/Newsletter/Renderer/Columns/Renderer.php
@@ -2,14 +2,14 @@
namespace MailPoet\Newsletter\Renderer\Columns;
class Renderer {
- function render($column_styles, $columns_count, $columns_data) {
+ function render($column_styles, $column_image, $columns_count, $columns_data) {
$styles = $column_styles['block'];
$width = ColumnsHelper::columnWidth($columns_count);
$class = ColumnsHelper::columnClass($columns_count);
$alignment = ColumnsHelper::columnAlignment($columns_count);
$template = ($columns_count === 1) ?
- $this->getOneColumnTemplate($styles, $class) :
- $this->getMultipleColumnsTemplate($styles, $width, $alignment, $class);
+ $this->getOneColumnTemplate($styles, $column_image, $class) :
+ $this->getMultipleColumnsTemplate($styles, $column_image, $width, $alignment, $class);
$result = array_map(function($content) use ($template) {
return $template['content_start'] . $content . $template['content_end'];
}, $columns_data);
@@ -20,16 +20,16 @@ class Renderer {
return $result;
}
- function getOneColumnTemplate($styles, $class) {
- $background_color = $this->getBackgroundColor($styles);
+ function getOneColumnTemplate($styles, $image, $class) {
+ $background_css = $this->getBackgroundCss($styles, $image);
$template['content_start'] = '
-
+ |
-
+
';
$template['content_end'] = '
@@ -43,11 +43,11 @@ class Renderer {
return $template;
}
- function getMultipleColumnsTemplate($styles, $width, $alignment, $class) {
- $background_color = $this->getBackgroundColor($styles);
+ function getMultipleColumnsTemplate($styles, $image, $width, $alignment, $class) {
+ $background_css = $this->getBackgroundCss($styles, $image);
$template['container_start'] = '
-
+ |
@@ -78,11 +78,21 @@ class Renderer {
return $template;
}
- function getBackgroundColor($styles) {
- if(!isset($styles['backgroundColor'])) return false;
- $background_color = $styles['backgroundColor'];
- return ($background_color !== 'transparent') ?
- sprintf('background-color:%s!important;" bgcolor="%s', $background_color, $background_color) :
- false;
+ private function getBackgroundCss($styles, $image) {
+ if($image !== null && $image['src'] !== null) {
+ $background_color = isset($styles['backgroundColor']) && $styles['backgroundColor'] !== 'transparent' ? $styles['backgroundColor'] : '#ffffff';
+ $repeat = $image['display'] === 'tile' ? 'repeat' : 'no-repeat';
+ $size = $image['display'] === 'scale' ? 'cover' : 'contain';
+ return sprintf(
+ 'background: %s url(%s) %s center/%s;background-color: %s;background-image: url(%s);background-repeat: %s;background-position: center;background-size: %s;',
+ $background_color, $image['src'], $repeat, $size, $background_color, $image['src'], $repeat, $size
+ );
+ } else {
+ if(!isset($styles['backgroundColor'])) return false;
+ $background_color = $styles['backgroundColor'];
+ return ($background_color !== 'transparent') ?
+ sprintf('background-color:%s!important;" bgcolor="%s', $background_color, $background_color) :
+ false;
+ }
}
}
\ No newline at end of file
diff --git a/lib/Newsletter/Renderer/Renderer.php b/lib/Newsletter/Renderer/Renderer.php
index f9b051c9f6..65a98677e4 100644
--- a/lib/Newsletter/Renderer/Renderer.php
+++ b/lib/Newsletter/Renderer/Renderer.php
@@ -102,8 +102,10 @@ class Renderer {
$content_block,
$column_count
);
+ $content_block_image = isset($content_block['image'])?$content_block['image']:null;
return $_this->columns_renderer->render(
$content_block['styles'],
+ $content_block_image,
$column_count,
$column_data
);
diff --git a/tests/javascript/newsletter_editor/blocks/container.spec.js b/tests/javascript/newsletter_editor/blocks/container.spec.js
index ef0e7ebfe9..231c0dfab0 100644
--- a/tests/javascript/newsletter_editor/blocks/container.spec.js
+++ b/tests/javascript/newsletter_editor/blocks/container.spec.js
@@ -29,6 +29,10 @@ define([
expect(model.get('styles.block.backgroundColor')).to.match(/^(#[abcdef0-9]{6})|transparent$/);
});
+ it('has a image display style', function () {
+ expect(model.get('image.display')).to.equal('scale');
+ });
+
it('has a collection of blocks', function () {
expect(model.get('blocks')).to.be.instanceof(Backbone.Collection);
});
@@ -42,6 +46,10 @@ define([
block: {
backgroundColor: '#123456'
}
+ },
+ image: {
+ src: null,
+ display: 'scale'
}
}
}
@@ -49,6 +57,7 @@ define([
innerModel = new (ContainerBlock.ContainerBlockModel)();
expect(innerModel.get('styles.block.backgroundColor')).to.equal('#123456');
+ expect(innerModel.get('image.display')).to.equal('scale');
});
it('do not update blockDefaults.container when changed', function () {
@@ -130,7 +139,22 @@ define([
describe('once rendered', function () {
describe('on root level', function () {
- var model = new (ContainerBlock.ContainerBlockModel)();
+ var imageSrc = 'http://example.org/someNewImage.png';
+ var model = new (ContainerBlock.ContainerBlockModel)({
+ type: 'container',
+ orientation: 'vertical',
+ image: {
+ src: imageSrc,
+ display: 'scale',
+ width: 123,
+ height: 456
+ },
+ styles: {
+ block: {
+ backgroundColor: 'transparent'
+ }
+ }
+ });
var view;
beforeEach(function () {
@@ -159,6 +183,15 @@ define([
it('has a duplication tool', function () {
expect(view.$('.mailpoet_duplicate_block')).to.have.length(1);
});
+
+ it('has a background image set', function () {
+ var style = view.$('style').text();
+ expect(style).contains('.mailpoet_editor_view_' + view.cid);
+ expect(style).contains('background-color: #ffffff !important;');
+ expect(style).contains('background-image: url(http://example.org/someNewImage.png);');
+ expect(style).contains('background-position: center;');
+ expect(style).contains('background-size: cover;');
+ });
});
describe.skip('on non-root levels', function () {
@@ -211,11 +244,13 @@ define([
describe('once rendered', function () {
var model;
var view;
+ var newSrc = 'http://example.org/someNewImage.png';
beforeEach(function () {
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication);
model = new (ContainerBlock.ContainerBlockModel)();
view = new (ContainerBlock.ContainerBlockSettingsView)({ model: model });
+ view.render();
});
it('updates the model when background color changes', function () {
@@ -223,6 +258,23 @@ define([
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
});
+ it('updates the model background image display type changes', function () {
+ view.$('.mailpoet_field_display_type:nth(2)').attr('checked', true).change();
+ expect(model.get('image.display')).to.equal('tile');
+ });
+
+ it('updates the model when background image src changes', function () {
+ global.stubImage(123, 456);
+ view.$('.mailpoet_field_image_address').val(newSrc).trigger('input');
+ expect(model.get('image.src')).to.equal(newSrc);
+ });
+
+ it('updates the model when background image src is deleted', function () {
+ global.stubImage(123, 456);
+ view.$('.mailpoet_field_image_address').val('').trigger('input');
+ expect(model.get('image.src')).to.equal(null);
+ });
+
it.skip('closes the sidepanel after "Done" is clicked', function () {
var mock = sinon.mock().once();
global.MailPoet.Modal.cancel = mock;
diff --git a/tests/unit/Newsletter/RendererTest.php b/tests/unit/Newsletter/RendererTest.php
index a600fa2e3c..9c8405f180 100644
--- a/tests/unit/Newsletter/RendererTest.php
+++ b/tests/unit/Newsletter/RendererTest.php
@@ -59,6 +59,7 @@ class RendererTest extends \MailPoetTest {
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
+ null,
count($column_content),
$column_content)
);
@@ -81,6 +82,7 @@ class RendererTest extends \MailPoetTest {
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
+ null,
count($column_content),
$column_content)
);
@@ -104,6 +106,7 @@ class RendererTest extends \MailPoetTest {
$DOM = $this->DOM_parser->parseStr(
$this->column_renderer->render(
$column_styles,
+ null,
count($column_content),
$column_content)
);
@@ -113,6 +116,82 @@ class RendererTest extends \MailPoetTest {
expect($rendered_column_content)->equals($column_content);
}
+ function testItRendersScaledColumnBackgroundImage() {
+ $column_content = ['one'];
+ $column_styles = ['block' => ['backgroundColor' => "#999999"]];
+ $column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'scale', 'width' => '1000px', 'height' => '500px'];
+ $DOM = $this->DOM_parser->parseStr(
+ $this->column_renderer->render(
+ $column_styles,
+ $column_image,
+ count($column_content),
+ $column_content)
+ );
+ $column_css = $DOM('td.mailpoet_content')[0]->attr('style');
+ expect($column_css)->contains('background: #999999 url(https://example.com/image.jpg) no-repeat center/cover;');
+ expect($column_css)->contains('background-color: #999999;');
+ expect($column_css)->contains('background-image: url(https://example.com/image.jpg);');
+ expect($column_css)->contains('background-repeat: no-repeat;');
+ expect($column_css)->contains('background-position: center;');
+ expect($column_css)->contains('background-size: cover;');
+ }
+
+ function testItRendersFitColumnBackgroundImage() {
+ $column_content = ['one'];
+ $column_styles = ['block' => ['backgroundColor' => "#999999"]];
+ $column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'fit', 'width' => '1000px', 'height' => '500px'];
+ $DOM = $this->DOM_parser->parseStr(
+ $this->column_renderer->render(
+ $column_styles,
+ $column_image,
+ count($column_content),
+ $column_content)
+ );
+ $column_css = $DOM('td.mailpoet_content')[0]->attr('style');
+ expect($column_css)->contains('background: #999999 url(https://example.com/image.jpg) no-repeat center/contain;');
+ expect($column_css)->contains('background-color: #999999;');
+ expect($column_css)->contains('background-image: url(https://example.com/image.jpg);');
+ expect($column_css)->contains('background-repeat: no-repeat;');
+ expect($column_css)->contains('background-position: center;');
+ expect($column_css)->contains('background-size: contain;');
+ }
+
+ function testItRendersTiledColumnBackgroundImage() {
+ $column_content = ['one'];
+ $column_styles = ['block' => ['backgroundColor' => "#999999"]];
+ $column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'tile', 'width' => '1000px', 'height' => '500px'];
+ $DOM = $this->DOM_parser->parseStr(
+ $this->column_renderer->render(
+ $column_styles,
+ $column_image,
+ count($column_content),
+ $column_content)
+ );
+ $column_css = $DOM('td.mailpoet_content')[0]->attr('style');
+ expect($column_css)->contains('background: #999999 url(https://example.com/image.jpg) repeat center/contain;');
+ expect($column_css)->contains('background-color: #999999;');
+ expect($column_css)->contains('background-image: url(https://example.com/image.jpg);');
+ expect($column_css)->contains('background-repeat: repeat;');
+ expect($column_css)->contains('background-position: center;');
+ expect($column_css)->contains('background-size: contain;');
+ }
+
+ function testItRendersFallbackColumnBackgroundColorForBackgroundImage() {
+ $column_content = ['one'];
+ $column_styles = ['block' => ['backgroundColor' => 'transparent']];
+ $column_image = ['src' => 'https://example.com/image.jpg', 'display' => 'tile', 'width' => '1000px', 'height' => '500px'];
+ $DOM = $this->DOM_parser->parseStr(
+ $this->column_renderer->render(
+ $column_styles,
+ $column_image,
+ count($column_content),
+ $column_content)
+ );
+ $column_css = $DOM('td.mailpoet_content')[0]->attr('style');
+ expect($column_css)->contains('background: #ffffff url(https://example.com/image.jpg) repeat center/contain;');
+ expect($column_css)->contains('background-color: #ffffff;');
+ }
+
function testItRendersHeader() {
$newsletter = $this->newsletter['body'];
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][0];
diff --git a/views/newsletter/editor.html b/views/newsletter/editor.html
index e38dd2c5b8..6339afe961 100644
--- a/views/newsletter/editor.html
+++ b/views/newsletter/editor.html
@@ -1113,6 +1113,10 @@
},
},
container: {
+ image: {
+ src: null,
+ display: 'scale',
+ },
styles: {
block: {
backgroundColor: 'transparent',
diff --git a/views/newsletter/templates/blocks/container/block.hbs b/views/newsletter/templates/blocks/container/block.hbs
index 61687e89d9..a13c2905f5 100644
--- a/views/newsletter/templates/blocks/container/block.hbs
+++ b/views/newsletter/templates/blocks/container/block.hbs
@@ -1,8 +1,22 @@
-{{#ifCond model.styles.block.backgroundColor '!=' 'transparent'}}
-
-{{/ifCond}}
+{{#if model.image.src}}
+
+{{else}}
+ {{#ifCond model.styles.block.backgroundColor '!=' 'transparent'}}
+
+ {{/ifCond}}
+{{/if}}
+
diff --git a/views/newsletter/templates/blocks/container/settings.hbs b/views/newsletter/templates/blocks/container/settings.hbs
index 1ecf80f3c8..256f66fcf0 100644
--- a/views/newsletter/templates/blocks/container/settings.hbs
+++ b/views/newsletter/templates/blocks/container/settings.hbs
@@ -4,11 +4,41 @@
- <%= __('Background') %>
+ <%= __('Background color') %>
-
-
+
+
+
+
+
| | |