Merge pull request #229 from mailpoet/editor_posts

Editor: Posts widget
This commit is contained in:
Marco
2015-11-19 10:31:31 +01:00
14 changed files with 101 additions and 49 deletions

View File

@ -19,6 +19,10 @@ $divider-hover-border-color = $primary-active-color
width: 100% width: 100%
border: 1px solid transparent border: 1px solid transparent
.mailpoet_active_divider_style
border: 1px solid $active-divider-border-color
background: $active-divider-background-color
.mailpoet_field_divider_style:hover .mailpoet_field_divider_style:hover
border: 1px solid $divider-hover-border-color border: 1px solid $divider-hover-border-color

View File

@ -1,15 +1,12 @@
.mailpoet_posts_block .mailpoet_posts_block
box-shadow(none) padding-left: 0
padding-right: 0
& > .mailpoet_content .mailpoet_posts_block_posts
font-size: 1em overflow: auto
text-align: center
background-color: $primary-active-color & > .mailpoet_block
margin: 20px 0 width: 100%
padding: 15px
box-shadow(inset 1px 2px 1px $primary-inset-shadow-color)
color: $white-color
border-radius(3px)
.mailpoet_post_selection_filter_row .mailpoet_post_selection_filter_row
margin-top: 5px margin-top: 5px

View File

@ -214,6 +214,7 @@ define([
this.model.set(field, value); this.model.set(field, value);
}, },
onBeforeDestroy: function() { onBeforeDestroy: function() {
console.log('Calling close');
MailPoet.Modal.close(); MailPoet.Modal.close();
}, },
}); });

View File

