Compare commits

..

33 Commits
0.0.4 ... 0.0.5

Author SHA1 Message Date
ebca4257a6 Version bump: 0.0.5 2015-11-21 00:39:11 +01:00
c3944095bc Install npm deps in build command. 2015-11-21 00:36:12 +01:00
a1445d1b6a Vagrant Container and new build process. 2015-11-21 00:28:33 +01:00
e62c24879b Merge pull request #239 from mailpoet/revert-238-queue
Revert "Queue"
2015-11-20 23:51:19 +01:00
00f06ea202 Revert "Queue" 2015-11-20 23:51:02 +01:00
32ca24ce38 Merge pull request #238 from mailpoet/queue
Queue
2015-11-20 23:02:30 +01:00
44e3adb422 Merge pull request #235 from mailpoet/alc
Editor: ALC fixes
2015-11-20 23:00:47 +01:00
a1346ebecc Merge pull request #237 from mailpoet/changelog
welcome/update pages display logic
2015-11-20 23:00:30 +01:00
25b51d0446 - Adds queue management and supervisor. Issue #227 2015-11-20 16:20:54 -05:00
556a170903 - Bootstraps queue 2015-11-20 16:20:35 -05:00
7ac386a8e2 fixed welcome/update pages display logic 2015-11-20 13:37:52 +01:00
d91b55ec52 Include static sticky-kit library, patch it, fix sticky editor sidebar 2015-11-20 14:16:51 +02:00
786fbc36a2 Change post types to plural labels, move Posts/ALC UI elements 2015-11-19 19:08:48 +02:00
160e8b7a12 Merge pull request #232 from mailpoet/export-rename
Rename BootstrapMenu.php to BootStrapMenu.php
2015-11-19 15:01:24 +01:00
0b1f85da09 Rename BootstrapMenu.php to BootStrapMenu.php
- Renames BootStrapMenu class to use proper camelCase
2015-11-19 08:57:37 -05:00
fbc6f54ddc Merge pull request #230 from mailpoet/export
Export
2015-11-19 10:35:27 +01:00
a603e97b8c Merge pull request #229 from mailpoet/editor_posts
Editor: Posts widget
2015-11-19 10:31:31 +01:00
0875b627b6 Merge pull request #231 from mailpoet/newsletters_select_all_fix
fixed missing messages in newsletters listing
2015-11-19 10:27:45 +01:00
035784ece0 fixed missing messages in newsletters listing 2015-11-19 10:24:27 +01:00
aa93c7349f - Rebases master
- Adds tests for all Export class methods
Closes #221
2015-11-18 22:14:48 -05:00
82cf4a28fd - Updates export tests 2015-11-18 14:42:28 -05:00
832e5ef342 - Fixed minor import issue wrt to status not being set 2015-11-18 14:42:27 -05:00
e3de3a123a - Corrects exported subscriber count
- Properly exports subscribers not in any list
- Adds test for export class constructor method
Resolves #221
2015-11-18 14:42:27 -05:00
db91590159 Merge pull request #228 from mailpoet/welcome_redirect
Welcome page redirection
2015-11-18 14:33:48 +01:00
28c61fca0b Newsletters listing
- fixed listing actions on newsletters
2015-11-18 14:04:34 +01:00
e62a8c2ec5 Implement Becs' changes to Posts widget and post rendering 2015-11-18 14:44:57 +02:00
fdbd1245e3 Redirect to welcome or update page 2015-11-17 20:11:03 +01:00
0eef46db57 Fix post transformation to take titleIsLink option into account 2015-11-17 16:55:55 +02:00
080ae88a04 Fix Posts block tests 2015-11-17 16:31:57 +02:00
225be9f3cd Starting welcome page redirection 2015-11-17 13:43:25 +01:00
c9a42ebb76 Add auto-refresh of Posts block contents on option change 2015-11-17 14:40:39 +02:00
ae9b3df92d Merge pull request #223 from mailpoet/build
Fix JS lib inclusion in build step
2015-11-16 13:38:33 +01:00
63a08ebb55 Fix inclusion of js/lib/tinymce by disabling removal of node_modules/ 2015-11-16 14:27:29 +02:00
44 changed files with 6153 additions and 5255 deletions

3
.gitignore vendored
View File

@ -15,4 +15,5 @@ temp
wysija-newsletters.zip
tests/javascript/testBundles
assets/css/*.css
assets/js/*.js
assets/js/*.js
.vagrant

67
Vagrantfile vendored Normal file
View File

@ -0,0 +1,67 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.provider "virtualbox" do |v|
v.name = "phoenix"
v.memory = "2048"
end
config.vm.define :web do |web|
web.vm.box = "ubuntu/trusty64"
web.vm.hostname = "phoenix"
web.vm.network "forwarded_port", guest: 80, host: 8080
web.vm.synced_folder(
".",
"/var/www/html/wp-content/plugins/wysija-newsletters",
create: true,
owner: "vagrant",
group: "www-data"
)
web.vm.provision "shell", inline: <<-SHELL
sudo apt-get update
sudo apt-get install -y apache2 curl zip sendmail git build-essential
sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password password root'
sudo debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password root'
sudo apt-get install -y mysql-server-5.5
sudo apt-get install -y php5 libapache2-mod-php5 php5-curl php5-gd php5-mcrypt php5-readline mysql-server-5.5 php5-mysql php-apc
sudo sed -i "s/error_reporting = .*/error_reporting = E_ALL/" /etc/php5/apache2/php.ini
sudo sed -i "s/display_errors = .*/display_errors = On/" /etc/php5/apache2/php.ini
cd /var/www/html
sudo wget https://github.com/calvinlough/sqlbuddy/raw/gh-pages/sqlbuddy.zip -O /var/www/html/sqlbuddy.zip
sudo rm index.html
unzip sqlbuddy.zip
sudo curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
sudo chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
sudo wp core download --allow-root
mysql -uroot -proot -e "create database wordpress"
sudo wp core config --allow-root --dbname=wordpress --dbuser=root --dbpass=root
sudo wp core install --allow-root --url="http://localhost:8080" --title=WordPress --admin_user=admin --admin_password=password --admin_email=test@mailpoet-container.com
sudo sed -i "s/upload_max_filesize = .*/upload_max_filesize = 32M/" /etc/php5/apache2/php.ini
sudo sed -i "s/post_max_size = .*/post_max_size = 32M/" /etc/php5/apache2/php.ini
sudo chown -hR vagrant:www-data /var/www/html/
sudo a2enmod rewrite > /dev/null 2>&1
cd /var/www/html/wp-content/plugins/wysija-newsletters
curl -sS https://getcomposer.org/installer | php
sudo add-apt-repository -y ppa:chris-lea/node.js
sudo apt-get update
sudo apt-get install -y nodejs
sudo sed -i "s/export APACHE_RUN_USER.*/export APACHE_RUN_USER=vagrant/" /etc/apache2/envvars
sudo chown -R vagrant:www-data /var/lock/apache2
sudo service apache2 restart
SHELL
end
end

View File