@ -31,7 +31,7 @@ define([
base = BaseBlock; base = BaseBlock;
Module.PostsBlockModel = base.BlockModel.extend({ Module.PostsBlockModel = base.BlockModel.extend({
stale: ['_selectedPosts', '_availablePosts'], stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
defaults: function() { defaults: function() {
return this._getDefaults({ return this._getDefaults({
type: 'posts', type: 'posts',
@ -63,6 +63,7 @@ define([
divider: {}, divider: {},
_selectedPosts: [], _selectedPosts: [],
_availablePosts: [], _availablePosts: [],
_transformedPosts: new (App.getBlockTypeModel('container'))(),
}, App.getConfig().get('blockDefaults.posts')); }, App.getConfig().get('blockDefaults.posts'));
}, },
relations: function() { relations: function() {
@ -71,15 +72,26 @@ define([
divider: App.getBlockTypeModel('divider'), divider: App.getBlockTypeModel('divider'),
_selectedPosts: Backbone.Collection, _selectedPosts: Backbone.Collection,
_availablePosts: Backbone.Collection, _availablePosts: Backbone.Collection,
_transformedPosts: App.getBlockTypeModel('container'),
}; };
}, },
initialize: function() { initialize: function() {
var that = this; var that = this,
POST_REFRESH_DELAY_MS = 500,
refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS),
refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
// Attach Radio.Requests API primarily for highlighting // Attach Radio.Requests API primarily for highlighting
_.extend(this, Radio.Requests); _.extend(this, Radio.Requests);
this.fetchAvailablePosts(); this.fetchAvailablePosts();
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', this._scheduleFetchAvailablePosts, this); this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', refreshAvailablePosts);
this.listenTo(this.get('_selectedPosts'), 'add remove reset', refreshTransformedPosts);
this.on('change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imagePadded change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:showDivider', refreshTransformedPosts);
this.listenTo(this.get('readMoreButton'), 'change', refreshTransformedPosts);
this.listenTo(this.get('divider'), 'change', refreshTransformedPosts);
this.on('insertSelectedPosts', this._insertSelectedPosts, this); this.on('insertSelectedPosts', this._insertSelectedPosts, this);
}, },
fetchAvailablePosts: function() { fetchAvailablePosts: function() {
@ -93,20 +105,23 @@ define([
console.log('Posts fetchPosts error', arguments); console.log('Posts fetchPosts error', arguments);
}); });
}, },
/** _refreshTransformedPosts: function() {
* Batch more changes during a specific time, instead of fetching var that = this,
* ALC posts on each model change data = this.toJSON();
*/
_scheduleFetchAvailablePosts: function() { data.posts = this.get('_selectedPosts').pluck('ID');
var timeout = 500,
that = this; if (data.posts.length === 0) {
if (this._fetchPostsTimer !== undefined) { this.get('_transformedPosts.blocks').reset();
clearTimeout(this._fetchPostsTimer); return;
} }
this._fetchPostsTimer = setTimeout(function() {
that.fetchAvailablePosts(); WordpressComponent.getTransformedPosts(data).done(function(posts) {
that._fetchPostsTimer = undefined; console.log('Transformed posts fetched', arguments);
}, timeout); that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
}).fail(function() {
console.log('Posts _refreshTransformedPosts error', arguments);
});
}, },
_insertSelectedPosts: function() { _insertSelectedPosts: function() {
var that = this, var that = this,
@ -131,6 +146,9 @@ define([
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block", className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
getTemplate: function() { return templates.postsBlock; }, getTemplate: function() { return templates.postsBlock; },
modelEvents: {}, modelEvents: {},
regions: _.extend({
postsRegion: '.mailpoet_posts_block_posts',
}, base.BlockView.prototype.regions),
onDragSubstituteBy: function() { return Module.PostsWidgetView; }, onDragSubstituteBy: function() { return Module.PostsWidgetView; },
initialize: function() { initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments); base.BlockView.prototype.initialize.apply(this, arguments);
@ -142,6 +160,13 @@ define([
this.toolsRegion.show(this.toolsView); this.toolsRegion.show(this.toolsView);
} }
this.trigger('showSettings'); this.trigger('showSettings');
var ContainerView = App.getBlockTypeView('container'),
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
};
this.postsRegion.show(new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
}, },
notifyAboutSelf: function() { notifyAboutSelf: function() {
return this; return this;
@ -216,6 +241,7 @@ define([
insertPosts: function() { insertPosts: function() {
this.model.trigger('insertSelectedPosts'); this.model.trigger('insertSelectedPosts');
this.model.destroy(); this.model.destroy();
this.close();
}, },
}); });
@ -307,11 +333,6 @@ define([
}, },
}).trigger( 'change' ); }).trigger( 'change' );
}, },
onBeforeDestroy: function() {
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
// Force close select2 if it hasn't closed yet
this.$('.mailpoet_posts_categories_and_tags').select2('close');
},
changeField: function(field, event) { changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val()); this.model.set(field, jQuery(event.target).val());
}, },

View File

@ -56,7 +56,7 @@ define([
}; };
Module.getTransformedPosts = function(options) { Module.getTransformedPosts = function(options) {
return Module._cachedQuery({ return Module._query({
action: 'getTransformedPosts', action: 'getTransformedPosts',
options: options, options: options,
}); });

View File

@ -50,9 +50,11 @@ class MetaInformationManager {
// check if the user specified a label to be displayed before the author's name // check if the user specified a label to be displayed before the author's name
if(strlen($preceded_by) > 0) { if(strlen($preceded_by) > 0) {
$content = stripslashes($preceded_by) . ' '; $content = stripslashes($preceded_by) . ' ';
} else {
$content = '';
} }
return join(', ', $categories); return $content . join(', ', $categories);
} else { } else {
return ''; return '';
} }

View File

@ -24,14 +24,24 @@ class PostTransformer {
$structure_transformer = new StructureTransformer(); $structure_transformer = new StructureTransformer();
$structure = $structure_transformer->transform($content, (bool)$this->args['imagePadded']); $structure = $structure_transformer->transform($content, (bool)$this->args['imagePadded']);
$structure = $this->appendFeaturedImage($post, (bool)$this->args['imagePadded'], $structure); $structure = $this->appendFeaturedImage(
$post,
$this->args['displayType'],
$this->args['imagePadded'] === 'true',
$structure
);
$structure = $this->appendPostTitle($post, $structure); $structure = $this->appendPostTitle($post, $structure);
$structure = $this->appendReadMore($post->ID, $structure); $structure = $this->appendReadMore($post->ID, $structure);
return $structure; return $structure;
} }
private function appendFeaturedImage($post, $image_padded, $structure) { private function appendFeaturedImage($post, $display_type, $image_padded, $structure) {
if ($display_type === 'full') {
// No featured images for full posts
return $structure;
}
$featured_image = $this->getFeaturedImage( $featured_image = $this->getFeaturedImage(
$post->ID, $post->ID,
$post->post_title, $post->post_title,
@ -68,7 +78,7 @@ class PostTransformer {
return array( return array(
'type' => 'image', 'type' => 'image',
'link' => '', 'link' => get_permalink($post_id),
'src' => $image_info[0], 'src' => $image_info[0],
'alt' => $alt_text, 'alt' => $alt_text,
'padded' => $image_padded, 'padded' => $image_padded,
@ -84,6 +94,8 @@ class PostTransformer {
} }
private function appendPostTitle($post, $structure) { private function appendPostTitle($post, $structure) {
$title = $this->getPostTitle($post);
if ($this->args['titlePosition'] === 'inTextBlock') { if ($this->args['titlePosition'] === 'inTextBlock') {
// Attach title to the first text block // Attach title to the first text block
$text_block_index = null; $text_block_index = null;
@ -94,7 +106,6 @@ class PostTransformer {
} }
} }
$title = $this->getPostTitle($post);
if ($text_block_index === null) { if ($text_block_index === null) {
$structure[] = array( $structure[] = array(
'type' => 'text', 'type' => 'text',
@ -103,6 +114,14 @@ class PostTransformer {
} else { } else {
$structure[$text_block_index]['text'] = $title . $structure[$text_block_index]['text']; $structure[$text_block_index]['text'] = $title . $structure[$text_block_index]['text'];
} }
} elseif ($this->args['titlePosition'] === 'aboveBlock') {
array_unshift(
$structure,
array(
'type' => 'text',
'text' => $title,
)
);
} }
return $structure; return $structure;
@ -113,6 +132,15 @@ class PostTransformer {
$button = $this->args['readMoreButton']; $button = $this->args['readMoreButton'];
$button['url'] = get_permalink($post_id); $button['url'] = get_permalink($post_id);
$structure[] = $button; $structure[] = $button;
} else {
$structure[] = array(
'type' => 'text',
'text' => sprintf(
'<a href="%s">%s</a>',
get_permalink($post_id),
$this->args['readMoreText']
),
);
} }
return $structure; return $structure;
@ -121,7 +149,7 @@ class PostTransformer {
private function getPostTitle($post) { private function getPostTitle($post) {
$title = $post->post_title; $title = $post->post_title;
if ((bool)$this->args['titleIsLink']) { if ($this->args['titleIsLink'] === 'true') {
$title = '<a href="' . get_permalink($post->ID) . '">' . $title . '</a>'; $title = '<a href="' . get_permalink($post->ID) . '">' . $title . '</a>';
} }

View File

@ -1,7 +1,7 @@
define([ define([
'newsletter_editor/App', 'newsletter_editor/App',
'newsletter_editor/components/wordpress', 'newsletter_editor/components/wordpress',
'newsletter_editor/blocks/posts', 'newsletter_editor/blocks/posts'
], function(EditorApplication, WordpressComponent, PostsBlock) { ], function(EditorApplication, WordpressComponent, PostsBlock) {
describe('Posts', function () { describe('Posts', function () {
@ -373,12 +373,7 @@ define([
}); });
describe('when "title as list" is selected', function() { describe('when "title as list" is selected', function() {
var model, view;
beforeEach(function() { beforeEach(function() {
model = new (PostsBlock.PostsBlockModel)();
model.request = sinon.stub().returns({$el: {}});
view = new (PostsBlock.PostsBlockSettingsView)({model: model});
view.render();
view.$('.mailpoet_posts_display_type').val('titleOnly').change(); view.$('.mailpoet_posts_display_type').val('titleOnly').change();
view.$('.mailpoet_posts_title_format').val('ul').change(); view.$('.mailpoet_posts_title_format').val('ul').change();
}); });

View File

@ -1,6 +1,6 @@
<div class="mailpoet_tools"></div> <div class="mailpoet_tools"></div>
<div class="mailpoet_content"> <div class="mailpoet_content">
<div class="mailpoet_automated_latest_content_block_overlay"></div> <div class="mailpoet_automated_latest_content_block_overlay"></div>
<div class="mailpoet_automated_latest_content_block_posts"></div> <div class="mailpoet_automated_latest_content_block_posts"></div>
</div> </div>
<div class="mailpoet_block_highlight"></div> <div class="mailpoet_block_highlight"></div>

View File

@ -215,7 +215,7 @@
<%= __('Below text') %> <%= __('Below text') %>
</label> </label>
</div> </div>
<div class="mailpoet_form_field_title mailpoet_form_field_title_small mailpoet_form_field_title_inline"><%= __('Categories:') %></div> <div class="mailpoet_form_field_title mailpoet_form_field_title_small mailpoet_form_field_title_inline"><%= __('Preceded by:') %></div>
<div class="mailpoet_form_field_input_option"> <div class="mailpoet_form_field_input_option">
<input type="text" class="mailpoet_input mailpoet_input_medium mailpoet_automated_latest_content_categories" value="{{ model.categoriesPrecededBy }}" /> <input type="text" class="mailpoet_input mailpoet_input_medium mailpoet_automated_latest_content_categories" value="{{ model.categoriesPrecededBy }}" />
</div> </div>

View File

@ -20,6 +20,7 @@
{{/ifCond}} {{/ifCond}}
<div class="mailpoet_form_field"> <div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Alignment') %></div>
<div class="mailpoet_form_field_radio_option"> <div class="mailpoet_form_field_radio_option">
<label> <label>
<input type="radio" name="alignment" class="mailpoet_field_button_alignment" value="left" {{#ifCond model.styles.block.textAlign '===' 'left'}}CHECKED{{/ifCond}}/> <input type="radio" name="alignment" class="mailpoet_field_button_alignment" value="left" {{#ifCond model.styles.block.textAlign '===' 'left'}}CHECKED{{/ifCond}}/>

View File

@ -1,5 +1,5 @@
<div class="mailpoet_tools"></div> <div class="mailpoet_tools"></div>
<div class="mailpoet_content"> <div class="mailpoet_content">
<%= __('Your posts will be inserted here') %> <div class="mailpoet_posts_block_posts"></div>
</div> </div>
<div class="mailpoet_block_highlight"></div> <div class="mailpoet_block_highlight"></div>

View File

@ -170,7 +170,7 @@
<%= __('Below text') %> <%= __('Below text') %>
</label> </label>
</div> </div>
<div class="mailpoet_form_field_title mailpoet_form_field_title_small mailpoet_form_field_title_inline"><%= __('Categories:') %></div> <div class="mailpoet_form_field_title mailpoet_form_field_title_small mailpoet_form_field_title_inline"><%= __('Preceded by:') %></div>
<div class="mailpoet_form_field_input_option"> <div class="mailpoet_form_field_input_option">
<input type="text" class="mailpoet_input mailpoet_input_medium mailpoet_posts_categories" value="{{ model.categoriesPrecededBy }}" /> <input type="text" class="mailpoet_input mailpoet_input_medium mailpoet_posts_categories" value="{{ model.categoriesPrecededBy }}" />
</div> </div>

View File

@ -13,6 +13,9 @@
<option value="pending"><%= __('Pending Review') %></option> <option value="pending"><%= __('Pending Review') %></option>
<option value="private"><%= __('Private') %></option> <option value="private"><%= __('Private') %></option>
</select></div> </select></div>
<div class="mailpoet_post_selection_filter_row">
<div class="mailpoet_form_field_title"><%= __('Categories & tags:') %></div>
</div>
<div class="mailpoet_post_selection_filter_row"> <div class="mailpoet_post_selection_filter_row">
<select class="mailpoet_select mailpoet_posts_categories_and_tags" multiple="multiple"> <select class="mailpoet_select mailpoet_posts_categories_and_tags" multiple="multiple">
{{#each terms}} {{#each terms}}