@ -159,23 +159,6 @@ body.mailpoet_modal_opened
margin: 0
text-align: right
.mailpoet_button
padding: 3px 15px
border: 1px solid #444
font-weight: normal
cursor: pointer
background-color: #222
color: #cfcfcf
font-size: 1em
.mailpoet_button:hover
background-color: #00aacc
color: #fff
.mailpoet_button:active
background-color: #00ccff
color: #fff
@media screen and (max-width: 782px)
#mailpoet_modal_overlay.mailpoet_panel_overlay
top: 46px

View File

@ -19,6 +19,10 @@ $divider-hover-border-color = $primary-active-color
width: 100%
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
border: 1px solid $divider-hover-border-color

View File

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

View File

@ -306,7 +306,7 @@ define([
_.each(postTypes, function(type) {
select.append(jQuery('<option>', {
value: type.name,
text: type.labels.singular_name,
text: type.label,
}));
});
select.val(selectedValue);

View File

@ -31,7 +31,7 @@ define([
base = BaseBlock;
Module.PostsBlockModel = base.BlockModel.extend({
stale: ['_selectedPosts', '_availablePosts'],
stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
defaults: function() {
return this._getDefaults({
type: 'posts',
@ -63,6 +63,7 @@ define([
divider: {},
_selectedPosts: [],
_availablePosts: [],
_transformedPosts: new (App.getBlockTypeModel('container'))(),
}, App.getConfig().get('blockDefaults.posts'));
},
relations: function() {
@ -71,15 +72,26 @@ define([
divider: App.getBlockTypeModel('divider'),
_selectedPosts: Backbone.Collection,
_availablePosts: Backbone.Collection,
_transformedPosts: App.getBlockTypeModel('container'),
};
},
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
_.extend(this, Radio.Requests);
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);
},
fetchAvailablePosts: function() {
@ -93,20 +105,23 @@ define([
console.log('Posts fetchPosts error', arguments);
});
},
/**
* Batch more changes during a specific time, instead of fetching
* ALC posts on each model change
*/
_scheduleFetchAvailablePosts: function() {
var timeout = 500,
that = this;
if (this._fetchPostsTimer !== undefined) {
clearTimeout(this._fetchPostsTimer);
_refreshTransformedPosts: function() {
var that = this,
data = this.toJSON();
data.posts = this.get('_selectedPosts').pluck('ID');
if (data.posts.length === 0) {
this.get('_transformedPosts.blocks').reset();
return;
}
this._fetchPostsTimer = setTimeout(function() {
that.fetchAvailablePosts();
that._fetchPostsTimer = undefined;
}, timeout);
WordpressComponent.getTransformedPosts(data).done(function(posts) {
console.log('Transformed posts fetched', arguments);
that.get('_transformedPosts').get('blocks').reset(posts, {parse: true});
}).fail(function() {
console.log('Posts _refreshTransformedPosts error', arguments);
});
},
_insertSelectedPosts: function() {
var that = this,
@ -131,6 +146,9 @@ define([
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
getTemplate: function() { return templates.postsBlock; },
modelEvents: {},
regions: _.extend({
postsRegion: '.mailpoet_posts_block_posts',
}, base.BlockView.prototype.regions),
onDragSubstituteBy: function() { return Module.PostsWidgetView; },
initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments);
@ -142,6 +160,13 @@ define([
this.toolsRegion.show(this.toolsView);
}
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() {
return this;
@ -216,6 +241,7 @@ define([
insertPosts: function() {
this.model.trigger('insertSelectedPosts');
this.model.destroy();
this.close();
},
});
@ -307,11 +333,6 @@ define([
},
}).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) {
this.model.set(field, jQuery(event.target).val());
},
@ -323,7 +344,7 @@ define([
_.each(postTypes, function(type) {
select.append(jQuery('<option>', {
value: type.name,
text: type.labels.singular_name,
text: type.label,
}));
});
select.val(selectedValue);

View File

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

View File

@ -0,0 +1,264 @@
// Generated by CoffeeScript 1.9.2
/**
@license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
*/
(function() {
var $, win;
$ = this.jQuery || window.jQuery;
win = $(window);
$.fn.stick_in_parent = function(opts) {
var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
if (opts == null) {
opts = {};
}
sticky_class = opts.sticky_class, inner_scrolling = opts.inner_scrolling, recalc_every = opts.recalc_every, parent_selector = opts.parent, offset_top = opts.offset_top, manual_spacer = opts.spacer, enable_bottoming = opts.bottoming;
if (offset_top == null) {
offset_top = 0;
}
if (parent_selector == null) {
parent_selector = void 0;
}
if (inner_scrolling == null) {
inner_scrolling = true;
}
if (sticky_class == null) {
sticky_class = "is_stuck";
}
doc = $(document);
if (enable_bottoming == null) {
enable_bottoming = true;
}
outer_width = function(el) {
var _el, computed, w;
if (window.getComputedStyle) {
_el = el[0];
computed = window.getComputedStyle(el[0]);
w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
if (computed.getPropertyValue("box-sizing") !== "border-box") {
w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
}
return w;
} else {
return el.outerWidth(true);
}
};
fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
if (elm.data("sticky_kit")) {
return;
}
elm.data("sticky_kit", true);
last_scroll_height = doc.height();
parent = elm.parent();
if (parent_selector != null) {
parent = parent.closest(parent_selector);
}
if (!parent.length) {
throw "failed to find stick parent";
}
fixed = false;
bottomed = false;
spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
if (spacer) {
spacer.css('position', elm.css('position'));
}
recalc = function() {
var border_top, padding_top, restore;
if (detached) {
return;
}
last_scroll_height = doc.height();
border_top = parseInt(parent.css("border-top-width"), 10);
padding_top = parseInt(parent.css("padding-top"), 10);
padding_bottom = parseInt(parent.css("padding-bottom"), 10);
parent_top = parent.offset().top + border_top + padding_top;
parent_height = parent.height();
if (fixed) {
fixed = false;
bottomed = false;
if (manual_spacer == null) {
elm.insertAfter(spacer);
spacer.detach();
}
elm.css({
position: "",
top: "",
width: "",
bottom: ""
}).removeClass(sticky_class);
restore = true;
}
top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
height = elm.outerHeight(true);
el_float = elm.css("float");
if (spacer) {
spacer.css({
width: outer_width(elm),
height: height,
display: elm.css("display"),
"vertical-align": elm.css("vertical-align"),
"float": el_float
});
}
if (restore) {
return tick();
}
};
recalc();
last_pos = void 0;
offset = offset_top;
recalc_counter = recalc_every;
tick = function() {
var css, delta, recalced, scroll, will_bottom, win_height;
if (detached) {
return;
}
recalced = false;
if (recalc_counter != null) {
recalc_counter -= 1;
if (recalc_counter <= 0) {
recalc_counter = recalc_every;
recalc();
recalced = true;
}
}
if (!recalced && doc.height() !== last_scroll_height) {
recalc();
recalced = true;
}
scroll = win.scrollTop();
if (last_pos != null) {
delta = scroll - last_pos;
}
last_pos = scroll;
if (fixed) {
if (enable_bottoming) {
will_bottom = scroll + height + offset > parent_height + parent_top;
if (bottomed && !will_bottom) {
bottomed = false;
elm.css({
position: "fixed",
bottom: "",
top: offset
}).trigger("sticky_kit:unbottom");
}
}
if (scroll < top) {
fixed = false;
offset = offset_top;
if (manual_spacer == null) {
if (el_float === "left" || el_float === "right") {
elm.insertAfter(spacer);
}
spacer.detach();
}
css = {
position: "",
width: "",
top: ""
};
elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
}
if (inner_scrolling) {
win_height = win.height();
if (height + offset_top > win_height) {
if (!bottomed) {
offset -= delta;
offset = Math.max(win_height - height, offset);
offset = Math.min(offset_top, offset);
if (fixed) {
elm.css({
top: offset + "px"
});
}
}
}
}
} else {
if (scroll > top) {
fixed = true;
css = {
position: "fixed",
top: offset
};
css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
elm.css(css).addClass(sticky_class);
if (manual_spacer == null) {
elm.after(spacer);
if (el_float === "left" || el_float === "right") {
spacer.append(elm);
}
}
elm.trigger("sticky_kit:stick");
}
}
if (fixed && enable_bottoming) {
if (will_bottom == null) {
will_bottom = scroll + height + offset > parent_height + parent_top;
}
if (!bottomed && will_bottom) {
bottomed = true;
if (parent.css("position") === "static") {
parent.css({
position: "relative"
});
}
return elm.css({
position: "absolute",
bottom: padding_bottom,
top: "auto"
}).trigger("sticky_kit:bottom");
}
}
};
recalc_and_tick = function() {
recalc();
return tick();
};
detach = function() {
detached = true;
win.off("touchmove", tick);
win.off("scroll", tick);
win.off("resize", recalc_and_tick);
$(document.body).off("sticky_kit:recalc", recalc_and_tick);
elm.off("sticky_kit:detach", detach);
elm.removeData("sticky_kit");
elm.css({
position: "",
bottom: "",
top: "",
width: ""
});
parent.position("position", "");
if (fixed) {
if (manual_spacer == null) {
if (el_float === "left" || el_float === "right") {
elm.insertAfter(spacer);
}
spacer.remove();
}
return elm.removeClass(sticky_class);
}
};
win.on("touchmove", tick);
win.on("scroll", tick);
win.on("resize", recalc_and_tick);
$(document.body).on("sticky_kit:recalc", recalc_and_tick);
elm.on("sticky_kit:detach", detach);
return setTimeout(tick, 0);
};
for (i = 0, len = this.length; i < len; i++) {
elm = this[i];
fn($(elm));
}
return this;
};
}).call(this);

6
build
View File

@ -7,12 +7,10 @@ rm wysija-newsletters.zip;
mkdir wysija-newsletters;
# Production assets.
npm install;
./do compile:all;
# Production libraries.
rm -rf vendor;
rm -rf node_modules;
rm composer.lock;
./composer.phar install --no-dev;
# Copy release folders.
@ -38,6 +36,4 @@ zip -r wysija-newsletters.zip wysija-newsletters;
rm -rf wysija-newsletters;
# Reinstall dev dependencies.
rm composer.lock;
./composer.phar install;
./do install;

61
lib/Config/Changelog.php Normal file
View File

@ -0,0 +1,61 @@
<?php
namespace MailPoet\Config;
use \MailPoet\Models\Setting;
class Changelog {
function init() {
$doing_ajax = (bool)(defined('DOING_AJAX') && DOING_AJAX);
// don't run any check when it's an ajax request
if($doing_ajax) {
return;
}
// don't run any check when we're not on our pages
if(
!(isset($_GET['page']))
or
(isset($_GET['page']) && strpos($_GET['page'], 'mailpoet') !== 0)
) {
return;
}
add_action(
'admin_init',
array($this, 'check')
);
}
function check() {
$version = Setting::getValue('version', null);
$redirect_url = null;
if($version === null) {
// new install
$redirect_url = admin_url('admin.php?page=mailpoet-welcome');
} else if($version !== Env::$version) {
// update
$redirect_url = admin_url('admin.php?page=mailpoet-update');
}
if($redirect_url !== null) {
// save version number
Setting::setValue('version', Env::$version);
global $wp;
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
if($redirect_url !== $current_url) {
wp_safe_redirect(
add_query_arg(
array(
'mailpoet_redirect' => urlencode($current_url)
),
$redirect_url
)
);
exit;
}
}
}
}

View File

@ -6,6 +6,7 @@ if(!defined('ABSPATH')) exit;
class Env {
public static $version;
public static $plugin_name;
public static $plugin_url;
public static $file;
public static $path;
public static $views_path;
@ -29,9 +30,10 @@ class Env {
public static function init($file, $version) {
global $wpdb;
self::$version = $version;
self::$plugin_name = 'mailpoet';
self::$file = $file;
self::$path = dirname(self::$file);
self::$plugin_name = 'mailpoet';
self::$plugin_url = plugins_url() . '/' . basename(Env::$path);
self::$views_path = self::$path . '/views';
self::$assets_path = self::$path . '/assets';
self::$assets_url = plugins_url('/assets', $file);

View File

@ -24,6 +24,7 @@ class Initializer {
$this->setupWidget();
$this->setupAnalytics();
$this->setupPermissions();
$this->setupChangelog();
}
function setupDB() {
@ -104,4 +105,9 @@ class Initializer {
$permissions = new Permissions();
$permissions->init();
}
function setupChangelog() {
$changelog = new Changelog();
$changelog->init();
}
}

View File

@ -34,7 +34,7 @@ class Menu {
'MailPoet',
'manage_options',
'mailpoet',
array($this, 'welcome'),
array($this, 'home'),
$this->assets_url . '/img/menu_icon.png',
30
);
@ -94,31 +94,42 @@ class Menu {
'mailpoet-export',
array($this, 'export')
);
// add_submenu_page(
// 'mailpoet',
// __('Newsletter editor'),
// __('Newsletter editor'),
// 'manage_options',
// 'mailpoet-newsletter-editor',
// array($this, 'newletterEditor')
// );
$this->registered_pages();
}
function registered_pages() {
global $_registered_pages;
$pages = array(
'mailpoet-welcome' => array($this, 'welcome'),
'mailpoet-form-editor' => array($this, 'formEditor'),
'mailpoet-newsletter-editor' => array($this, 'newletterEditor')
add_submenu_page(
null,
__('Welcome'),
__('Welcome'),
'manage_options',
'mailpoet-welcome',
array($this, 'welcome')
);
add_submenu_page(
null,
__('Update'),
__('Update'),
'manage_options',
'mailpoet-update',
array($this, 'update')
);
add_submenu_page(
null,
__('Form editor'),
__('Form editor'),
'manage_options',
'mailpoet-form-editor',
array($this, 'formEditor')
);
add_submenu_page(
null,
__('Newsletter editor'),
__('Newsletter editor'),
'manage_options',
'mailpoet-newsletter-editor',
array($this, 'newletterEditor')
);
foreach($pages as $menu_slug => $callback) {
$hookname = get_plugin_page_hookname($menu_slug, null);
if(!empty($hookname)) {
add_action($hookname, $callback);
}
$_registered_pages[$hookname] = true;
}
}
function home() {
@ -127,12 +138,52 @@ class Menu {
}
function welcome() {
global $wp;
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
$redirect_url =
(!empty($_GET['mailpoet_redirect']))
? urldecode($_GET['mailpoet_redirect'])
: wp_get_referer();
if(
$redirect_url === $current_url
or
strpos($redirect_url, 'mailpoet') === false
) {
$redirect_url = admin_url('admin.php?page=mailpoet');
}
$data = array(
'settings' => Setting::getAll(),
'current_user' => wp_get_current_user()
'current_user' => wp_get_current_user(),
'redirect_url' => $redirect_url
);
echo $this->renderer->render('welcome.html', $data);
}
function update() {
global $wp;
$current_url = home_url(add_query_arg($wp->query_string, $wp->request));
$redirect_url =
(!empty($_GET['mailpoet_redirect']))
? urldecode($_GET['mailpoet_redirect'])
: wp_get_referer();
if(
$redirect_url === $current_url
or
strpos($redirect_url, 'mailpoet') === false
) {
$redirect_url = admin_url('admin.php?page=mailpoet');
}
$data = array(
'settings' => Setting::getAll(),
'current_user' => wp_get_current_user(),
'redirect_url' => $redirect_url
);
echo $this->renderer->render('welcome.html', $data);
echo $this->renderer->render('update.html', $data);
}
function settings() {

View File

@ -14,9 +14,17 @@ class Newsletter extends Model {
if(is_string($this->deleted_at) && strlen(trim($this->deleted_at)) === 0) {
$this->set_expr('deleted_at', 'NULL');
}
return parent::save();
}
function delete() {
// delete all relations to segments
NewsletterSegment::where('newsletter_id', $this->id)->deleteMany();
return parent::delete();
}
function segments() {
return $this->has_many_through(
__NAMESPACE__.'\Segment',
@ -126,8 +134,8 @@ class Newsletter extends Model {
static function createOrUpdate($data = array()) {
$newsletter = false;
if(isset($data['id']) && (int) $data['id'] > 0) {
$newsletter = self::findOne((int) $data['id']);
if(isset($data['id']) && (int)$data['id'] > 0) {
$newsletter = self::findOne((int)$data['id']);
}
if($newsletter === false) {

View File

@ -17,7 +17,7 @@ class Segment extends Model {
function delete() {
// delete all relations to subscribers
SubscriberSegment::where('segment_id', $this->id)->deleteMany();
parent::delete();
return parent::delete();
}
function newsletters() {

View File

@ -28,6 +28,13 @@ class Setting extends Model {
}
}
public static function setValue($key, $value) {
return Setting::createOrUpdate(array(
'name' => $key,
'value' => $value
));
}
public static function getAll() {
$settingsCollection = self::findMany();
$settings = array();

View File

@ -135,20 +135,41 @@ class Subscriber extends Model {
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END), NULL) as "' . $customField['name'].'"');
}
$orm = $orm
->left_outer_join(
->leftOuterJoin(
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
->left_outer_join(
->leftOuterJoin(
MP_CUSTOM_FIELDS_TABLE,
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'))
->group_by(MP_SUBSCRIBERS_TABLE.'.id');
->groupBy(MP_SUBSCRIBERS_TABLE.'.id');
return $orm;
}
static function filterWithCustomFieldsForExport($orm) {
$orm = $orm->select(MP_SUBSCRIBERS_TABLE.'.*');
$customFields = CustomField::findArray();
foreach ($customFields as $customField) {
$orm = $orm->selectExpr(
'CASE WHEN ' .
MP_CUSTOM_FIELDS_TABLE . '.id=' . $customField['id'] . ' THEN ' .
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE . '.value END as "' . $customField['name'].'"');
}
$orm = $orm
->leftOuterJoin(
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE,
array(MP_SUBSCRIBERS_TABLE.'.id', '=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.subscriber_id'))
->leftOuterJoin(
MP_CUSTOM_FIELDS_TABLE,
array(MP_CUSTOM_FIELDS_TABLE.'.id','=',
MP_SUBSCRIBER_CUSTOM_FIELD_TABLE.'.custom_field_id'));
return $orm;
}
function customFields() {
return $this->has_many_through(
return $this->hasManyThrough(
__NAMESPACE__.'\CustomField',
__NAMESPACE__.'\SubscriberCustomField',
'subscriber_id',

View File

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

View File

@ -14,7 +14,7 @@ class PostListTransformer {
function transform($posts) {
$results = array();
$use_divider = (bool)$this->args['showDivider'];
$use_divider = $this->args['showDivider'] === 'true';
foreach ($posts as $index => $post) {
if ($use_divider && $index > 0) {

View File

@ -22,16 +22,26 @@ class PostTransformer {
$content = $content_manager->filterContent($content);
$structure_transformer = new StructureTransformer();
$structure = $structure_transformer->transform($content, (bool)$this->args['imagePadded']);
$structure = $structure_transformer->transform($content, $this->args['imagePadded'] === 'true');
$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->appendReadMore($post->ID, $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(
$post->ID,
$post->post_title,
@ -68,7 +78,7 @@ class PostTransformer {
return array(
'type' => 'image',
'link' => '',
'link' => get_permalink($post_id),
'src' => $image_info[0],
'alt' => $alt_text,
'padded' => $image_padded,
@ -84,6 +94,8 @@ class PostTransformer {
}
private function appendPostTitle($post, $structure) {
$title = $this->getPostTitle($post);
if ($this->args['titlePosition'] === 'inTextBlock') {
// Attach title to the first text block
$text_block_index = null;
@ -94,7 +106,6 @@ class PostTransformer {
}
}
$title = $this->getPostTitle($post);
if ($text_block_index === null) {
$structure[] = array(
'type' => 'text',
@ -103,6 +114,14 @@ class PostTransformer {
} else {
$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;
@ -113,6 +132,15 @@ class PostTransformer {
$button = $this->args['readMoreButton'];
$button['url'] = get_permalink($post_id);
$structure[] = $button;
} else {
$structure[] = array(
'type' => 'text',
'text' => sprintf(
'<a href="%s">%s</a>',
get_permalink($post_id),
$this->args['readMoreText']
),
);
}
return $structure;
@ -121,7 +149,7 @@ class PostTransformer {
private function getPostTitle($post) {
$title = $post->post_title;
if ((bool)$this->args['titleIsLink']) {
if ($this->args['titleIsLink'] === 'true') {
$title = '<a href="' . get_permalink($post->ID) . '">' . $title . '</a>';
}

View File

@ -101,31 +101,51 @@ class Newsletters {
));
}
function delete($data = array()) {
$newsletter = newsletter::findOne($data['id']);
$confirm_delete = filter_var($data['confirm'], FILTER_VALIDATE_BOOLEAN);
function restore($id) {
$result = false;
$newsletter = Newsletter::findOne($id);
if($newsletter !== false) {
if($confirm_delete) {
$newsletter->delete();
$result = array('newsletters' => 1);
} else {
$newsletter->set_expr('deleted_at', 'NOW()');
$result = array('newsletters' => (int)$newsletter->save());
}
} else {
$result = false;
$result = $newsletter->restore();
}
wp_send_json($result);
}
function restore($id) {
function trash($id) {
$result = false;
$newsletter = Newsletter::findOne($id);
if($newsletter !== false) {
$newsletter->set_expr('deleted_at', 'NULL');
$result = array('newsletters' => (int)$newsletter->save());
} else {
$result = false;
$result = $newsletter->trash();
}
wp_send_json($result);
}
function delete($id) {
$result = false;
$newsletter = Newsletter::findOne($id);
if($newsletter !== false) {
$newsletter->delete();
$result = 1;
}
wp_send_json($result);
}
function duplicate($id) {
$result = false;
$newsletter = Newsletter::findOne($id);
if($newsletter !== false) {
$data = array(
'subject' => sprintf(__('Copy of %s'), $newsletter->subject)
);
$result = $newsletter->duplicate($data)->asArray();
}
wp_send_json($result);
}

View File

@ -55,7 +55,6 @@ class Segments {
)
->findOne()->asArray();
!d(\ORM::get_last_query());exit;
$item = array_merge($item, $stats);
$item['subscribers_url'] = admin_url(

View File

@ -132,4 +132,4 @@ class BootStrapMenu {
}
return $data;
}
}
}

View File

@ -2,11 +2,14 @@
namespace MailPoet\Subscribers\ImportExport\Export;
use MailPoet\Config\Env;
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use MailPoet\Models\CustomField;
use MailPoet\Models\Segment;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberSegment;
use MailPoet\Subscribers\ImportExport\BootStrapMenu;
use MailPoet\Util\Helpers;
use MailPoet\Util\XLSXWriter;
use Symfony\Component\Console\Helper\Helper;
class Export {
public function __construct($data) {
@ -16,82 +19,79 @@ class Export {
$this->segments = $data['segments'];
$this->subscribersWithoutSegment = array_search(0, $this->segments);
$this->subscriberFields = $data['subscriberFields'];
$this->exportFile = $this->getExportFile($this->exportFormatOption);
$this->exportFileURL = $this->getExportFileURL($this->exportFile);
$this->profilerStart = microtime(true);
$this->exportFile = sprintf(
Env::$temp_path . '/mailpoet_export_%s.%s',
substr(md5(time()), 0, 4),
$this->exportFormatOption
);
$this->exportFileURL = sprintf(
'%s/%s/%s/%s',
plugins_url(),
Env::$plugin_name,
Env::$temp_name,
basename($this->exportFile)
);
}
function process() {
$subscribers = SubscriberSegment::
left_outer_join(
Subscriber::$_table,
array(
Subscriber::$_table . '.id',
'=',
SubscriberSegment::$_table . '.subscriber_id'
))
->left_outer_join(
Segment::$_table,
array(
Segment::$_table . '.id',
'=',
SubscriberSegment::$_table . '.segment_id'
))
->select(Segment::$_table . '.name', 'segment_name')
->orderByAsc('segment_name')
->filter('filterWithCustomFields')
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments);
if(!$this->groupBySegmentOption) $subscribers = $subscribers->groupBy(Subscriber::$_table . '.id');
if($this->exportConfirmedOption) $subscribers = $subscribers->where(Subscriber::$_table . '.status', 'confirmed');
$subscribers = $subscribers->findArray();
$formattedSubscriberFields = $this->formatSubscriberFields($this->subscriberFields);
$subscribers = $this->getSubscribers();
$subscriberCustomFields = $this->getSubscriberCustomFields();
$formattedSubscriberFields = $this->formatSubscriberFields(
$this->subscriberFields,
$subscriberCustomFields
);
try {
if($this->exportFormatOption === 'csv') {
$CSVFile = fopen($this->exportFile, 'w');
$formatCSV = function ($row) {
return '"' . str_replace('"', '\"', $row) . '"';
};
// add UTF-8 BOM (3 bytes, hex EF BB BF) at the start of the file for Excel to automatically recognize the encoding
// add UTF-8 BOM (3 bytes, hex EF BB BF) at the start of the file for
// Excel to automatically recognize the encoding
fwrite($CSVFile, chr(0xEF) . chr(0xBB) . chr(0xBF));
if($this->groupBySegmentOption) $formattedSubscriberFields[] = __('List');
fwrite($CSVFile, implode(",", array_map($formatCSV, $formattedSubscriberFields)) . "\n");
if($this->groupBySegmentOption) {
$formattedSubscriberFields[] = __('List');
}
fwrite(
$CSVFile,
implode(
',',
array_map(
$formatCSV,
$formattedSubscriberFields
)
) . "\n"
);
foreach ($subscribers as $subscriber) {
$row = array_map(function ($field) use ($subscriber) {
return $subscriber[$field];
}, $this->subscriberFields);
if($this->groupBySegmentOption) $row[] = $subscriber['segment_name'];
fwrite($CSVFile, implode(",", array_map($formatCSV, $row)) . "\n");
$row = $this->formatSubscriberData(
$subscriber,
$formattedSubscriberFields
);
if($this->groupBySegmentOption) {
$row[] = ucwords($subscriber['segment_name']);
}
fwrite($CSVFile, implode(',', array_map($formatCSV, $row)) . "\n");
}
fclose($CSVFile);
} else {
$writer = new XLSXWriter();
$writer->setAuthor('MailPoet (www.mailpoet.com)');
$headerRow = array($formattedSubscriberFields);
$segment = null;
$lastSegment = false;
$rows = array();
foreach ($subscribers as $subscriber) {
if($segment && $segment != $subscriber['segment_name'] && $this->groupBySegmentOption) {
$writer->writeSheet(array_merge($headerRow, $rows), ucwords($segment));
if($lastSegment && $lastSegment !== $subscriber['segment_name'] &&
$this->groupBySegmentOption
) {
$writer->writeSheet(
array_merge($headerRow, $rows), ucwords($lastSegment)
);
$rows = array();
}
// detect RTL language and set Excel to properly display the sheet
if(!$writer->rtl && preg_grep('/\p{Arabic}|\p{Hebrew}/u', $subscriber)) {
$RTLRegex = '/\p{Arabic}|\p{Hebrew}/u';
if(!$writer->rtl && (
preg_grep($RTLRegex, $subscriber) ||
preg_grep($RTLRegex, $formattedSubscriberFields))
) {
$writer->rtl = true;
}
$rows[] = array_map(function ($field) use ($subscriber) {
return $subscriber[$field];
}, $this->subscriberFields);
$segment = $subscriber['segment_name'];
$rows[] = $this->formatSubscriberData(
$subscriber,
$formattedSubscriberFields
);
$lastSegment = $subscriber['segment_name'];
}
$writer->writeSheet(array_merge($headerRow, $rows), 'MailPoet');
$writer->writeToFile($this->exportFile);
@ -107,20 +107,106 @@ class Export {
'data' => array(
'totalExported' => count($subscribers),
'exportFileURL' => $this->exportFileURL
)
),
'profiler' => $this->timeExecution()
);
}
function formatSubscriberFields($subscriberFields) {
function getSubscribers() {
$subscribers = Subscriber::
left_outer_join(
SubscriberSegment::$_table,
array(
Subscriber::$_table . '.id',
'=',
SubscriberSegment::$_table . '.subscriber_id'
))
->left_outer_join(
Segment::$_table,
array(
Segment::$_table . '.id',
'=',
SubscriberSegment::$_table . '.segment_id'
))
->orderByAsc('segment_name')
->filter('filterWithCustomFieldsForExport');
if($this->subscribersWithoutSegment !== false) {
$subscribers = $subscribers
->selectExpr('CASE WHEN ' . Segment::$_table . '.name IS NOT NULL ' .
'THEN ' . Segment::$_table . '.name ' .
'ELSE "' . __('Not In List') . '" END as segment_name'
)
->whereRaw(
SubscriberSegment::$_table . '.segment_id IN (' .
rtrim(str_repeat('?,', count($this->segments)), ',') . ') ' .
'OR ' . SubscriberSegment::$_table . '.segment_id IS NULL ',
$this->segments
);
} else {
$subscribers = $subscribers
->select(Segment::$_table . '.name', 'segment_name')
->whereIn(SubscriberSegment::$_table . '.segment_id', $this->segments);
}
if(!$this->groupBySegmentOption) {
$subscribers =
$subscribers->groupBy(Subscriber::$_table . '.id');
}
if($this->exportConfirmedOption) {
$subscribers =
$subscribers->where(Subscriber::$_table . '.status', 'subscribed');
}
return $subscribers->findArray();
}
function getExportFileURL($file) {
return sprintf(
'%s/%s/%s',
Env::$plugin_url,
Env::$temp_name,
basename($file)
);
}
function getExportFile($format) {
return sprintf(
Env::$temp_path . '/MailPoet_export_%s.%s',
substr(md5(time()), 0, 4),
$format
);
}
function getSubscriberCustomFields() {
return Helpers::arrayColumn(
CustomField::findArray(),
'name',
'id'
);
}
function formatSubscriberFields($subscriberFields, $subscriberCustomFields) {
$bootStrapMenu = new BootStrapMenu();
$translatedFields = $bootStrapMenu->getSubscriberFields();
return array_map(function ($field) use ($translatedFields) {
return (isset($translatedFields[$field])) ?
return array_map(function ($field) use (
$translatedFields, $subscriberCustomFields
) {
$field = (isset($translatedFields[$field])) ?
ucfirst($translatedFields[$field]) :
ucfirst($field);
return (isset($subscriberCustomFields[$field])) ?
$subscriberCustomFields[$field] : $field;
}, $subscriberFields);
}
function formatSubscriberData($subscriber, $subscriberCustomFields) {
return array_map(function ($field) use (
$subscriber, $subscriberCustomFields
) {
return (isset($subscriberCustomFields[$field])) ?
$subscriberCustomFields[$field] :
$subscriber[$field];
}, $this->subscriberFields);
}
function timeExecution() {
$profilerEnd = microtime(true);
return ($profilerEnd - $this->profilerStart) / 60;

View File

@ -75,7 +75,7 @@ class Import {
'updated' => count($updatedSubscribers),
'segments' => $segments->getSegments()
),
'profile' => $this->timeExecution()
'profiler' => $this->timeExecution()
);
}
@ -165,7 +165,7 @@ class Import {
}
function filterSubscriberStatus($subscribersData) {
if(!in_array('status', $this->subscriberFields)) return;
if(!in_array('status', $this->subscriberFields)) return $subscribersData;
$statuses = array(
'subscribed' => array(
'subscribed',
@ -174,6 +174,11 @@ class Import {
'1',
'true'
),
'unconfirmed' => array(
'unconfirmed',
0,
"0"
),
'unsubscribed' => array(
'unsubscribed',
-1,
@ -183,12 +188,15 @@ class Import {
);
$subscribersData['status'] = array_map(function ($state) use ($statuses) {
if(in_array(strtolower($state), $statuses['subscribed'])) {
return 'confirmed';
return 'subscribed';
}
if(in_array(strtolower($state), $statuses['unsubscribed'])) {
return 'unsubscribed';
}
return 'confirmed'; // make "subscribed" a default status
if(in_array(strtolower($state), $statuses['unconfirmed'])) {
return 'unconfirmed';
}
return 'subscribed'; // make "subscribed" a default status
}, $subscribersData['status']);
return $subscribersData;
}

View File

@ -5,7 +5,7 @@ if (!defined('ABSPATH')) exit;
/*
* Plugin Name: MailPoet
* Version: 0.0.4
* Version: 0.0.5
* Plugin URI: http://www.mailpoet.com
* Description: MailPoet Newsletters.
* Author: MailPoet
@ -18,12 +18,12 @@ if (!defined('ABSPATH')) exit;
*
* @package WordPress
* @author MailPoet
* @since 0.0.4
* @since 0.0.5
*/
require 'vendor/autoload.php';
define('MAILPOET_VERSION', '0.0.4');
define('MAILPOET_VERSION', '0.0.5');
$initializer = new Initializer(array(
'file' => __FILE__,

View File

@ -5,8 +5,7 @@
},
"napa": {
"blob": "eligrey/Blob.js.git",
"filesaver": "eligrey/FileSaver.js.git",
"sticky-kit": "leafo/sticky-kit.git"
"filesaver": "eligrey/FileSaver.js.git"
},
"dependencies": {
"backbone": "1.2.3",

View File

@ -1,7 +1,7 @@
define([
'newsletter_editor/App',
'newsletter_editor/components/wordpress',
'newsletter_editor/blocks/posts',
'newsletter_editor/blocks/posts'
], function(EditorApplication, WordpressComponent, PostsBlock) {
describe('Posts', function () {
@ -373,12 +373,7 @@ define([
});
describe('when "title as list" is selected', function() {
var model, view;
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_title_format').val('ul').change();
});

View File

@ -1,23 +1,237 @@
<?php
use MailPoet\Subscribers\ImportExport\Export;
use MailPoet\Config\Env;
use MailPoet\Models\CustomField;
use MailPoet\Models\Segment;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberCustomField;
use MailPoet\Models\SubscriberSegment;
use MailPoet\Subscribers\ImportExport\Export\Export;
class ExportCest {
function __construct() {
function _before() {
$this->JSONdata = json_decode(file_get_contents(dirname(__FILE__) . '/ExportTestData.json'), true);
$this->subscriberFields = array(
'first_name' => 'First name',
'last_name' => 'Last name',
'email' => 'Email',
1 => 'Country'
);
$this->subscribersData = array(
array(
'first_name' => 'Adam',
'last_name' => 'Smith',
'email' => 'adam@smith.com'
),
array(
'first_name' => 'Mary',
'last_name' => 'Jane',
'email' => 'mary@jane.com',
'status' => 'subscribed',
1 => 'Brazil'
),
array(
'first_name' => 'John',
'last_name' => 'Kookoo',
'email' => 'john@kookoo.com'
),
array(
'first_name' => 'Paul',
'last_name' => 'Newman',
'email' => 'paul@newman.com'
)
);
$this->customFieldsData = array(
array(
'name' => 'Country',
'type' => 'text'
)
);
$this->segmentsData = array(
array(
'name' => 'Newspapers'
),
array(
'name' => 'Journals'
)
);
foreach ($this->subscribersData as $subscriber) {
if(isset($subscriber[1])) {
unset($subscriber[1]);
}
$entity = Subscriber::create();
$entity->hydrate($subscriber);
$entity->save();
}
foreach ($this->segmentsData as $customField) {
$entity = Segment::create();
$entity->hydrate($customField);
$entity->save();
}
foreach ($this->customFieldsData as $customField) {
$entity = CustomField::create();
$entity->hydrate($customField);
$entity->save();
}
$entity = SubscriberCustomField::create();
$entity->subscriber_id = 2;
$entity->custom_field_id = 1;
$entity->value = $this->subscribersData[1][1];
$entity->save();
$entity = SubscriberSegment::create();
$entity->subscriber_id = 1;
$entity->segment_id = 1;
$entity->save();
$entity = SubscriberSegment::create();
$entity->subscriber_id = 1;
$entity->segment_id = 2;
$entity->save();
$entity = SubscriberSegment::create();
$entity->subscriber_id = 2;
$entity->segment_id = 1;
$entity->save();
$entity = SubscriberSegment::create();
$entity->subscriber_id = 3;
$entity->segment_id = 2;
$entity->save();
$this->export = new Export($this->JSONdata);
}
function itCanConstruct() {
expect($this->export->exportConfirmedOption)
->equals(false);
expect($this->export->exportFormatOption)
->equals('csv');
expect($this->export->groupBySegmentOption)
->equals(false);
expect($this->export->segments)
->equals(
array(
1,
2
)
);
expect($this->export->subscribersWithoutSegment)
->equals(0);
expect($this->export->subscriberFields)
->equals(
array(
'email',
'first_name',
'1'
)
);
expect(
preg_match(
'|' .
Env::$temp_path . '/MailPoet_export_[a-f0-9]{4}.' .
$this->export->exportFormatOption .
'|', $this->export->exportFile)
)->equals(1);
expect(
preg_match(
'|' .
Env::$plugin_url . '/' .
Env::$temp_name . '/' .
basename($this->export->exportFile) .
'|'
, $this->export->exportFileURL)
)->equals(1);
}
function itCanGetSubscriberCustomFields() {
$source = CustomField::where('name', $this->customFieldsData[0]['name'])
->findOne();
$target = $this->export->getSubscriberCustomFields();
expect($target)->equals(array($source->id => $source->name));
}
function itCanFormatSubscriberFields() {
$formattedSubscriberFields = $this->export->formatSubscriberFields(
array_keys($this->subscriberFields),
$this->export->getSubscriberCustomFields()
);
expect($formattedSubscriberFields)
->equals(array_values($this->subscriberFields));
}
function itProperlyReturnsSubscriberCustomFields() {
$subscribers = $this->export->getSubscribers();
foreach ($subscribers as $subscriber) {
if($subscriber['email'] === $this->subscribersData[1]) {
expect($subscriber['Country'])
->equals($this->subscribersData[1][1]);
}
}
}
function itCanGetSubscribers() {
$this->export->segments = array(1);
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(2);
$this->export->segments = array(2);
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(2);
$this->export->segments = array(
1,
2
);
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(3);
}
function itCanGroupSubscribersBySegments() {
$this->export->groupBySegmentOption = true;
$this->export->subscribersWithoutSegment = true;
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(5);
}
function itCanGetSubscribersOnlyWithoutSegments() {
$this->export->segments = array(0);
$this->export->subscribersWithoutSegment = true;
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(1);
expect($subscribers[0]['segment_name'])->equals('Not In List');
}
function itCanGetOnlyConfirmedSubscribers() {
$this->export->exportConfirmedOption = true;
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(1);
expect($subscribers[0]['email'])
->equals($this->subscribersData[1]['email']);
}
function itCanGetSubscribersOnlyInSegments() {
SubscriberSegment::where('subscriber_id', 3)
->findOne()
->delete();
$subscribers = $this->export->getSubscribers();
expect(count($subscribers))->equals(2);
}
function itCanProcess() {
$this->export->exportFile = $this->export->getExportFile('csv');
$this->export->exportFormatOption = 'csv';
$this->export->process();
$CSVFileSize = filesize($this->export->exportFile);
$this->export->exportFile = $this->export->getExportFile('xls');
$this->export->exportFormatOption = 'xls';
$this->export->process();
$XLSFileSize = filesize($this->export->exportFile);
expect($CSVFileSize)->greaterThan(0);
expect($XLSFileSize)->greaterThan(0);
expect($XLSFileSize)->greaterThan($CSVFileSize);
}
function _after() {
ORM::forTable(Subscriber::$_table)
->deleteMany();
ORM::forTable(SubscriberCustomField::$_table)
->deleteMany();
ORM::forTable(SubscriberSegment::$_table)
->deleteMany();
ORM::raw_execute('TRUNCATE ' . Subscriber::$_table);
ORM::raw_execute('TRUNCATE ' . Segment::$_table);
ORM::raw_execute('TRUNCATE ' . SubscriberSegment::$_table);
ORM::raw_execute('TRUNCATE ' . CustomField::$_table);
ORM::raw_execute('TRUNCATE ' . SubscriberCustomField::$_table);
}
}

View File

@ -0,0 +1,14 @@
{
"exportConfirmedOption": false,
"exportFormatOption": "csv",
"groupBySegmentOption": false,
"segments": [
"1",
"2"
],
"subscriberFields": [
"email",
"first_name",
"1"
]
}

View File

@ -1,9 +1,9 @@
<?php
use MailPoet\Subscribers\ImportExport\Import\Import;
use MailPoet\Models\Subscriber;
use MailPoet\Models\SubscriberCustomField;
use MailPoet\Models\SubscriberSegment;
use MailPoet\Subscribers\ImportExport\Import\Import;
use MailPoet\Util\Helpers;
class ImportCest {
@ -108,38 +108,39 @@ class ImportCest {
function itCanFilterSubscriberState() {
$data = array(
'status' => array(
'confirmed',
'subscribed',
//subscribed
'subscribed',
'confirmed',
1,
'1',
'true',
//unconfirmed
'unconfirmed',
0,
"0",
//unsubscribed
'unsubscribed',
-1,
'-1',
'false',
'something',
'else'
'false'
),
);
$statuses = $this->import->filterSubscriberStatus($data);
expect($statuses)->equals(
array(
'status' => array(
'confirmed',
'confirmed',
'confirmed',
'confirmed',
'confirmed',
'confirmed',
'confirmed',
'subscribed',
'subscribed',
'subscribed',
'subscribed',
'subscribed',
'unconfirmed',
'unconfirmed',
'unconfirmed',
'unsubscribed',
'unsubscribed',
'unsubscribed',
'unsubscribed',
'confirmed',
'confirmed'
'unsubscribed'
)
)
);

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
<div class="mailpoet_tools"></div>
<div class="mailpoet_content">
<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_overlay"></div>
<div class="mailpoet_automated_latest_content_block_posts"></div>
</div>
<div class="mailpoet_block_highlight"></div>

View File

@ -37,26 +37,6 @@
<hr class="mailpoet_separator" />
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_automated_latest_content_display_type" class="mailpoet_automated_latest_content_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_automated_latest_content_display_type" class="mailpoet_automated_latest_content_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_automated_latest_content_display_type" class="mailpoet_automated_latest_content_display_type" value="titleOnly" {{#ifCond model.displayType '==' 'titleOnly'}}CHECKED{{/ifCond}} />
<%= __('Title only') %>
</label>
</div>
</div>
<div class="mailpoet_form_field">
<a href="javascript:;" class="mailpoet_automated_latest_content_show_display_options"><%= __('Display options') %></a>
@ -66,6 +46,27 @@
<a href="javascript:;" class="mailpoet_automated_latest_content_hide_display_options"><%= __('Hide display options') %></a>
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_automated_latest_content_display_type" class="mailpoet_automated_latest_content_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_automated_latest_content_display_type" class="mailpoet_automated_latest_content_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_automated_latest_content_display_type" class="mailpoet_automated_latest_content_display_type" value="titleOnly" {{#ifCond model.displayType '==' 'titleOnly'}}CHECKED{{/ifCond}} />
<%= __('Title only') %>
</label>
</div>
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Title Format') %></div>
<div class="mailpoet_form_field_radio_option">
@ -215,7 +216,7 @@
<%= __('Below text') %>
</label>
</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">
<input type="text" class="mailpoet_input mailpoet_input_medium mailpoet_automated_latest_content_categories" value="{{ model.categoriesPrecededBy }}" />
</div>
@ -251,7 +252,7 @@
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title mailpoet_form_field_title_small mailpoet_form_field_title_inline"><%= __('Sort by') %></div>
<div class="mailpoet_form_field_title mailpoet_form_field_title_small"><%= __('Sort by') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_automated_latest_content_sort_by" class="mailpoet_automated_latest_content_sort_by" value="newest" {{#ifCond model.sortBy '==' 'newest'}}CHECKED{{/ifCond}}/>
@ -268,7 +269,7 @@
<div class="mailpoet_automated_latest_content_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_title mailpoet_form_field_title_small"><%= __('Show divider between posts') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="mailpoet_automated_latest_content_show_divider"class="mailpoet_automated_latest_content_show_divider" value="true" {{#if model.showDivider}}CHECKED{{/if}}/>
@ -281,7 +282,7 @@
<%= __('No') %>
</label>
</div>
<div class="mailpoet_form_field_input_option">
<div>
<a href="javascript:;" class="mailpoet_automated_latest_content_select_divider"><%= __('Select divider') %></a>
</div>
</div>

View File

@ -20,6 +20,7 @@
{{/ifCond}}
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Alignment') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<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_content">
<%= __('Your posts will be inserted here') %>
<div class="mailpoet_posts_block_posts"></div>
</div>
<div class="mailpoet_block_highlight"></div>

View File

@ -2,7 +2,9 @@
<div class="mailpoet_settings_posts_selection"></div>
<div class="mailpoet_settings_posts_display_options mailpoet_hidden"></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="mailpoet_button mailpoet_button_primary mailpoet_settings_posts_insert_selected" value="<%= __('Insert selected') %>" />
<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>

View File

@ -170,7 +170,7 @@
<%= __('Below text') %>
</label>
</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">
<input type="text" class="mailpoet_input mailpoet_input_medium mailpoet_posts_categories" value="{{ model.categoriesPrecededBy }}" />
</div>

View File

@ -13,6 +13,9 @@
<option value="pending"><%= __('Pending Review') %></option>
<option value="private"><%= __('Private') %></option>
</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">
<select class="mailpoet_select mailpoet_posts_categories_and_tags" multiple="multiple">
{{#each terms}}

View File

@ -7,7 +7,11 @@
'pageTitle': __('Newsletters'),
'searchLabel': __('Search'),
'loadingItems': __('Loading newsletters...'),
'noItemsFound': __('No newsletters found.')
'noItemsFound': __('No newsletters found.'),
'selectAllLabel': __('All newsletters on this page are selected.'),
'selectedAllLabel': __('All %d newsletters are selected.'),
'selectAllLink': __('Select all pages.'),
'clearSelection': __('Clear selection.')
}) %>
<script type="text/javascript">

24
views/update.html Normal file
View File

@ -0,0 +1,24 @@
<% extends 'layout.html' %>
<% block content %>
<div id="mailpoet_update">
<h2><%= __("What's new?") %></h2>
<p>Plugin version: <strong><%= settings.version %></strong></p>
<p>
Current user:
<strong><%= current_user.user_nicename %></strong>
&lt;<a href="mailto:<%= current_user.user_email %>">
<%=- current_user.user_email -%>
</a>&gt;
</p>
<p>
<a
class="button button-primary"
href="<%= redirect_url %>"
>Take me to MailPoet</a>
</p>
</div>
<% endblock %>

View File

@ -2,12 +2,23 @@
<% block content %>
<div id="mailpoet_welcome">
<h2><%= __('Welcome welcome welcome!') %></h2>
<h2><%= __('Welcome!') %></h2>
<h3>Settings:</h3>
<%= dump(settings) %>
<p>Plugin version: <strong><%= settings.version %></strong></p>
<h3>Current user:</h3>
<%= dump(current_user) %>
<p>
Current user:
<strong><%= current_user.user_nicename %></strong>
&lt;<a href="mailto:<%= current_user.user_email %>">
<%=- current_user.user_email -%>
</a>&gt;
</p>
<p>
<a
class="button button-primary"
href="<%= redirect_url %>"
>Take me to MailPoet</a>
</p>
</div>
<% endblock %>

View File

@ -23,7 +23,7 @@ baseConfig = {
'handlebars': 'handlebars/dist/handlebars.js',
'backbone.marionette': 'backbone.marionette/lib/backbone.marionette',
'backbone.supermodel$': 'backbone.supermodel/build/backbone.supermodel.js',
'sticky-kit': 'sticky-kit/jquery.sticky-kit',
'sticky-kit': 'vendor_static/jquery.sticky-kit.js',
'interact$': 'interact.js/interact.js',
'spectrum$': 'spectrum-colorpicker/spectrum.js',
'blob$': 'blob/Blob.js',
@ -166,9 +166,6 @@ config.push(_.extend({}, baseConfig, {
'public.js'
]
},
/*plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),
],*/
externals: {
'jquery': 'jQuery'
}