Version Description
- 2016-12-27 =
- Improved: newsletter is saved prior to sending an email preview;
- Improved: subscription management page conditionally displays the "bounced" status;
- Improved: deleted lists are displayed in newsletter listings;
- Fixed: newsletter/subscriber/list/form dates are properly formatted according to WP settings;
- Fixed: emails' "Return-path" header is set to the bounce address configured in Settings->Advanced;
- Fixed: archived newsletters' shortcode works for site visitors;
- Fixed: unicode support for newsletters.
Download this release
Release Info
| Developer | wysija |
| Plugin | |
| Version | 3.0.0-beta.10 |
| Comparing to | |
| See all releases | |
Code changes from version 3.0.0-beta.9 to 3.0.0-beta.10
- assets/css/admin.css +2 -2
- assets/js/admin.js +9 -3
- assets/js/form_editor.js +679 -676
- assets/js/mailpoet.js +1 -3
- assets/js/newsletter_editor.js +39 -24
- lang/index.php +0 -3
- lang/mailpoet.pot +44 -40
- lib/API/Endpoints/Newsletters.php +11 -5
- lib/Config/Env.php +1 -0
- lib/Config/Initializer.php +1 -2
- lib/Config/Menu.php +12 -12
- lib/Config/Migrator.php +1 -0
- lib/Config/Shortcodes.php +13 -8
- lib/Form/Block/Select.php +8 -2
- lib/Mailer/Mailer.php +16 -5
- lib/Mailer/Methods/AmazonSES.php +6 -3
- lib/Mailer/Methods/PHPMail.php +6 -1
- lib/Mailer/Methods/SMTP.php +6 -1
- lib/Models/Newsletter.php +65 -3
- lib/Models/SendingQueue.php +5 -2
- lib/Models/Subscriber.php +7 -3
- lib/Newsletter/Links/Links.php +58 -12
- lib/Newsletter/Renderer/PostProcess/OpenTracking.php +1 -1
- lib/Newsletter/Renderer/Renderer.php +8 -4
- lib/Newsletter/Shortcodes/Categories/Link.php +42 -16
- lib/Newsletter/Shortcodes/Shortcodes.php +8 -3
- lib/Newsletter/Url.php +59 -31
- lib/Newsletter/ViewInBrowser.php +25 -10
- lib/Router/Endpoints/Track.php +4 -3
- lib/Router/Endpoints/ViewInBrowser.php +45 -26
- lib/Subscription/Pages.php +4 -0
- mailpoet.php +2 -2
- readme.txt +18 -9
- vendor/autoload.php +1 -1
- vendor/composer/ClassLoader.php +31 -5
- vendor/composer/autoload_real.php +8 -8
- vendor/composer/autoload_static.php +5 -5
- vendor/composer/installed.json +11 -11
assets/css/admin.css
CHANGED
|
@@ -1799,8 +1799,8 @@ div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
|
|
| 1799 |
position: relative;
|
| 1800 |
overflow: visible;
|
| 1801 |
-webkit-tap-highlight-color: transparent;
|
| 1802 |
-
-webkit-font-variant-ligatures:
|
| 1803 |
-
font-variant-ligatures:
|
| 1804 |
}
|
| 1805 |
.CodeMirror-wrap pre {
|
| 1806 |
word-wrap: break-word;
|
| 1799 |
position: relative;
|
| 1800 |
overflow: visible;
|
| 1801 |
-webkit-tap-highlight-color: transparent;
|
| 1802 |
+
-webkit-font-variant-ligatures: contextual;
|
| 1803 |
+
font-variant-ligatures: contextual;
|
| 1804 |
}
|
| 1805 |
.CodeMirror-wrap pre {
|
| 1806 |
word-wrap: break-word;
|
assets/js/admin.js
CHANGED
|
@@ -839,12 +839,18 @@ webpackJsonp([0],[
|
|
| 839 |
* will remain to ensure logic does not differ in production.
|
| 840 |
*/
|
| 841 |
|
| 842 |
-
function
|
| 843 |
-
|
|
|
|
|
|
|
| 844 |
if (format === undefined) {
|
| 845 |
throw new Error('invariant requires an error message argument');
|
| 846 |
}
|
| 847 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 848 |
|
| 849 |
if (!condition) {
|
| 850 |
var error;
|
| 839 |
* will remain to ensure logic does not differ in production.
|
| 840 |
*/
|
| 841 |
|
| 842 |
+
var validateFormat = function validateFormat(format) {};
|
| 843 |
+
|
| 844 |
+
if (process.env.NODE_ENV !== 'production') {
|
| 845 |
+
validateFormat = function validateFormat(format) {
|
| 846 |
if (format === undefined) {
|
| 847 |
throw new Error('invariant requires an error message argument');
|
| 848 |
}
|
| 849 |
+
};
|
| 850 |
+
}
|
| 851 |
+
|
| 852 |
+
function invariant(condition, format, a, b, c, d, e, f) {
|
| 853 |
+
validateFormat(format);
|
| 854 |
|
| 855 |
if (!condition) {
|
| 856 |
var error;
|
assets/js/form_editor.js
CHANGED
|
@@ -2077,12 +2077,12 @@ webpackJsonp([1],{
|
|
| 2077 |
var bidiOrdering = (function() {
|
| 2078 |
// Character types for codepoints 0 to 0xff
|
| 2079 |
var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
|
| 2080 |
-
// Character types for codepoints 0x600 to
|
| 2081 |
-
var arabicTypes = "
|
| 2082 |
function charType(code) {
|
| 2083 |
if (code <= 0xf7) { return lowTypes.charAt(code) }
|
| 2084 |
else if (0x590 <= code && code <= 0x5f4) { return "R" }
|
| 2085 |
-
else if (0x600 <= code && code <=
|
| 2086 |
else if (0x6ee <= code && code <= 0x8ac) { return "r" }
|
| 2087 |
else if (0x2000 <= code && code <= 0x200b) { return "w" }
|
| 2088 |
else if (code == 0x200c) { return "b" }
|
|
@@ -4388,7 +4388,7 @@ webpackJsonp([1],{
|
|
| 4388 |
}
|
| 4389 |
}
|
| 4390 |
|
| 4391 |
-
|
| 4392 |
this.cm = cm
|
| 4393 |
var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
|
| 4394 |
var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
|
|
@@ -4404,91 +4404,92 @@ webpackJsonp([1],{
|
|
| 4404 |
this.checkedZeroWidth = false
|
| 4405 |
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
|
| 4406 |
if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
|
| 4407 |
-
}
|
| 4408 |
-
|
| 4409 |
-
NativeScrollbars.prototype =
|
| 4410 |
-
|
| 4411 |
-
|
| 4412 |
-
|
| 4413 |
-
|
| 4414 |
-
|
| 4415 |
-
|
| 4416 |
-
|
| 4417 |
-
|
| 4418 |
-
|
| 4419 |
-
|
| 4420 |
-
|
| 4421 |
-
|
| 4422 |
-
|
| 4423 |
-
|
| 4424 |
-
this.vert.firstChild.style.height = "0"
|
| 4425 |
-
}
|
| 4426 |
-
|
| 4427 |
-
if (needsH) {
|
| 4428 |
-
this.horiz.style.display = "block"
|
| 4429 |
-
this.horiz.style.right = needsV ? sWidth + "px" : "0"
|
| 4430 |
-
this.horiz.style.left = measure.barLeft + "px"
|
| 4431 |
-
var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
|
| 4432 |
-
this.horiz.firstChild.style.width =
|
| 4433 |
-
(measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
|
| 4434 |
-
} else {
|
| 4435 |
-
this.horiz.style.display = ""
|
| 4436 |
-
this.horiz.firstChild.style.width = "0"
|
| 4437 |
-
}
|
| 4438 |
-
|
| 4439 |
-
if (!this.checkedZeroWidth && measure.clientHeight > 0) {
|
| 4440 |
-
if (sWidth == 0) { this.zeroWidthHack() }
|
| 4441 |
-
this.checkedZeroWidth = true
|
| 4442 |
-
}
|
| 4443 |
-
|
| 4444 |
-
return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
|
| 4445 |
-
},
|
| 4446 |
-
setScrollLeft: function(pos) {
|
| 4447 |
-
if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
|
| 4448 |
-
if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz) }
|
| 4449 |
-
},
|
| 4450 |
-
setScrollTop: function(pos) {
|
| 4451 |
-
if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
|
| 4452 |
-
if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) }
|
| 4453 |
-
},
|
| 4454 |
-
zeroWidthHack: function() {
|
| 4455 |
-
var w = mac && !mac_geMountainLion ? "12px" : "18px"
|
| 4456 |
-
this.horiz.style.height = this.vert.style.width = w
|
| 4457 |
-
this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
|
| 4458 |
-
this.disableHoriz = new Delayed
|
| 4459 |
-
this.disableVert = new Delayed
|
| 4460 |
-
},
|
| 4461 |
-
enableZeroWidthBar: function(bar, delay) {
|
| 4462 |
-
bar.style.pointerEvents = "auto"
|
| 4463 |
-
function maybeDisable() {
|
| 4464 |
-
// To find out whether the scrollbar is still visible, we
|
| 4465 |
-
// check whether the element under the pixel in the bottom
|
| 4466 |
-
// left corner of the scrollbar box is the scrollbar box
|
| 4467 |
-
// itself (when the bar is still visible) or its filler child
|
| 4468 |
-
// (when the bar is hidden). If it is still visible, we keep
|
| 4469 |
-
// it enabled, if it's hidden, we disable pointer events.
|
| 4470 |
-
var box = bar.getBoundingClientRect()
|
| 4471 |
-
var elt = document.elementFromPoint(box.left + 1, box.bottom - 1)
|
| 4472 |
-
if (elt != bar) { bar.style.pointerEvents = "none" }
|
| 4473 |
-
else { delay.set(1000, maybeDisable) }
|
| 4474 |
-
}
|
| 4475 |
-
delay.set(1000, maybeDisable)
|
| 4476 |
-
},
|
| 4477 |
-
clear: function() {
|
| 4478 |
-
var parent = this.horiz.parentNode
|
| 4479 |
-
parent.removeChild(this.horiz)
|
| 4480 |
-
parent.removeChild(this.vert)
|
| 4481 |
}
|
| 4482 |
-
}, NativeScrollbars.prototype)
|
| 4483 |
-
|
| 4484 |
-
function NullScrollbars() {}
|
| 4485 |
|
| 4486 |
-
|
| 4487 |
-
|
| 4488 |
-
|
| 4489 |
-
|
| 4490 |
-
|
| 4491 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4492 |
|
| 4493 |
function updateScrollbars(cm, measure) {
|
| 4494 |
if (!measure) { measure = measureForScrollbars(cm) }
|
|
@@ -5067,7 +5068,7 @@ webpackJsonp([1],{
|
|
| 5067 |
|
| 5068 |
// DISPLAY DRAWING
|
| 5069 |
|
| 5070 |
-
|
| 5071 |
var display = cm.display
|
| 5072 |
|
| 5073 |
this.viewport = viewport
|
|
@@ -5080,18 +5081,18 @@ webpackJsonp([1],{
|
|
| 5080 |
this.force = force
|
| 5081 |
this.dims = getDimensions(cm)
|
| 5082 |
this.events = []
|
| 5083 |
-
}
|
| 5084 |
|
| 5085 |
-
DisplayUpdate.prototype.signal = function(emitter, type) {
|
| 5086 |
if (hasHandler(emitter, type))
|
| 5087 |
{ this.events.push(arguments) }
|
| 5088 |
-
}
|
| 5089 |
-
DisplayUpdate.prototype.finish = function() {
|
| 5090 |
-
|
| 5091 |
|
| 5092 |
for (var i = 0; i < this.events.length; i++)
|
| 5093 |
{ signal.apply(null, this$1.events[i]) }
|
| 5094 |
-
}
|
| 5095 |
|
| 5096 |
function maybeClipScrollbars(cm) {
|
| 5097 |
var display = cm.display
|
|
@@ -8219,7 +8220,7 @@ webpackJsonp([1],{
|
|
| 8219 |
for (var i = newBreaks.length - 1; i >= 0; i--)
|
| 8220 |
{ replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
|
| 8221 |
})
|
| 8222 |
-
option("specialChars", /[\u0000-\u001f\u007f\u00ad\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
|
| 8223 |
cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
|
| 8224 |
if (old != Init) { cm.refresh() }
|
| 8225 |
})
|
|
@@ -8309,7 +8310,7 @@ webpackJsonp([1],{
|
|
| 8309 |
function guttersChanged(cm) {
|
| 8310 |
updateGutters(cm)
|
| 8311 |
regChange(cm)
|
| 8312 |
-
|
| 8313 |
}
|
| 8314 |
|
| 8315 |
function dragDropChanged(cm, value, old) {
|
|
@@ -8364,7 +8365,6 @@ webpackJsonp([1],{
|
|
| 8364 |
themeChanged(this)
|
| 8365 |
if (options.lineWrapping)
|
| 8366 |
{ this.display.wrapper.className += " CodeMirror-wrap" }
|
| 8367 |
-
if (options.autofocus && !mobile) { display.input.focus() }
|
| 8368 |
initScrollbars(this)
|
| 8369 |
|
| 8370 |
this.state = {
|
|
@@ -8383,6 +8383,8 @@ webpackJsonp([1],{
|
|
| 8383 |
specialChars: null
|
| 8384 |
}
|
| 8385 |
|
|
|
|
|
|
|
| 8386 |
// Override magic textarea content restore that IE sometimes does
|
| 8387 |
// on our hidden textarea on reload
|
| 8388 |
if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
|
|
@@ -8738,6 +8740,7 @@ webpackJsonp([1],{
|
|
| 8738 |
options[option] = value
|
| 8739 |
if (optionHandlers.hasOwnProperty(option))
|
| 8740 |
{ operation(this, optionHandlers[option])(this, value, old) }
|
|
|
|
| 8741 |
},
|
| 8742 |
|
| 8743 |
getOption: function(option) {return this.options[option]},
|
|
@@ -9245,331 +9248,333 @@ webpackJsonp([1],{
|
|
| 9245 |
|
| 9246 |
// CONTENTEDITABLE INPUT STYLE
|
| 9247 |
|
| 9248 |
-
|
| 9249 |
this.cm = cm
|
| 9250 |
this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
|
| 9251 |
this.polling = new Delayed()
|
| 9252 |
this.composing = null
|
| 9253 |
this.gracePeriod = false
|
| 9254 |
this.readDOMTimeout = null
|
| 9255 |
-
}
|
| 9256 |
|
| 9257 |
-
ContentEditableInput.prototype =
|
| 9258 |
-
init: function(display) {
|
| 9259 |
var this$1 = this;
|
| 9260 |
|
| 9261 |
-
|
| 9262 |
-
|
| 9263 |
-
|
| 9264 |
|
| 9265 |
-
|
| 9266 |
-
|
| 9267 |
-
|
| 9268 |
-
|
| 9269 |
-
|
| 9270 |
-
|
| 9271 |
-
|
| 9272 |
|
| 9273 |
-
|
| 9274 |
-
|
| 9275 |
-
|
| 9276 |
-
|
| 9277 |
-
|
| 9278 |
-
|
| 9279 |
-
|
| 9280 |
-
|
| 9281 |
-
|
| 9282 |
-
|
| 9283 |
-
|
| 9284 |
-
|
| 9285 |
|
| 9286 |
-
|
| 9287 |
|
| 9288 |
-
|
| 9289 |
-
|
| 9290 |
-
|
| 9291 |
|
| 9292 |
-
|
| 9293 |
-
|
| 9294 |
-
|
| 9295 |
-
|
| 9296 |
-
|
| 9297 |
-
|
| 9298 |
-
|
| 9299 |
-
|
| 9300 |
-
|
| 9301 |
-
|
| 9302 |
-
|
| 9303 |
-
|
| 9304 |
-
|
| 9305 |
-
|
| 9306 |
-
|
| 9307 |
-
}
|
| 9308 |
}
|
| 9309 |
-
|
| 9310 |
-
|
| 9311 |
-
|
| 9312 |
-
|
| 9313 |
-
|
| 9314 |
-
|
| 9315 |
-
|
| 9316 |
-
|
| 9317 |
-
|
| 9318 |
}
|
| 9319 |
-
// Old-fashioned briefly-focus-a-textarea hack
|
| 9320 |
-
var kludge = hiddenTextarea(), te = kludge.firstChild
|
| 9321 |
-
cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
|
| 9322 |
-
te.value = lastCopied.text.join("\n")
|
| 9323 |
-
var hadFocus = document.activeElement
|
| 9324 |
-
selectInput(te)
|
| 9325 |
-
setTimeout(function () {
|
| 9326 |
-
cm.display.lineSpace.removeChild(kludge)
|
| 9327 |
-
hadFocus.focus()
|
| 9328 |
-
if (hadFocus == div) { input.showPrimarySelection() }
|
| 9329 |
-
}, 50)
|
| 9330 |
}
|
| 9331 |
-
|
| 9332 |
-
|
| 9333 |
-
|
| 9334 |
-
|
| 9335 |
-
|
| 9336 |
-
|
| 9337 |
-
|
| 9338 |
-
|
| 9339 |
-
|
| 9340 |
-
|
| 9341 |
-
|
| 9342 |
-
|
| 9343 |
-
|
| 9344 |
-
|
| 9345 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9346 |
|
| 9347 |
-
|
| 9348 |
-
|
| 9349 |
-
|
| 9350 |
-
|
| 9351 |
-
|
| 9352 |
-
|
| 9353 |
-
|
| 9354 |
-
|
| 9355 |
-
|
| 9356 |
-
var
|
| 9357 |
-
var
|
| 9358 |
-
|
| 9359 |
-
|
| 9360 |
-
|
| 9361 |
-
|
| 9362 |
-
|
| 9363 |
-
|
| 9364 |
-
|
| 9365 |
-
|
| 9366 |
-
|
| 9367 |
-
|
| 9368 |
-
}
|
| 9369 |
-
|
| 9370 |
-
var rng
|
| 9371 |
-
try { rng = range(start.node, start.offset, end.offset, end.node) }
|
| 9372 |
-
catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
|
| 9373 |
-
if (rng) {
|
| 9374 |
-
if (!gecko && this.cm.state.focused) {
|
| 9375 |
-
sel.collapse(start.node, start.offset)
|
| 9376 |
-
if (!rng.collapsed) {
|
| 9377 |
-
sel.removeAllRanges()
|
| 9378 |
-
sel.addRange(rng)
|
| 9379 |
-
}
|
| 9380 |
-
} else {
|
| 9381 |
sel.removeAllRanges()
|
| 9382 |
sel.addRange(rng)
|
| 9383 |
}
|
| 9384 |
-
|
| 9385 |
-
|
|
|
|
| 9386 |
}
|
| 9387 |
-
|
| 9388 |
-
|
|
|
|
|
|
|
|
|
|
| 9389 |
|
| 9390 |
-
|
| 9391 |
var this$1 = this;
|
| 9392 |
|
| 9393 |
-
|
| 9394 |
-
|
| 9395 |
-
|
| 9396 |
-
|
| 9397 |
-
|
| 9398 |
-
|
| 9399 |
-
|
| 9400 |
-
|
| 9401 |
-
|
| 9402 |
-
|
| 9403 |
-
|
| 9404 |
-
|
| 9405 |
-
|
| 9406 |
-
|
| 9407 |
-
|
| 9408 |
-
|
| 9409 |
-
|
| 9410 |
-
|
| 9411 |
-
|
| 9412 |
-
|
| 9413 |
-
|
| 9414 |
-
|
| 9415 |
-
|
| 9416 |
-
|
| 9417 |
-
|
| 9418 |
-
|
| 9419 |
-
|
| 9420 |
-
|
| 9421 |
-
|
| 9422 |
-
|
| 9423 |
-
|
| 9424 |
-
|
| 9425 |
-
|
| 9426 |
-
|
| 9427 |
-
|
| 9428 |
-
|
| 9429 |
-
supportsTouch: function() { return true },
|
| 9430 |
-
|
| 9431 |
-
receivedFocus: function() {
|
| 9432 |
-
var input = this
|
| 9433 |
-
if (this.selectionInEditor())
|
| 9434 |
-
{ this.pollSelection() }
|
| 9435 |
-
else
|
| 9436 |
-
{ runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
|
| 9437 |
|
| 9438 |
-
|
| 9439 |
-
if (input.cm.state.focused) {
|
| 9440 |
-
input.pollSelection()
|
| 9441 |
-
input.polling.set(input.cm.options.pollInterval, poll)
|
| 9442 |
-
}
|
| 9443 |
-
}
|
| 9444 |
-
this.polling.set(this.cm.options.pollInterval, poll)
|
| 9445 |
-
},
|
| 9446 |
|
| 9447 |
-
|
| 9448 |
-
|
| 9449 |
-
|
| 9450 |
-
|
| 9451 |
-
|
|
|
|
| 9452 |
|
| 9453 |
-
|
| 9454 |
-
if (
|
| 9455 |
-
|
| 9456 |
-
|
| 9457 |
-
var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
|
| 9458 |
-
var head = domToPos(cm, sel.focusNode, sel.focusOffset)
|
| 9459 |
-
if (anchor && head) { runInOp(cm, function () {
|
| 9460 |
-
setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
|
| 9461 |
-
if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
|
| 9462 |
-
}) }
|
| 9463 |
}
|
| 9464 |
-
}
|
|
|
|
|
|
|
| 9465 |
|
| 9466 |
-
|
| 9467 |
-
|
| 9468 |
-
|
| 9469 |
-
|
| 9470 |
-
|
| 9471 |
|
| 9472 |
-
|
| 9473 |
-
|
| 9474 |
-
|
| 9475 |
-
|
| 9476 |
-
|
| 9477 |
-
|
| 9478 |
-
if (
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9479 |
|
| 9480 |
-
|
| 9481 |
-
|
| 9482 |
-
|
| 9483 |
-
|
| 9484 |
-
|
| 9485 |
-
|
| 9486 |
-
|
| 9487 |
-
|
| 9488 |
-
var toIndex = findViewIndex(cm, to.line)
|
| 9489 |
-
var toLine, toNode
|
| 9490 |
-
if (toIndex == display.view.length - 1) {
|
| 9491 |
-
toLine = display.viewTo - 1
|
| 9492 |
-
toNode = display.lineDiv.lastChild
|
| 9493 |
-
} else {
|
| 9494 |
-
toLine = lineNo(display.view[toIndex + 1].line) - 1
|
| 9495 |
-
toNode = display.view[toIndex + 1].node.previousSibling
|
| 9496 |
-
}
|
| 9497 |
-
|
| 9498 |
-
if (!fromNode) { return false }
|
| 9499 |
-
var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
|
| 9500 |
-
var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
|
| 9501 |
-
while (newText.length > 1 && oldText.length > 1) {
|
| 9502 |
-
if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
|
| 9503 |
-
else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
|
| 9504 |
-
else { break }
|
| 9505 |
-
}
|
| 9506 |
-
|
| 9507 |
-
var cutFront = 0, cutEnd = 0
|
| 9508 |
-
var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
|
| 9509 |
-
while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
|
| 9510 |
-
{ ++cutFront }
|
| 9511 |
-
var newBot = lst(newText), oldBot = lst(oldText)
|
| 9512 |
-
var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
|
| 9513 |
-
oldBot.length - (oldText.length == 1 ? cutFront : 0))
|
| 9514 |
-
while (cutEnd < maxCutEnd &&
|
| 9515 |
-
newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
|
| 9516 |
-
{ ++cutEnd }
|
| 9517 |
-
|
| 9518 |
-
newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
|
| 9519 |
-
newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
|
| 9520 |
-
|
| 9521 |
-
var chFrom = Pos(fromLine, cutFront)
|
| 9522 |
-
var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
|
| 9523 |
-
if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
|
| 9524 |
-
replaceRange(cm.doc, newText, chFrom, chTo, "+input")
|
| 9525 |
-
return true
|
| 9526 |
-
}
|
| 9527 |
-
},
|
| 9528 |
|
| 9529 |
-
|
| 9530 |
-
|
| 9531 |
-
|
| 9532 |
-
|
| 9533 |
-
|
| 9534 |
-
|
| 9535 |
-
|
| 9536 |
-
|
| 9537 |
-
|
| 9538 |
-
|
| 9539 |
-
|
| 9540 |
-
|
| 9541 |
-
|
| 9542 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9543 |
var this$1 = this;
|
| 9544 |
|
| 9545 |
-
|
| 9546 |
-
|
| 9547 |
-
|
| 9548 |
-
|
| 9549 |
-
if (this$1.
|
| 9550 |
-
|
| 9551 |
-
}
|
| 9552 |
-
|
|
|
|
|
|
|
|
|
|
| 9553 |
|
| 9554 |
-
|
| 9555 |
-
|
| 9556 |
-
|
| 9557 |
|
| 9558 |
-
|
| 9559 |
-
|
| 9560 |
-
|
| 9561 |
-
|
| 9562 |
-
|
| 9563 |
|
| 9564 |
-
|
| 9565 |
-
|
| 9566 |
-
|
| 9567 |
|
| 9568 |
-
|
| 9569 |
-
|
| 9570 |
|
| 9571 |
-
|
| 9572 |
-
}, ContentEditableInput.prototype)
|
| 9573 |
|
| 9574 |
function posToDOM(cm, pos) {
|
| 9575 |
var view = findViewForLine(cm, pos.line)
|
|
@@ -9706,7 +9711,7 @@ webpackJsonp([1],{
|
|
| 9706 |
|
| 9707 |
// TEXTAREA INPUT STYLE
|
| 9708 |
|
| 9709 |
-
|
| 9710 |
this.cm = cm
|
| 9711 |
// See input.poll and input.reset
|
| 9712 |
this.prevInput = ""
|
|
@@ -9723,335 +9728,333 @@ webpackJsonp([1],{
|
|
| 9723 |
// Used to work around IE issue with selection being forgotten when focus moves away from textarea
|
| 9724 |
this.hasSelection = false
|
| 9725 |
this.composing = null
|
| 9726 |
-
}
|
| 9727 |
|
| 9728 |
-
TextareaInput.prototype =
|
| 9729 |
-
init: function(display) {
|
| 9730 |
var this$1 = this;
|
| 9731 |
|
| 9732 |
-
|
| 9733 |
|
| 9734 |
-
|
| 9735 |
-
|
| 9736 |
-
|
| 9737 |
-
|
| 9738 |
-
|
| 9739 |
-
|
| 9740 |
|
| 9741 |
-
|
| 9742 |
-
|
| 9743 |
|
| 9744 |
-
|
| 9745 |
-
|
| 9746 |
-
|
| 9747 |
-
|
| 9748 |
|
| 9749 |
-
|
| 9750 |
-
|
| 9751 |
|
| 9752 |
-
|
| 9753 |
-
|
| 9754 |
-
|
| 9755 |
|
| 9756 |
-
|
| 9757 |
-
|
| 9758 |
-
|
| 9759 |
-
|
| 9760 |
-
|
| 9761 |
-
|
| 9762 |
-
|
| 9763 |
-
|
| 9764 |
-
|
| 9765 |
-
|
| 9766 |
-
|
| 9767 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9768 |
} else {
|
| 9769 |
-
|
| 9770 |
-
|
| 9771 |
-
|
| 9772 |
-
cm.setSelections(ranges.ranges, null, sel_dontScroll)
|
| 9773 |
-
} else {
|
| 9774 |
-
input.prevInput = ""
|
| 9775 |
-
te.value = ranges.text.join("\n")
|
| 9776 |
-
selectInput(te)
|
| 9777 |
-
}
|
| 9778 |
}
|
| 9779 |
-
if (e.type == "cut") { cm.state.cutIncoming = true }
|
| 9780 |
}
|
| 9781 |
-
|
| 9782 |
-
|
| 9783 |
-
|
| 9784 |
-
|
| 9785 |
-
if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
|
| 9786 |
-
cm.state.pasteIncoming = true
|
| 9787 |
-
input.focus()
|
| 9788 |
-
})
|
| 9789 |
-
|
| 9790 |
-
// Prevent normal selection in the editor (we handle our own)
|
| 9791 |
-
on(display.lineSpace, "selectstart", function (e) {
|
| 9792 |
-
if (!eventInWidget(display, e)) { e_preventDefault(e) }
|
| 9793 |
-
})
|
| 9794 |
|
| 9795 |
-
|
| 9796 |
-
|
| 9797 |
-
|
| 9798 |
-
|
| 9799 |
-
|
| 9800 |
-
range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
|
| 9801 |
-
}
|
| 9802 |
-
})
|
| 9803 |
-
on(te, "compositionend", function () {
|
| 9804 |
-
if (input.composing) {
|
| 9805 |
-
input.poll()
|
| 9806 |
-
input.composing.range.clear()
|
| 9807 |
-
input.composing = null
|
| 9808 |
-
}
|
| 9809 |
-
})
|
| 9810 |
-
},
|
| 9811 |
|
| 9812 |
-
|
| 9813 |
-
|
| 9814 |
-
|
| 9815 |
-
|
| 9816 |
|
| 9817 |
-
|
| 9818 |
-
|
| 9819 |
-
|
| 9820 |
-
|
| 9821 |
-
|
| 9822 |
-
|
| 9823 |
-
result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
| 9824 |
-
headPos.left + lineOff.left - wrapOff.left))
|
| 9825 |
}
|
| 9826 |
-
|
| 9827 |
-
|
| 9828 |
-
|
| 9829 |
-
|
| 9830 |
-
|
| 9831 |
-
|
| 9832 |
-
removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
|
| 9833 |
-
removeChildrenAndAdd(display.selectionDiv, drawn.selection)
|
| 9834 |
-
if (drawn.teTop != null) {
|
| 9835 |
-
this.wrapper.style.top = drawn.teTop + "px"
|
| 9836 |
-
this.wrapper.style.left = drawn.teLeft + "px"
|
| 9837 |
}
|
| 9838 |
-
}
|
| 9839 |
-
|
| 9840 |
-
// Reset the input to correspond to the selection (or to be empty,
|
| 9841 |
-
// when not typing and nothing is selected)
|
| 9842 |
-
reset: function(typing) {
|
| 9843 |
-
if (this.contextMenuPending) { return }
|
| 9844 |
-
var minimal, selected, cm = this.cm, doc = cm.doc
|
| 9845 |
-
if (cm.somethingSelected()) {
|
| 9846 |
-
this.prevInput = ""
|
| 9847 |
-
var range = doc.sel.primary()
|
| 9848 |
-
minimal = hasCopyEvent &&
|
| 9849 |
-
(range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000)
|
| 9850 |
-
var content = minimal ? "-" : selected || cm.getSelection()
|
| 9851 |
-
this.textarea.value = content
|
| 9852 |
-
if (cm.state.focused) { selectInput(this.textarea) }
|
| 9853 |
-
if (ie && ie_version >= 9) { this.hasSelection = content }
|
| 9854 |
-
} else if (!typing) {
|
| 9855 |
-
this.prevInput = this.textarea.value = ""
|
| 9856 |
-
if (ie && ie_version >= 9) { this.hasSelection = null }
|
| 9857 |
-
}
|
| 9858 |
-
this.inaccurateSelection = minimal
|
| 9859 |
-
},
|
| 9860 |
|
| 9861 |
-
|
|
|
|
|
|
|
|
|
|
| 9862 |
|
| 9863 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9864 |
|
| 9865 |
-
|
| 9866 |
-
|
| 9867 |
-
|
| 9868 |
-
|
| 9869 |
-
|
| 9870 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9871 |
|
| 9872 |
-
|
| 9873 |
|
| 9874 |
-
|
| 9875 |
-
this.wrapper.style.top = this.wrapper.style.left = 0
|
| 9876 |
-
},
|
| 9877 |
|
| 9878 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9879 |
|
| 9880 |
-
|
| 9881 |
-
// runs as long as the editor is focused.
|
| 9882 |
-
slowPoll: function() {
|
| 9883 |
-
var this$1 = this;
|
| 9884 |
|
| 9885 |
-
|
| 9886 |
-
|
| 9887 |
-
|
| 9888 |
-
if (this$1.cm.state.focused) { this$1.slowPoll() }
|
| 9889 |
-
})
|
| 9890 |
-
},
|
| 9891 |
|
| 9892 |
-
|
| 9893 |
-
// something in the input textarea, we poll faster, to ensure that
|
| 9894 |
-
// the change appears on the screen quickly.
|
| 9895 |
-
fastPoll: function() {
|
| 9896 |
-
var missed = false, input = this
|
| 9897 |
-
input.pollingFast = true
|
| 9898 |
-
function p() {
|
| 9899 |
-
var changed = input.poll()
|
| 9900 |
-
if (!changed && !missed) {missed = true; input.polling.set(60, p)}
|
| 9901 |
-
else {input.pollingFast = false; input.slowPoll()}
|
| 9902 |
-
}
|
| 9903 |
-
input.polling.set(20, p)
|
| 9904 |
-
},
|
| 9905 |
|
| 9906 |
-
|
| 9907 |
-
|
| 9908 |
-
|
| 9909 |
-
// used). When nothing is selected, the cursor sits after previously
|
| 9910 |
-
// seen text (can be empty), which is stored in prevInput (we must
|
| 9911 |
-
// not reset the textarea when typing, because that breaks IME).
|
| 9912 |
-
poll: function() {
|
| 9913 |
var this$1 = this;
|
| 9914 |
|
| 9915 |
-
|
| 9916 |
-
|
| 9917 |
-
|
| 9918 |
-
|
| 9919 |
-
|
| 9920 |
-
|
| 9921 |
-
|
| 9922 |
-
|
| 9923 |
-
|
| 9924 |
-
|
| 9925 |
-
|
| 9926 |
-
|
| 9927 |
-
|
| 9928 |
-
|
| 9929 |
-
|
| 9930 |
-
|
| 9931 |
-
|
| 9932 |
-
|
| 9933 |
-
|
| 9934 |
-
|
| 9935 |
-
|
| 9936 |
-
|
| 9937 |
-
|
| 9938 |
-
|
| 9939 |
-
|
| 9940 |
-
|
| 9941 |
-
|
| 9942 |
-
|
| 9943 |
-
var
|
| 9944 |
-
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
|
| 9945 |
|
| 9946 |
-
|
| 9947 |
-
|
| 9948 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9949 |
|
| 9950 |
-
|
| 9951 |
-
|
| 9952 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9953 |
|
| 9954 |
-
|
| 9955 |
-
|
| 9956 |
-
|
| 9957 |
-
|
| 9958 |
-
|
| 9959 |
-
|
| 9960 |
-
|
| 9961 |
-
}
|
| 9962 |
|
| 9963 |
-
|
| 9964 |
-
|
| 9965 |
-
|
| 9966 |
|
| 9967 |
-
|
| 9968 |
-
if (
|
| 9969 |
-
this.
|
| 9970 |
-
},
|
| 9971 |
|
| 9972 |
-
|
| 9973 |
-
|
| 9974 |
-
|
| 9975 |
-
|
| 9976 |
-
|
| 9977 |
-
// Reset the current text selection only if the click is done outside of the selection
|
| 9978 |
-
// and 'resetSelectionOnContextMenu' option is true.
|
| 9979 |
-
var reset = cm.options.resetSelectionOnContextMenu
|
| 9980 |
-
if (reset && cm.doc.sel.contains(pos) == -1)
|
| 9981 |
-
{ operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
|
| 9982 |
-
|
| 9983 |
-
var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
|
| 9984 |
-
input.wrapper.style.cssText = "position: absolute"
|
| 9985 |
-
var wrapperBox = input.wrapper.getBoundingClientRect()
|
| 9986 |
-
te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
|
| 9987 |
-
var oldScrollY
|
| 9988 |
-
if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
|
| 9989 |
-
display.input.focus()
|
| 9990 |
-
if (webkit) { window.scrollTo(null, oldScrollY) }
|
| 9991 |
-
display.input.reset()
|
| 9992 |
-
// Adds "Select all" to context menu in FF
|
| 9993 |
-
if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
|
| 9994 |
-
input.contextMenuPending = true
|
| 9995 |
-
display.selForContextMenu = cm.doc.sel
|
| 9996 |
-
clearTimeout(display.detectingSelectAll)
|
| 9997 |
-
|
| 9998 |
-
// Select-all will be greyed out if there's nothing to select, so
|
| 9999 |
-
// this adds a zero-width space so that we can later check whether
|
| 10000 |
-
// it got selected.
|
| 10001 |
-
function prepareSelectAllHack() {
|
| 10002 |
-
if (te.selectionStart != null) {
|
| 10003 |
-
var selected = cm.somethingSelected()
|
| 10004 |
-
var extval = "\u200b" + (selected ? te.value : "")
|
| 10005 |
-
te.value = "\u21da" // Used to catch context-menu undo
|
| 10006 |
-
te.value = extval
|
| 10007 |
-
input.prevInput = selected ? "" : "\u200b"
|
| 10008 |
-
te.selectionStart = 1; te.selectionEnd = extval.length
|
| 10009 |
-
// Re-set this, in case some other handler touched the
|
| 10010 |
-
// selection in the meantime.
|
| 10011 |
-
display.selForContextMenu = cm.doc.sel
|
| 10012 |
-
}
|
| 10013 |
}
|
| 10014 |
-
|
| 10015 |
-
|
| 10016 |
-
|
| 10017 |
-
|
| 10018 |
-
|
| 10019 |
-
|
| 10020 |
-
|
| 10021 |
-
|
| 10022 |
-
|
| 10023 |
-
|
| 10024 |
-
|
| 10025 |
-
|
| 10026 |
-
|
| 10027 |
-
|
| 10028 |
-
|
| 10029 |
-
|
| 10030 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10031 |
}
|
|
|
|
| 10032 |
}
|
|
|
|
| 10033 |
|
| 10034 |
-
|
| 10035 |
-
|
| 10036 |
-
|
| 10037 |
-
|
| 10038 |
-
|
| 10039 |
-
|
| 10040 |
-
}
|
| 10041 |
-
on(window, "mouseup", mouseup)
|
| 10042 |
-
} else {
|
| 10043 |
-
setTimeout(rehide, 50)
|
| 10044 |
}
|
| 10045 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10046 |
|
| 10047 |
-
|
| 10048 |
-
|
| 10049 |
-
|
| 10050 |
|
| 10051 |
-
|
| 10052 |
|
| 10053 |
-
|
| 10054 |
-
}, TextareaInput.prototype)
|
| 10055 |
|
| 10056 |
function fromTextArea(textarea, options) {
|
| 10057 |
options = options ? copyObj(options) : {}
|
|
@@ -10202,7 +10205,7 @@ webpackJsonp([1],{
|
|
| 10202 |
|
| 10203 |
addLegacyProps(CodeMirror)
|
| 10204 |
|
| 10205 |
-
CodeMirror.version = "5.
|
| 10206 |
|
| 10207 |
return CodeMirror;
|
| 10208 |
|
|
@@ -10739,7 +10742,7 @@ webpackJsonp([1],{
|
|
| 10739 |
"transition-property", "transition-timing-function", "unicode-bidi",
|
| 10740 |
"user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
|
| 10741 |
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
|
| 10742 |
-
"voice-volume", "volume", "white-space", "widows", "width", "word-break",
|
| 10743 |
"word-spacing", "word-wrap", "z-index",
|
| 10744 |
// SVG-specific
|
| 10745 |
"clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
|
|
@@ -10813,7 +10816,7 @@ webpackJsonp([1],{
|
|
| 10813 |
"cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
|
| 10814 |
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
|
| 10815 |
"col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
|
| 10816 |
-
"compact", "condensed", "contain", "content",
|
| 10817 |
"content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
|
| 10818 |
"cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
|
| 10819 |
"decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
|
|
@@ -10856,7 +10859,7 @@ webpackJsonp([1],{
|
|
| 10856 |
"mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
|
| 10857 |
"narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
|
| 10858 |
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
|
| 10859 |
-
"ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
|
| 10860 |
"optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
|
| 10861 |
"outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
|
| 10862 |
"painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
|
|
@@ -10868,7 +10871,7 @@ webpackJsonp([1],{
|
|
| 10868 |
"rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
|
| 10869 |
"rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
|
| 10870 |
"s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
|
| 10871 |
-
"scroll", "scrollbar", "se-resize", "searchfield",
|
| 10872 |
"searchfield-cancel-button", "searchfield-decoration",
|
| 10873 |
"searchfield-results-button", "searchfield-results-decoration",
|
| 10874 |
"semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
|
|
@@ -10886,9 +10889,9 @@ webpackJsonp([1],{
|
|
| 10886 |
"thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
|
| 10887 |
"threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
|
| 10888 |
"tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
|
| 10889 |
-
"trad-chinese-formal", "trad-chinese-informal",
|
| 10890 |
"translate", "translate3d", "translateX", "translateY", "translateZ",
|
| 10891 |
-
"transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
|
| 10892 |
"upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
|
| 10893 |
"upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
|
| 10894 |
"var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
|
| 2077 |
var bidiOrdering = (function() {
|
| 2078 |
// Character types for codepoints 0 to 0xff
|
| 2079 |
var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"
|
| 2080 |
+
// Character types for codepoints 0x600 to 0x6f9
|
| 2081 |
+
var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"
|
| 2082 |
function charType(code) {
|
| 2083 |
if (code <= 0xf7) { return lowTypes.charAt(code) }
|
| 2084 |
else if (0x590 <= code && code <= 0x5f4) { return "R" }
|
| 2085 |
+
else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) }
|
| 2086 |
else if (0x6ee <= code && code <= 0x8ac) { return "r" }
|
| 2087 |
else if (0x2000 <= code && code <= 0x200b) { return "w" }
|
| 2088 |
else if (code == 0x200c) { return "b" }
|
| 4388 |
}
|
| 4389 |
}
|
| 4390 |
|
| 4391 |
+
var NativeScrollbars = function(place, scroll, cm) {
|
| 4392 |
this.cm = cm
|
| 4393 |
var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar")
|
| 4394 |
var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar")
|
| 4404 |
this.checkedZeroWidth = false
|
| 4405 |
// Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
|
| 4406 |
if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px" }
|
| 4407 |
+
};
|
| 4408 |
+
|
| 4409 |
+
NativeScrollbars.prototype.update = function (measure) {
|
| 4410 |
+
var needsH = measure.scrollWidth > measure.clientWidth + 1
|
| 4411 |
+
var needsV = measure.scrollHeight > measure.clientHeight + 1
|
| 4412 |
+
var sWidth = measure.nativeBarWidth
|
| 4413 |
+
|
| 4414 |
+
if (needsV) {
|
| 4415 |
+
this.vert.style.display = "block"
|
| 4416 |
+
this.vert.style.bottom = needsH ? sWidth + "px" : "0"
|
| 4417 |
+
var totalHeight = measure.viewHeight - (needsH ? sWidth : 0)
|
| 4418 |
+
// A bug in IE8 can cause this value to be negative, so guard it.
|
| 4419 |
+
this.vert.firstChild.style.height =
|
| 4420 |
+
Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"
|
| 4421 |
+
} else {
|
| 4422 |
+
this.vert.style.display = ""
|
| 4423 |
+
this.vert.firstChild.style.height = "0"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4424 |
}
|
|
|
|
|
|
|
|
|
|
| 4425 |
|
| 4426 |
+
if (needsH) {
|
| 4427 |
+
this.horiz.style.display = "block"
|
| 4428 |
+
this.horiz.style.right = needsV ? sWidth + "px" : "0"
|
| 4429 |
+
this.horiz.style.left = measure.barLeft + "px"
|
| 4430 |
+
var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0)
|
| 4431 |
+
this.horiz.firstChild.style.width =
|
| 4432 |
+
(measure.scrollWidth - measure.clientWidth + totalWidth) + "px"
|
| 4433 |
+
} else {
|
| 4434 |
+
this.horiz.style.display = ""
|
| 4435 |
+
this.horiz.firstChild.style.width = "0"
|
| 4436 |
+
}
|
| 4437 |
+
|
| 4438 |
+
if (!this.checkedZeroWidth && measure.clientHeight > 0) {
|
| 4439 |
+
if (sWidth == 0) { this.zeroWidthHack() }
|
| 4440 |
+
this.checkedZeroWidth = true
|
| 4441 |
+
}
|
| 4442 |
+
|
| 4443 |
+
return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0}
|
| 4444 |
+
};
|
| 4445 |
+
|
| 4446 |
+
NativeScrollbars.prototype.setScrollLeft = function (pos) {
|
| 4447 |
+
if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos }
|
| 4448 |
+
if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz) }
|
| 4449 |
+
};
|
| 4450 |
+
|
| 4451 |
+
NativeScrollbars.prototype.setScrollTop = function (pos) {
|
| 4452 |
+
if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos }
|
| 4453 |
+
if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert) }
|
| 4454 |
+
};
|
| 4455 |
+
|
| 4456 |
+
NativeScrollbars.prototype.zeroWidthHack = function () {
|
| 4457 |
+
var w = mac && !mac_geMountainLion ? "12px" : "18px"
|
| 4458 |
+
this.horiz.style.height = this.vert.style.width = w
|
| 4459 |
+
this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"
|
| 4460 |
+
this.disableHoriz = new Delayed
|
| 4461 |
+
this.disableVert = new Delayed
|
| 4462 |
+
};
|
| 4463 |
+
|
| 4464 |
+
NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay) {
|
| 4465 |
+
bar.style.pointerEvents = "auto"
|
| 4466 |
+
function maybeDisable() {
|
| 4467 |
+
// To find out whether the scrollbar is still visible, we
|
| 4468 |
+
// check whether the element under the pixel in the bottom
|
| 4469 |
+
// left corner of the scrollbar box is the scrollbar box
|
| 4470 |
+
// itself (when the bar is still visible) or its filler child
|
| 4471 |
+
// (when the bar is hidden). If it is still visible, we keep
|
| 4472 |
+
// it enabled, if it's hidden, we disable pointer events.
|
| 4473 |
+
var box = bar.getBoundingClientRect()
|
| 4474 |
+
var elt = document.elementFromPoint(box.left + 1, box.bottom - 1)
|
| 4475 |
+
if (elt != bar) { bar.style.pointerEvents = "none" }
|
| 4476 |
+
else { delay.set(1000, maybeDisable) }
|
| 4477 |
+
}
|
| 4478 |
+
delay.set(1000, maybeDisable)
|
| 4479 |
+
};
|
| 4480 |
+
|
| 4481 |
+
NativeScrollbars.prototype.clear = function () {
|
| 4482 |
+
var parent = this.horiz.parentNode
|
| 4483 |
+
parent.removeChild(this.horiz)
|
| 4484 |
+
parent.removeChild(this.vert)
|
| 4485 |
+
};
|
| 4486 |
+
|
| 4487 |
+
var NullScrollbars = function () {};
|
| 4488 |
+
|
| 4489 |
+
NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} };
|
| 4490 |
+
NullScrollbars.prototype.setScrollLeft = function () {};
|
| 4491 |
+
NullScrollbars.prototype.setScrollTop = function () {};
|
| 4492 |
+
NullScrollbars.prototype.clear = function () {};
|
| 4493 |
|
| 4494 |
function updateScrollbars(cm, measure) {
|
| 4495 |
if (!measure) { measure = measureForScrollbars(cm) }
|
| 5068 |
|
| 5069 |
// DISPLAY DRAWING
|
| 5070 |
|
| 5071 |
+
var DisplayUpdate = function(cm, viewport, force) {
|
| 5072 |
var display = cm.display
|
| 5073 |
|
| 5074 |
this.viewport = viewport
|
| 5081 |
this.force = force
|
| 5082 |
this.dims = getDimensions(cm)
|
| 5083 |
this.events = []
|
| 5084 |
+
};
|
| 5085 |
|
| 5086 |
+
DisplayUpdate.prototype.signal = function (emitter, type) {
|
| 5087 |
if (hasHandler(emitter, type))
|
| 5088 |
{ this.events.push(arguments) }
|
| 5089 |
+
};
|
| 5090 |
+
DisplayUpdate.prototype.finish = function () {
|
| 5091 |
+
var this$1 = this;
|
| 5092 |
|
| 5093 |
for (var i = 0; i < this.events.length; i++)
|
| 5094 |
{ signal.apply(null, this$1.events[i]) }
|
| 5095 |
+
};
|
| 5096 |
|
| 5097 |
function maybeClipScrollbars(cm) {
|
| 5098 |
var display = cm.display
|
| 8220 |
for (var i = newBreaks.length - 1; i >= 0; i--)
|
| 8221 |
{ replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)) }
|
| 8222 |
})
|
| 8223 |
+
option("specialChars", /[\u0000-\u001f\u007f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) {
|
| 8224 |
cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g")
|
| 8225 |
if (old != Init) { cm.refresh() }
|
| 8226 |
})
|
| 8310 |
function guttersChanged(cm) {
|
| 8311 |
updateGutters(cm)
|
| 8312 |
regChange(cm)
|
| 8313 |
+
alignHorizontally(cm)
|
| 8314 |
}
|
| 8315 |
|
| 8316 |
function dragDropChanged(cm, value, old) {
|
| 8365 |
themeChanged(this)
|
| 8366 |
if (options.lineWrapping)
|
| 8367 |
{ this.display.wrapper.className += " CodeMirror-wrap" }
|
|
|
|
| 8368 |
initScrollbars(this)
|
| 8369 |
|
| 8370 |
this.state = {
|
| 8383 |
specialChars: null
|
| 8384 |
}
|
| 8385 |
|
| 8386 |
+
if (options.autofocus && !mobile) { display.input.focus() }
|
| 8387 |
+
|
| 8388 |
// Override magic textarea content restore that IE sometimes does
|
| 8389 |
// on our hidden textarea on reload
|
| 8390 |
if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20) }
|
| 8740 |
options[option] = value
|
| 8741 |
if (optionHandlers.hasOwnProperty(option))
|
| 8742 |
{ operation(this, optionHandlers[option])(this, value, old) }
|
| 8743 |
+
signal(this, "optionChange", this, option)
|
| 8744 |
},
|
| 8745 |
|
| 8746 |
getOption: function(option) {return this.options[option]},
|
| 9248 |
|
| 9249 |
// CONTENTEDITABLE INPUT STYLE
|
| 9250 |
|
| 9251 |
+
var ContentEditableInput = function(cm) {
|
| 9252 |
this.cm = cm
|
| 9253 |
this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null
|
| 9254 |
this.polling = new Delayed()
|
| 9255 |
this.composing = null
|
| 9256 |
this.gracePeriod = false
|
| 9257 |
this.readDOMTimeout = null
|
| 9258 |
+
};
|
| 9259 |
|
| 9260 |
+
ContentEditableInput.prototype.init = function (display) {
|
|
|
|
| 9261 |
var this$1 = this;
|
| 9262 |
|
| 9263 |
+
var input = this, cm = input.cm
|
| 9264 |
+
var div = input.div = display.lineDiv
|
| 9265 |
+
disableBrowserMagic(div, cm.options.spellcheck)
|
| 9266 |
|
| 9267 |
+
on(div, "paste", function (e) {
|
| 9268 |
+
if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
|
| 9269 |
+
// IE doesn't fire input events, so we schedule a read for the pasted content in this way
|
| 9270 |
+
if (ie_version <= 11) { setTimeout(operation(cm, function () {
|
| 9271 |
+
if (!input.pollContent()) { regChange(cm) }
|
| 9272 |
+
}), 20) }
|
| 9273 |
+
})
|
| 9274 |
|
| 9275 |
+
on(div, "compositionstart", function (e) {
|
| 9276 |
+
this$1.composing = {data: e.data, done: false}
|
| 9277 |
+
})
|
| 9278 |
+
on(div, "compositionupdate", function (e) {
|
| 9279 |
+
if (!this$1.composing) { this$1.composing = {data: e.data, done: false} }
|
| 9280 |
+
})
|
| 9281 |
+
on(div, "compositionend", function (e) {
|
| 9282 |
+
if (this$1.composing) {
|
| 9283 |
+
if (e.data != this$1.composing.data) { this$1.readFromDOMSoon() }
|
| 9284 |
+
this$1.composing.done = true
|
| 9285 |
+
}
|
| 9286 |
+
})
|
| 9287 |
|
| 9288 |
+
on(div, "touchstart", function () { return input.forceCompositionEnd(); })
|
| 9289 |
|
| 9290 |
+
on(div, "input", function () {
|
| 9291 |
+
if (!this$1.composing) { this$1.readFromDOMSoon() }
|
| 9292 |
+
})
|
| 9293 |
|
| 9294 |
+
function onCopyCut(e) {
|
| 9295 |
+
if (signalDOMEvent(cm, e)) { return }
|
| 9296 |
+
if (cm.somethingSelected()) {
|
| 9297 |
+
setLastCopied({lineWise: false, text: cm.getSelections()})
|
| 9298 |
+
if (e.type == "cut") { cm.replaceSelection("", null, "cut") }
|
| 9299 |
+
} else if (!cm.options.lineWiseCopyCut) {
|
| 9300 |
+
return
|
| 9301 |
+
} else {
|
| 9302 |
+
var ranges = copyableRanges(cm)
|
| 9303 |
+
setLastCopied({lineWise: true, text: ranges.text})
|
| 9304 |
+
if (e.type == "cut") {
|
| 9305 |
+
cm.operation(function () {
|
| 9306 |
+
cm.setSelections(ranges.ranges, 0, sel_dontScroll)
|
| 9307 |
+
cm.replaceSelection("", null, "cut")
|
| 9308 |
+
})
|
|
|
|
| 9309 |
}
|
| 9310 |
+
}
|
| 9311 |
+
if (e.clipboardData) {
|
| 9312 |
+
e.clipboardData.clearData()
|
| 9313 |
+
var content = lastCopied.text.join("\n")
|
| 9314 |
+
// iOS exposes the clipboard API, but seems to discard content inserted into it
|
| 9315 |
+
e.clipboardData.setData("Text", content)
|
| 9316 |
+
if (e.clipboardData.getData("Text") == content) {
|
| 9317 |
+
e.preventDefault()
|
| 9318 |
+
return
|
| 9319 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9320 |
}
|
| 9321 |
+
// Old-fashioned briefly-focus-a-textarea hack
|
| 9322 |
+
var kludge = hiddenTextarea(), te = kludge.firstChild
|
| 9323 |
+
cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild)
|
| 9324 |
+
te.value = lastCopied.text.join("\n")
|
| 9325 |
+
var hadFocus = document.activeElement
|
| 9326 |
+
selectInput(te)
|
| 9327 |
+
setTimeout(function () {
|
| 9328 |
+
cm.display.lineSpace.removeChild(kludge)
|
| 9329 |
+
hadFocus.focus()
|
| 9330 |
+
if (hadFocus == div) { input.showPrimarySelection() }
|
| 9331 |
+
}, 50)
|
| 9332 |
+
}
|
| 9333 |
+
on(div, "copy", onCopyCut)
|
| 9334 |
+
on(div, "cut", onCopyCut)
|
| 9335 |
+
};
|
| 9336 |
+
|
| 9337 |
+
ContentEditableInput.prototype.prepareSelection = function () {
|
| 9338 |
+
var result = prepareSelection(this.cm, false)
|
| 9339 |
+
result.focus = this.cm.state.focused
|
| 9340 |
+
return result
|
| 9341 |
+
};
|
| 9342 |
+
|
| 9343 |
+
ContentEditableInput.prototype.showSelection = function (info, takeFocus) {
|
| 9344 |
+
if (!info || !this.cm.display.view.length) { return }
|
| 9345 |
+
if (info.focus || takeFocus) { this.showPrimarySelection() }
|
| 9346 |
+
this.showMultipleSelections(info)
|
| 9347 |
+
};
|
| 9348 |
+
|
| 9349 |
+
ContentEditableInput.prototype.showPrimarySelection = function () {
|
| 9350 |
+
var sel = window.getSelection(), prim = this.cm.doc.sel.primary()
|
| 9351 |
+
var curAnchor = domToPos(this.cm, sel.anchorNode, sel.anchorOffset)
|
| 9352 |
+
var curFocus = domToPos(this.cm, sel.focusNode, sel.focusOffset)
|
| 9353 |
+
if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad &&
|
| 9354 |
+
cmp(minPos(curAnchor, curFocus), prim.from()) == 0 &&
|
| 9355 |
+
cmp(maxPos(curAnchor, curFocus), prim.to()) == 0)
|
| 9356 |
+
{ return }
|
| 9357 |
|
| 9358 |
+
var start = posToDOM(this.cm, prim.from())
|
| 9359 |
+
var end = posToDOM(this.cm, prim.to())
|
| 9360 |
+
if (!start && !end) { return }
|
| 9361 |
+
|
| 9362 |
+
var view = this.cm.display.view
|
| 9363 |
+
var old = sel.rangeCount && sel.getRangeAt(0)
|
| 9364 |
+
if (!start) {
|
| 9365 |
+
start = {node: view[0].measure.map[2], offset: 0}
|
| 9366 |
+
} else if (!end) { // FIXME dangerously hacky
|
| 9367 |
+
var measure = view[view.length - 1].measure
|
| 9368 |
+
var map = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map
|
| 9369 |
+
end = {node: map[map.length - 1], offset: map[map.length - 2] - map[map.length - 3]}
|
| 9370 |
+
}
|
| 9371 |
+
|
| 9372 |
+
var rng
|
| 9373 |
+
try { rng = range(start.node, start.offset, end.offset, end.node) }
|
| 9374 |
+
catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible
|
| 9375 |
+
if (rng) {
|
| 9376 |
+
if (!gecko && this.cm.state.focused) {
|
| 9377 |
+
sel.collapse(start.node, start.offset)
|
| 9378 |
+
if (!rng.collapsed) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9379 |
sel.removeAllRanges()
|
| 9380 |
sel.addRange(rng)
|
| 9381 |
}
|
| 9382 |
+
} else {
|
| 9383 |
+
sel.removeAllRanges()
|
| 9384 |
+
sel.addRange(rng)
|
| 9385 |
}
|
| 9386 |
+
if (old && sel.anchorNode == null) { sel.addRange(old) }
|
| 9387 |
+
else if (gecko) { this.startGracePeriod() }
|
| 9388 |
+
}
|
| 9389 |
+
this.rememberSelection()
|
| 9390 |
+
};
|
| 9391 |
|
| 9392 |
+
ContentEditableInput.prototype.startGracePeriod = function () {
|
| 9393 |
var this$1 = this;
|
| 9394 |
|
| 9395 |
+
clearTimeout(this.gracePeriod)
|
| 9396 |
+
this.gracePeriod = setTimeout(function () {
|
| 9397 |
+
this$1.gracePeriod = false
|
| 9398 |
+
if (this$1.selectionChanged())
|
| 9399 |
+
{ this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }) }
|
| 9400 |
+
}, 20)
|
| 9401 |
+
};
|
| 9402 |
+
|
| 9403 |
+
ContentEditableInput.prototype.showMultipleSelections = function (info) {
|
| 9404 |
+
removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors)
|
| 9405 |
+
removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection)
|
| 9406 |
+
};
|
| 9407 |
+
|
| 9408 |
+
ContentEditableInput.prototype.rememberSelection = function () {
|
| 9409 |
+
var sel = window.getSelection()
|
| 9410 |
+
this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset
|
| 9411 |
+
this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset
|
| 9412 |
+
};
|
| 9413 |
+
|
| 9414 |
+
ContentEditableInput.prototype.selectionInEditor = function () {
|
| 9415 |
+
var sel = window.getSelection()
|
| 9416 |
+
if (!sel.rangeCount) { return false }
|
| 9417 |
+
var node = sel.getRangeAt(0).commonAncestorContainer
|
| 9418 |
+
return contains(this.div, node)
|
| 9419 |
+
};
|
| 9420 |
+
|
| 9421 |
+
ContentEditableInput.prototype.focus = function () {
|
| 9422 |
+
if (this.cm.options.readOnly != "nocursor") {
|
| 9423 |
+
if (!this.selectionInEditor())
|
| 9424 |
+
{ this.showSelection(this.prepareSelection(), true) }
|
| 9425 |
+
this.div.focus()
|
| 9426 |
+
}
|
| 9427 |
+
};
|
| 9428 |
+
ContentEditableInput.prototype.blur = function () { this.div.blur() };
|
| 9429 |
+
ContentEditableInput.prototype.getField = function () { return this.div };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9430 |
|
| 9431 |
+
ContentEditableInput.prototype.supportsTouch = function () { return true };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9432 |
|
| 9433 |
+
ContentEditableInput.prototype.receivedFocus = function () {
|
| 9434 |
+
var input = this
|
| 9435 |
+
if (this.selectionInEditor())
|
| 9436 |
+
{ this.pollSelection() }
|
| 9437 |
+
else
|
| 9438 |
+
{ runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }) }
|
| 9439 |
|
| 9440 |
+
function poll() {
|
| 9441 |
+
if (input.cm.state.focused) {
|
| 9442 |
+
input.pollSelection()
|
| 9443 |
+
input.polling.set(input.cm.options.pollInterval, poll)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9444 |
}
|
| 9445 |
+
}
|
| 9446 |
+
this.polling.set(this.cm.options.pollInterval, poll)
|
| 9447 |
+
};
|
| 9448 |
|
| 9449 |
+
ContentEditableInput.prototype.selectionChanged = function () {
|
| 9450 |
+
var sel = window.getSelection()
|
| 9451 |
+
return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset ||
|
| 9452 |
+
sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset
|
| 9453 |
+
};
|
| 9454 |
|
| 9455 |
+
ContentEditableInput.prototype.pollSelection = function () {
|
| 9456 |
+
if (!this.composing && this.readDOMTimeout == null && !this.gracePeriod && this.selectionChanged()) {
|
| 9457 |
+
var sel = window.getSelection(), cm = this.cm
|
| 9458 |
+
this.rememberSelection()
|
| 9459 |
+
var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset)
|
| 9460 |
+
var head = domToPos(cm, sel.focusNode, sel.focusOffset)
|
| 9461 |
+
if (anchor && head) { runInOp(cm, function () {
|
| 9462 |
+
setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll)
|
| 9463 |
+
if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true }
|
| 9464 |
+
}) }
|
| 9465 |
+
}
|
| 9466 |
+
};
|
| 9467 |
+
|
| 9468 |
+
ContentEditableInput.prototype.pollContent = function () {
|
| 9469 |
+
if (this.readDOMTimeout != null) {
|
| 9470 |
+
clearTimeout(this.readDOMTimeout)
|
| 9471 |
+
this.readDOMTimeout = null
|
| 9472 |
+
}
|
| 9473 |
+
|
| 9474 |
+
var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary()
|
| 9475 |
+
var from = sel.from(), to = sel.to()
|
| 9476 |
+
if (from.ch == 0 && from.line > cm.firstLine())
|
| 9477 |
+
{ from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length) }
|
| 9478 |
+
if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine())
|
| 9479 |
+
{ to = Pos(to.line + 1, 0) }
|
| 9480 |
+
if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false }
|
| 9481 |
+
|
| 9482 |
+
var fromIndex, fromLine, fromNode
|
| 9483 |
+
if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) {
|
| 9484 |
+
fromLine = lineNo(display.view[0].line)
|
| 9485 |
+
fromNode = display.view[0].node
|
| 9486 |
+
} else {
|
| 9487 |
+
fromLine = lineNo(display.view[fromIndex].line)
|
| 9488 |
+
fromNode = display.view[fromIndex - 1].node.nextSibling
|
| 9489 |
+
}
|
| 9490 |
+
var toIndex = findViewIndex(cm, to.line)
|
| 9491 |
+
var toLine, toNode
|
| 9492 |
+
if (toIndex == display.view.length - 1) {
|
| 9493 |
+
toLine = display.viewTo - 1
|
| 9494 |
+
toNode = display.lineDiv.lastChild
|
| 9495 |
+
} else {
|
| 9496 |
+
toLine = lineNo(display.view[toIndex + 1].line) - 1
|
| 9497 |
+
toNode = display.view[toIndex + 1].node.previousSibling
|
| 9498 |
+
}
|
| 9499 |
|
| 9500 |
+
if (!fromNode) { return false }
|
| 9501 |
+
var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine))
|
| 9502 |
+
var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length))
|
| 9503 |
+
while (newText.length > 1 && oldText.length > 1) {
|
| 9504 |
+
if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine-- }
|
| 9505 |
+
else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++ }
|
| 9506 |
+
else { break }
|
| 9507 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9508 |
|
| 9509 |
+
var cutFront = 0, cutEnd = 0
|
| 9510 |
+
var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length)
|
| 9511 |
+
while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront))
|
| 9512 |
+
{ ++cutFront }
|
| 9513 |
+
var newBot = lst(newText), oldBot = lst(oldText)
|
| 9514 |
+
var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0),
|
| 9515 |
+
oldBot.length - (oldText.length == 1 ? cutFront : 0))
|
| 9516 |
+
while (cutEnd < maxCutEnd &&
|
| 9517 |
+
newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1))
|
| 9518 |
+
{ ++cutEnd }
|
| 9519 |
+
|
| 9520 |
+
newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, "")
|
| 9521 |
+
newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, "")
|
| 9522 |
+
|
| 9523 |
+
var chFrom = Pos(fromLine, cutFront)
|
| 9524 |
+
var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0)
|
| 9525 |
+
if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) {
|
| 9526 |
+
replaceRange(cm.doc, newText, chFrom, chTo, "+input")
|
| 9527 |
+
return true
|
| 9528 |
+
}
|
| 9529 |
+
};
|
| 9530 |
+
|
| 9531 |
+
ContentEditableInput.prototype.ensurePolled = function () {
|
| 9532 |
+
this.forceCompositionEnd()
|
| 9533 |
+
};
|
| 9534 |
+
ContentEditableInput.prototype.reset = function () {
|
| 9535 |
+
this.forceCompositionEnd()
|
| 9536 |
+
};
|
| 9537 |
+
ContentEditableInput.prototype.forceCompositionEnd = function () {
|
| 9538 |
+
if (!this.composing) { return }
|
| 9539 |
+
clearTimeout(this.readDOMTimeout)
|
| 9540 |
+
this.composing = null
|
| 9541 |
+
if (!this.pollContent()) { regChange(this.cm) }
|
| 9542 |
+
this.div.blur()
|
| 9543 |
+
this.div.focus()
|
| 9544 |
+
};
|
| 9545 |
+
ContentEditableInput.prototype.readFromDOMSoon = function () {
|
| 9546 |
var this$1 = this;
|
| 9547 |
|
| 9548 |
+
if (this.readDOMTimeout != null) { return }
|
| 9549 |
+
this.readDOMTimeout = setTimeout(function () {
|
| 9550 |
+
this$1.readDOMTimeout = null
|
| 9551 |
+
if (this$1.composing) {
|
| 9552 |
+
if (this$1.composing.done) { this$1.composing = null }
|
| 9553 |
+
else { return }
|
| 9554 |
+
}
|
| 9555 |
+
if (this$1.cm.isReadOnly() || !this$1.pollContent())
|
| 9556 |
+
{ runInOp(this$1.cm, function () { return regChange(this$1.cm); }) }
|
| 9557 |
+
}, 80)
|
| 9558 |
+
};
|
| 9559 |
|
| 9560 |
+
ContentEditableInput.prototype.setUneditable = function (node) {
|
| 9561 |
+
node.contentEditable = "false"
|
| 9562 |
+
};
|
| 9563 |
|
| 9564 |
+
ContentEditableInput.prototype.onKeyPress = function (e) {
|
| 9565 |
+
e.preventDefault()
|
| 9566 |
+
if (!this.cm.isReadOnly())
|
| 9567 |
+
{ operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0) }
|
| 9568 |
+
};
|
| 9569 |
|
| 9570 |
+
ContentEditableInput.prototype.readOnlyChanged = function (val) {
|
| 9571 |
+
this.div.contentEditable = String(val != "nocursor")
|
| 9572 |
+
};
|
| 9573 |
|
| 9574 |
+
ContentEditableInput.prototype.onContextMenu = function () {};
|
| 9575 |
+
ContentEditableInput.prototype.resetPosition = function () {};
|
| 9576 |
|
| 9577 |
+
ContentEditableInput.prototype.needsContentAttribute = true
|
|
|
|
| 9578 |
|
| 9579 |
function posToDOM(cm, pos) {
|
| 9580 |
var view = findViewForLine(cm, pos.line)
|
| 9711 |
|
| 9712 |
// TEXTAREA INPUT STYLE
|
| 9713 |
|
| 9714 |
+
var TextareaInput = function(cm) {
|
| 9715 |
this.cm = cm
|
| 9716 |
// See input.poll and input.reset
|
| 9717 |
this.prevInput = ""
|
| 9728 |
// Used to work around IE issue with selection being forgotten when focus moves away from textarea
|
| 9729 |
this.hasSelection = false
|
| 9730 |
this.composing = null
|
| 9731 |
+
};
|
| 9732 |
|
| 9733 |
+
TextareaInput.prototype.init = function (display) {
|
|
|
|
| 9734 |
var this$1 = this;
|
| 9735 |
|
| 9736 |
+
var input = this, cm = this.cm
|
| 9737 |
|
| 9738 |
+
// Wraps and hides input textarea
|
| 9739 |
+
var div = this.wrapper = hiddenTextarea()
|
| 9740 |
+
// The semihidden textarea that is focused when the editor is
|
| 9741 |
+
// focused, and receives input.
|
| 9742 |
+
var te = this.textarea = div.firstChild
|
| 9743 |
+
display.wrapper.insertBefore(div, display.wrapper.firstChild)
|
| 9744 |
|
| 9745 |
+
// Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore)
|
| 9746 |
+
if (ios) { te.style.width = "0px" }
|
| 9747 |
|
| 9748 |
+
on(te, "input", function () {
|
| 9749 |
+
if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null }
|
| 9750 |
+
input.poll()
|
| 9751 |
+
})
|
| 9752 |
|
| 9753 |
+
on(te, "paste", function (e) {
|
| 9754 |
+
if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return }
|
| 9755 |
|
| 9756 |
+
cm.state.pasteIncoming = true
|
| 9757 |
+
input.fastPoll()
|
| 9758 |
+
})
|
| 9759 |
|
| 9760 |
+
function prepareCopyCut(e) {
|
| 9761 |
+
if (signalDOMEvent(cm, e)) { return }
|
| 9762 |
+
if (cm.somethingSelected()) {
|
| 9763 |
+
setLastCopied({lineWise: false, text: cm.getSelections()})
|
| 9764 |
+
if (input.inaccurateSelection) {
|
| 9765 |
+
input.prevInput = ""
|
| 9766 |
+
input.inaccurateSelection = false
|
| 9767 |
+
te.value = lastCopied.text.join("\n")
|
| 9768 |
+
selectInput(te)
|
| 9769 |
+
}
|
| 9770 |
+
} else if (!cm.options.lineWiseCopyCut) {
|
| 9771 |
+
return
|
| 9772 |
+
} else {
|
| 9773 |
+
var ranges = copyableRanges(cm)
|
| 9774 |
+
setLastCopied({lineWise: true, text: ranges.text})
|
| 9775 |
+
if (e.type == "cut") {
|
| 9776 |
+
cm.setSelections(ranges.ranges, null, sel_dontScroll)
|
| 9777 |
} else {
|
| 9778 |
+
input.prevInput = ""
|
| 9779 |
+
te.value = ranges.text.join("\n")
|
| 9780 |
+
selectInput(te)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9781 |
}
|
|
|
|
| 9782 |
}
|
| 9783 |
+
if (e.type == "cut") { cm.state.cutIncoming = true }
|
| 9784 |
+
}
|
| 9785 |
+
on(te, "cut", prepareCopyCut)
|
| 9786 |
+
on(te, "copy", prepareCopyCut)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9787 |
|
| 9788 |
+
on(display.scroller, "paste", function (e) {
|
| 9789 |
+
if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return }
|
| 9790 |
+
cm.state.pasteIncoming = true
|
| 9791 |
+
input.focus()
|
| 9792 |
+
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9793 |
|
| 9794 |
+
// Prevent normal selection in the editor (we handle our own)
|
| 9795 |
+
on(display.lineSpace, "selectstart", function (e) {
|
| 9796 |
+
if (!eventInWidget(display, e)) { e_preventDefault(e) }
|
| 9797 |
+
})
|
| 9798 |
|
| 9799 |
+
on(te, "compositionstart", function () {
|
| 9800 |
+
var start = cm.getCursor("from")
|
| 9801 |
+
if (input.composing) { input.composing.range.clear() }
|
| 9802 |
+
input.composing = {
|
| 9803 |
+
start: start,
|
| 9804 |
+
range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"})
|
|
|
|
|
|
|
| 9805 |
}
|
| 9806 |
+
})
|
| 9807 |
+
on(te, "compositionend", function () {
|
| 9808 |
+
if (input.composing) {
|
| 9809 |
+
input.poll()
|
| 9810 |
+
input.composing.range.clear()
|
| 9811 |
+
input.composing = null
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9812 |
}
|
| 9813 |
+
})
|
| 9814 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9815 |
|
| 9816 |
+
TextareaInput.prototype.prepareSelection = function () {
|
| 9817 |
+
// Redraw the selection and/or cursor
|
| 9818 |
+
var cm = this.cm, display = cm.display, doc = cm.doc
|
| 9819 |
+
var result = prepareSelection(cm)
|
| 9820 |
|
| 9821 |
+
// Move the hidden textarea near the cursor to prevent scrolling artifacts
|
| 9822 |
+
if (cm.options.moveInputWithCursor) {
|
| 9823 |
+
var headPos = cursorCoords(cm, doc.sel.primary().head, "div")
|
| 9824 |
+
var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect()
|
| 9825 |
+
result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10,
|
| 9826 |
+
headPos.top + lineOff.top - wrapOff.top))
|
| 9827 |
+
result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10,
|
| 9828 |
+
headPos.left + lineOff.left - wrapOff.left))
|
| 9829 |
+
}
|
| 9830 |
|
| 9831 |
+
return result
|
| 9832 |
+
};
|
| 9833 |
+
|
| 9834 |
+
TextareaInput.prototype.showSelection = function (drawn) {
|
| 9835 |
+
var cm = this.cm, display = cm.display
|
| 9836 |
+
removeChildrenAndAdd(display.cursorDiv, drawn.cursors)
|
| 9837 |
+
removeChildrenAndAdd(display.selectionDiv, drawn.selection)
|
| 9838 |
+
if (drawn.teTop != null) {
|
| 9839 |
+
this.wrapper.style.top = drawn.teTop + "px"
|
| 9840 |
+
this.wrapper.style.left = drawn.teLeft + "px"
|
| 9841 |
+
}
|
| 9842 |
+
};
|
| 9843 |
+
|
| 9844 |
+
// Reset the input to correspond to the selection (or to be empty,
|
| 9845 |
+
// when not typing and nothing is selected)
|
| 9846 |
+
TextareaInput.prototype.reset = function (typing) {
|
| 9847 |
+
if (this.contextMenuPending) { return }
|
| 9848 |
+
var minimal, selected, cm = this.cm, doc = cm.doc
|
| 9849 |
+
if (cm.somethingSelected()) {
|
| 9850 |
+
this.prevInput = ""
|
| 9851 |
+
var range = doc.sel.primary()
|
| 9852 |
+
minimal = hasCopyEvent &&
|
| 9853 |
+
(range.to().line - range.from().line > 100 || (selected = cm.getSelection()).length > 1000)
|
| 9854 |
+
var content = minimal ? "-" : selected || cm.getSelection()
|
| 9855 |
+
this.textarea.value = content
|
| 9856 |
+
if (cm.state.focused) { selectInput(this.textarea) }
|
| 9857 |
+
if (ie && ie_version >= 9) { this.hasSelection = content }
|
| 9858 |
+
} else if (!typing) {
|
| 9859 |
+
this.prevInput = this.textarea.value = ""
|
| 9860 |
+
if (ie && ie_version >= 9) { this.hasSelection = null }
|
| 9861 |
+
}
|
| 9862 |
+
this.inaccurateSelection = minimal
|
| 9863 |
+
};
|
| 9864 |
|
| 9865 |
+
TextareaInput.prototype.getField = function () { return this.textarea };
|
| 9866 |
|
| 9867 |
+
TextareaInput.prototype.supportsTouch = function () { return false };
|
|
|
|
|
|
|
| 9868 |
|
| 9869 |
+
TextareaInput.prototype.focus = function () {
|
| 9870 |
+
if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) {
|
| 9871 |
+
try { this.textarea.focus() }
|
| 9872 |
+
catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM
|
| 9873 |
+
}
|
| 9874 |
+
};
|
| 9875 |
|
| 9876 |
+
TextareaInput.prototype.blur = function () { this.textarea.blur() };
|
|
|
|
|
|
|
|
|
|
| 9877 |
|
| 9878 |
+
TextareaInput.prototype.resetPosition = function () {
|
| 9879 |
+
this.wrapper.style.top = this.wrapper.style.left = 0
|
| 9880 |
+
};
|
|
|
|
|
|
|
|
|
|
| 9881 |
|
| 9882 |
+
TextareaInput.prototype.receivedFocus = function () { this.slowPoll() };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9883 |
|
| 9884 |
+
// Poll for input changes, using the normal rate of polling. This
|
| 9885 |
+
// runs as long as the editor is focused.
|
| 9886 |
+
TextareaInput.prototype.slowPoll = function () {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9887 |
var this$1 = this;
|
| 9888 |
|
| 9889 |
+
if (this.pollingFast) { return }
|
| 9890 |
+
this.polling.set(this.cm.options.pollInterval, function () {
|
| 9891 |
+
this$1.poll()
|
| 9892 |
+
if (this$1.cm.state.focused) { this$1.slowPoll() }
|
| 9893 |
+
})
|
| 9894 |
+
};
|
| 9895 |
+
|
| 9896 |
+
// When an event has just come in that is likely to add or change
|
| 9897 |
+
// something in the input textarea, we poll faster, to ensure that
|
| 9898 |
+
// the change appears on the screen quickly.
|
| 9899 |
+
TextareaInput.prototype.fastPoll = function () {
|
| 9900 |
+
var missed = false, input = this
|
| 9901 |
+
input.pollingFast = true
|
| 9902 |
+
function p() {
|
| 9903 |
+
var changed = input.poll()
|
| 9904 |
+
if (!changed && !missed) {missed = true; input.polling.set(60, p)}
|
| 9905 |
+
else {input.pollingFast = false; input.slowPoll()}
|
| 9906 |
+
}
|
| 9907 |
+
input.polling.set(20, p)
|
| 9908 |
+
};
|
| 9909 |
+
|
| 9910 |
+
// Read input from the textarea, and update the document to match.
|
| 9911 |
+
// When something is selected, it is present in the textarea, and
|
| 9912 |
+
// selected (unless it is huge, in which case a placeholder is
|
| 9913 |
+
// used). When nothing is selected, the cursor sits after previously
|
| 9914 |
+
// seen text (can be empty), which is stored in prevInput (we must
|
| 9915 |
+
// not reset the textarea when typing, because that breaks IME).
|
| 9916 |
+
TextareaInput.prototype.poll = function () {
|
| 9917 |
+
var this$1 = this;
|
|
|
|
| 9918 |
|
| 9919 |
+
var cm = this.cm, input = this.textarea, prevInput = this.prevInput
|
| 9920 |
+
// Since this is called a *lot*, try to bail out as cheaply as
|
| 9921 |
+
// possible when it is clear that nothing happened. hasSelection
|
| 9922 |
+
// will be the case when there is a lot of text in the textarea,
|
| 9923 |
+
// in which case reading its value would be expensive.
|
| 9924 |
+
if (this.contextMenuPending || !cm.state.focused ||
|
| 9925 |
+
(hasSelection(input) && !prevInput && !this.composing) ||
|
| 9926 |
+
cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq)
|
| 9927 |
+
{ return false }
|
| 9928 |
|
| 9929 |
+
var text = input.value
|
| 9930 |
+
// If nothing changed, bail.
|
| 9931 |
+
if (text == prevInput && !cm.somethingSelected()) { return false }
|
| 9932 |
+
// Work around nonsensical selection resetting in IE9/10, and
|
| 9933 |
+
// inexplicable appearance of private area unicode characters on
|
| 9934 |
+
// some key combos in Mac (#2689).
|
| 9935 |
+
if (ie && ie_version >= 9 && this.hasSelection === text ||
|
| 9936 |
+
mac && /[\uf700-\uf7ff]/.test(text)) {
|
| 9937 |
+
cm.display.input.reset()
|
| 9938 |
+
return false
|
| 9939 |
+
}
|
| 9940 |
|
| 9941 |
+
if (cm.doc.sel == cm.display.selForContextMenu) {
|
| 9942 |
+
var first = text.charCodeAt(0)
|
| 9943 |
+
if (first == 0x200b && !prevInput) { prevInput = "\u200b" }
|
| 9944 |
+
if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") }
|
| 9945 |
+
}
|
| 9946 |
+
// Find the part of the input that is actually new
|
| 9947 |
+
var same = 0, l = Math.min(prevInput.length, text.length)
|
| 9948 |
+
while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same }
|
| 9949 |
|
| 9950 |
+
runInOp(cm, function () {
|
| 9951 |
+
applyTextInput(cm, text.slice(same), prevInput.length - same,
|
| 9952 |
+
null, this$1.composing ? "*compose" : null)
|
| 9953 |
|
| 9954 |
+
// Don't leave long text in the textarea, since it makes further polling slow
|
| 9955 |
+
if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = "" }
|
| 9956 |
+
else { this$1.prevInput = text }
|
|
|
|
| 9957 |
|
| 9958 |
+
if (this$1.composing) {
|
| 9959 |
+
this$1.composing.range.clear()
|
| 9960 |
+
this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"),
|
| 9961 |
+
{className: "CodeMirror-composing"})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9962 |
}
|
| 9963 |
+
})
|
| 9964 |
+
return true
|
| 9965 |
+
};
|
| 9966 |
+
|
| 9967 |
+
TextareaInput.prototype.ensurePolled = function () {
|
| 9968 |
+
if (this.pollingFast && this.poll()) { this.pollingFast = false }
|
| 9969 |
+
};
|
| 9970 |
+
|
| 9971 |
+
TextareaInput.prototype.onKeyPress = function () {
|
| 9972 |
+
if (ie && ie_version >= 9) { this.hasSelection = null }
|
| 9973 |
+
this.fastPoll()
|
| 9974 |
+
};
|
| 9975 |
+
|
| 9976 |
+
TextareaInput.prototype.onContextMenu = function (e) {
|
| 9977 |
+
var input = this, cm = input.cm, display = cm.display, te = input.textarea
|
| 9978 |
+
var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop
|
| 9979 |
+
if (!pos || presto) { return } // Opera is difficult.
|
| 9980 |
+
|
| 9981 |
+
// Reset the current text selection only if the click is done outside of the selection
|
| 9982 |
+
// and 'resetSelectionOnContextMenu' option is true.
|
| 9983 |
+
var reset = cm.options.resetSelectionOnContextMenu
|
| 9984 |
+
if (reset && cm.doc.sel.contains(pos) == -1)
|
| 9985 |
+
{ operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll) }
|
| 9986 |
+
|
| 9987 |
+
var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText
|
| 9988 |
+
input.wrapper.style.cssText = "position: absolute"
|
| 9989 |
+
var wrapperBox = input.wrapper.getBoundingClientRect()
|
| 9990 |
+
te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"
|
| 9991 |
+
var oldScrollY
|
| 9992 |
+
if (webkit) { oldScrollY = window.scrollY } // Work around Chrome issue (#2712)
|
| 9993 |
+
display.input.focus()
|
| 9994 |
+
if (webkit) { window.scrollTo(null, oldScrollY) }
|
| 9995 |
+
display.input.reset()
|
| 9996 |
+
// Adds "Select all" to context menu in FF
|
| 9997 |
+
if (!cm.somethingSelected()) { te.value = input.prevInput = " " }
|
| 9998 |
+
input.contextMenuPending = true
|
| 9999 |
+
display.selForContextMenu = cm.doc.sel
|
| 10000 |
+
clearTimeout(display.detectingSelectAll)
|
| 10001 |
+
|
| 10002 |
+
// Select-all will be greyed out if there's nothing to select, so
|
| 10003 |
+
// this adds a zero-width space so that we can later check whether
|
| 10004 |
+
// it got selected.
|
| 10005 |
+
function prepareSelectAllHack() {
|
| 10006 |
+
if (te.selectionStart != null) {
|
| 10007 |
+
var selected = cm.somethingSelected()
|
| 10008 |
+
var extval = "\u200b" + (selected ? te.value : "")
|
| 10009 |
+
te.value = "\u21da" // Used to catch context-menu undo
|
| 10010 |
+
te.value = extval
|
| 10011 |
+
input.prevInput = selected ? "" : "\u200b"
|
| 10012 |
+
te.selectionStart = 1; te.selectionEnd = extval.length
|
| 10013 |
+
// Re-set this, in case some other handler touched the
|
| 10014 |
+
// selection in the meantime.
|
| 10015 |
+
display.selForContextMenu = cm.doc.sel
|
| 10016 |
+
}
|
| 10017 |
+
}
|
| 10018 |
+
function rehide() {
|
| 10019 |
+
input.contextMenuPending = false
|
| 10020 |
+
input.wrapper.style.cssText = oldWrapperCSS
|
| 10021 |
+
te.style.cssText = oldCSS
|
| 10022 |
+
if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos) }
|
| 10023 |
+
|
| 10024 |
+
// Try to detect the user choosing select-all
|
| 10025 |
+
if (te.selectionStart != null) {
|
| 10026 |
+
if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack() }
|
| 10027 |
+
var i = 0, poll = function () {
|
| 10028 |
+
if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 &&
|
| 10029 |
+
te.selectionEnd > 0 && input.prevInput == "\u200b")
|
| 10030 |
+
{ operation(cm, selectAll)(cm) }
|
| 10031 |
+
else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500) }
|
| 10032 |
+
else { display.input.reset() }
|
| 10033 |
}
|
| 10034 |
+
display.detectingSelectAll = setTimeout(poll, 200)
|
| 10035 |
}
|
| 10036 |
+
}
|
| 10037 |
|
| 10038 |
+
if (ie && ie_version >= 9) { prepareSelectAllHack() }
|
| 10039 |
+
if (captureRightClick) {
|
| 10040 |
+
e_stop(e)
|
| 10041 |
+
var mouseup = function () {
|
| 10042 |
+
off(window, "mouseup", mouseup)
|
| 10043 |
+
setTimeout(rehide, 20)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10044 |
}
|
| 10045 |
+
on(window, "mouseup", mouseup)
|
| 10046 |
+
} else {
|
| 10047 |
+
setTimeout(rehide, 50)
|
| 10048 |
+
}
|
| 10049 |
+
};
|
| 10050 |
|
| 10051 |
+
TextareaInput.prototype.readOnlyChanged = function (val) {
|
| 10052 |
+
if (!val) { this.reset() }
|
| 10053 |
+
};
|
| 10054 |
|
| 10055 |
+
TextareaInput.prototype.setUneditable = function () {};
|
| 10056 |
|
| 10057 |
+
TextareaInput.prototype.needsContentAttribute = false
|
|
|
|
| 10058 |
|
| 10059 |
function fromTextArea(textarea, options) {
|
| 10060 |
options = options ? copyObj(options) : {}
|
| 10205 |
|
| 10206 |
addLegacyProps(CodeMirror)
|
| 10207 |
|
| 10208 |
+
CodeMirror.version = "5.22.0"
|
| 10209 |
|
| 10210 |
return CodeMirror;
|
| 10211 |
|
| 10742 |
"transition-property", "transition-timing-function", "unicode-bidi",
|
| 10743 |
"user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
|
| 10744 |
"voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
|
| 10745 |
+
"voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
|
| 10746 |
"word-spacing", "word-wrap", "z-index",
|
| 10747 |
// SVG-specific
|
| 10748 |
"clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
|
| 10816 |
"cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
|
| 10817 |
"cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
|
| 10818 |
"col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
|
| 10819 |
+
"compact", "condensed", "contain", "content", "contents",
|
| 10820 |
"content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
|
| 10821 |
"cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
|
| 10822 |
"decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
|
| 10859 |
"mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
|
| 10860 |
"narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
|
| 10861 |
"no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
|
| 10862 |
+
"ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
|
| 10863 |
"optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
|
| 10864 |
"outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
|
| 10865 |
"painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
|
| 10871 |
"rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
|
| 10872 |
"rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
|
| 10873 |
"s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
|
| 10874 |
+
"scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
|
| 10875 |
"searchfield-cancel-button", "searchfield-decoration",
|
| 10876 |
"searchfield-results-button", "searchfield-results-decoration",
|
| 10877 |
"semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
|
| 10889 |
"thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
|
| 10890 |
"threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
|
| 10891 |
"tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
|
| 10892 |
+
"trad-chinese-formal", "trad-chinese-informal", "transform",
|
| 10893 |
"translate", "translate3d", "translateX", "translateY", "translateZ",
|
| 10894 |
+
"transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
|
| 10895 |
"upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
|
| 10896 |
"upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
|
| 10897 |
"var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
|
assets/js/mailpoet.js
CHANGED
|
@@ -17048,9 +17048,7 @@ webpackJsonp([2],[
|
|
| 17048 |
var convertedFormat = [];
|
| 17049 |
var escapeToken = false;
|
| 17050 |
|
| 17051 |
-
for
|
| 17052 |
-
var token = format[index];
|
| 17053 |
-
|
| 17054 |
if (escapeToken === true) {
|
| 17055 |
convertedFormat.push('['+token+']');
|
| 17056 |
escapeToken = false;
|
| 17048 |
var convertedFormat = [];
|
| 17049 |
var escapeToken = false;
|
| 17050 |
|
| 17051 |
+
for(var index = 0, token = ''; token = format.charAt(index); index++){
|
|
|
|
|
|
|
| 17052 |
if (escapeToken === true) {
|
| 17053 |
convertedFormat.push('['+token+']');
|
| 17054 |
escapeToken = false;
|
assets/js/newsletter_editor.js
CHANGED
|
@@ -14624,7 +14624,7 @@ webpackJsonp([3],{
|
|
| 14624 |
/***/ function(module, exports, __webpack_require__) {
|
| 14625 |
|
| 14626 |
/**
|
| 14627 |
-
* interact.js v1.2.
|
| 14628 |
*
|
| 14629 |
* Copyright (c) 2012-2015 Taye Adeyemi <dev@taye.me>
|
| 14630 |
* Open source under the MIT License.
|
|
@@ -14869,7 +14869,8 @@ webpackJsonp([3],{
|
|
| 14869 |
supportsTouch = (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch),
|
| 14870 |
|
| 14871 |
// Does the browser support PointerEvents
|
| 14872 |
-
|
|
|
|
| 14873 |
|
| 14874 |
// Less Precision with touch input
|
| 14875 |
margin = supportsTouch || supportsPointerEvent? 20: 10,
|
|
@@ -16002,14 +16003,14 @@ webpackJsonp([3],{
|
|
| 16002 |
|
| 16003 |
this.pointerHover(pointer, event, this.matches, this.matchElements);
|
| 16004 |
events.add(eventTarget,
|
| 16005 |
-
|
| 16006 |
listeners.pointerHover);
|
| 16007 |
}
|
| 16008 |
else if (this.target) {
|
| 16009 |
if (nodeContains(prevTargetElement, eventTarget)) {
|
| 16010 |
this.pointerHover(pointer, event, this.matches, this.matchElements);
|
| 16011 |
events.add(this.element,
|
| 16012 |
-
|
| 16013 |
listeners.pointerHover);
|
| 16014 |
}
|
| 16015 |
else {
|
|
@@ -16061,7 +16062,7 @@ webpackJsonp([3],{
|
|
| 16061 |
// Remove temporary event listeners for selector Interactables
|
| 16062 |
if (!interactables.get(eventTarget)) {
|
| 16063 |
events.remove(eventTarget,
|
| 16064 |
-
|
| 16065 |
listeners.pointerHover);
|
| 16066 |
}
|
| 16067 |
|
|
@@ -16373,7 +16374,7 @@ webpackJsonp([3],{
|
|
| 16373 |
|
| 16374 |
// set the startCoords if there was no prepared action
|
| 16375 |
if (!this.prepared.name) {
|
| 16376 |
-
this.setEventXY(this.startCoords);
|
| 16377 |
}
|
| 16378 |
|
| 16379 |
this.prepared.name = action.name;
|
|
@@ -18555,7 +18556,7 @@ webpackJsonp([3],{
|
|
| 18555 |
|
| 18556 |
if (isElement(element, _window)) {
|
| 18557 |
|
| 18558 |
-
if (
|
| 18559 |
events.add(this._element, pEventTypes.down, listeners.pointerDown );
|
| 18560 |
events.add(this._element, pEventTypes.move, listeners.pointerHover);
|
| 18561 |
}
|
|
@@ -20400,7 +20401,7 @@ webpackJsonp([3],{
|
|
| 20400 |
events.add(doc, eventType, delegateUseCapture, true);
|
| 20401 |
}
|
| 20402 |
|
| 20403 |
-
if (
|
| 20404 |
if (PointerEvent === win.MSPointerEvent) {
|
| 20405 |
pEventTypes = {
|
| 20406 |
up: 'MSPointerUp', down: 'MSPointerDown', over: 'mouseover',
|
|
@@ -28413,19 +28414,25 @@ webpackJsonp([3],{
|
|
| 28413 |
// send test email
|
| 28414 |
MailPoet.Modal.loading(true);
|
| 28415 |
|
| 28416 |
-
|
| 28417 |
-
|
| 28418 |
-
|
| 28419 |
-
|
| 28420 |
-
|
| 28421 |
-
|
| 28422 |
-
|
| 28423 |
-
|
| 28424 |
-
MailPoet.Notice.
|
| 28425 |
-
|
| 28426 |
-
{ scroll: true
|
| 28427 |
-
|
| 28428 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28429 |
});
|
| 28430 |
},
|
| 28431 |
});
|
|
@@ -28767,7 +28774,7 @@ webpackJsonp([3],{
|
|
| 28767 |
App.getChannel().trigger('beforeEditorSave', json);
|
| 28768 |
|
| 28769 |
// save newsletter
|
| 28770 |
-
CommunicationComponent.saveNewsletter(json).done(function(response) {
|
| 28771 |
if(response.success !== undefined && response.success === true) {
|
| 28772 |
// TODO: Handle translations
|
| 28773 |
//MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
|
|
@@ -28793,6 +28800,14 @@ webpackJsonp([3],{
|
|
| 28793 |
});
|
| 28794 |
};
|
| 28795 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28796 |
Module.getThumbnail = function(element, options) {
|
| 28797 |
var promise = html2canvas(element, options || {});
|
| 28798 |
|
|
@@ -29062,12 +29077,12 @@ webpackJsonp([3],{
|
|
| 29062 |
};
|
| 29063 |
|
| 29064 |
App.on('before:start', function(options) {
|
| 29065 |
-
App.save = Module.
|
| 29066 |
App.getChannel().on('autoSave', Module.autoSave);
|
| 29067 |
|
| 29068 |
window.onbeforeunload = Module.beforeExitWithUnsavedChanges;
|
| 29069 |
|
| 29070 |
-
App.getChannel().on('save', function() { App.save(); });
|
| 29071 |
});
|
| 29072 |
|
| 29073 |
App.on('start', function(options) {
|
| 14624 |
/***/ function(module, exports, __webpack_require__) {
|
| 14625 |
|
| 14626 |
/**
|
| 14627 |
+
* interact.js v1.2.8
|
| 14628 |
*
|
| 14629 |
* Copyright (c) 2012-2015 Taye Adeyemi <dev@taye.me>
|
| 14630 |
* Open source under the MIT License.
|
| 14869 |
supportsTouch = (('ontouchstart' in window) || window.DocumentTouch && document instanceof window.DocumentTouch),
|
| 14870 |
|
| 14871 |
// Does the browser support PointerEvents
|
| 14872 |
+
// Avoid PointerEvent bugs introduced in Chrome 55
|
| 14873 |
+
supportsPointerEvent = PointerEvent && !/Chrome/.test(navigator.userAgent),
|
| 14874 |
|
| 14875 |
// Less Precision with touch input
|
| 14876 |
margin = supportsTouch || supportsPointerEvent? 20: 10,
|
| 16003 |
|
| 16004 |
this.pointerHover(pointer, event, this.matches, this.matchElements);
|
| 16005 |
events.add(eventTarget,
|
| 16006 |
+
supportsPointerEvent? pEventTypes.move : 'mousemove',
|
| 16007 |
listeners.pointerHover);
|
| 16008 |
}
|
| 16009 |
else if (this.target) {
|
| 16010 |
if (nodeContains(prevTargetElement, eventTarget)) {
|
| 16011 |
this.pointerHover(pointer, event, this.matches, this.matchElements);
|
| 16012 |
events.add(this.element,
|
| 16013 |
+
supportsPointerEvent? pEventTypes.move : 'mousemove',
|
| 16014 |
listeners.pointerHover);
|
| 16015 |
}
|
| 16016 |
else {
|
| 16062 |
// Remove temporary event listeners for selector Interactables
|
| 16063 |
if (!interactables.get(eventTarget)) {
|
| 16064 |
events.remove(eventTarget,
|
| 16065 |
+
supportsPointerEvent? pEventTypes.move : 'mousemove',
|
| 16066 |
listeners.pointerHover);
|
| 16067 |
}
|
| 16068 |
|
| 16374 |
|
| 16375 |
// set the startCoords if there was no prepared action
|
| 16376 |
if (!this.prepared.name) {
|
| 16377 |
+
this.setEventXY(this.startCoords, this.pointers);
|
| 16378 |
}
|
| 16379 |
|
| 16380 |
this.prepared.name = action.name;
|
| 18556 |
|
| 18557 |
if (isElement(element, _window)) {
|
| 18558 |
|
| 18559 |
+
if (supportsPointerEvent) {
|
| 18560 |
events.add(this._element, pEventTypes.down, listeners.pointerDown );
|
| 18561 |
events.add(this._element, pEventTypes.move, listeners.pointerHover);
|
| 18562 |
}
|
| 20401 |
events.add(doc, eventType, delegateUseCapture, true);
|
| 20402 |
}
|
| 20403 |
|
| 20404 |
+
if (supportsPointerEvent) {
|
| 20405 |
if (PointerEvent === win.MSPointerEvent) {
|
| 20406 |
pEventTypes = {
|
| 20407 |
up: 'MSPointerUp', down: 'MSPointerDown', over: 'mouseover',
|
| 28414 |
// send test email
|
| 28415 |
MailPoet.Modal.loading(true);
|
| 28416 |
|
| 28417 |
+
// save before sending
|
| 28418 |
+
var saveResult = {promise: null};
|
| 28419 |
+
App.getChannel().trigger('save', saveResult);
|
| 28420 |
+
|
| 28421 |
+
saveResult.promise.always(function() {
|
| 28422 |
+
CommunicationComponent.previewNewsletter(data).always(function() {
|
| 28423 |
+
MailPoet.Modal.loading(false);
|
| 28424 |
+
}).done(function(response) {
|
| 28425 |
+
MailPoet.Notice.success(
|
| 28426 |
+
MailPoet.I18n.t('newsletterPreviewSent'),
|
| 28427 |
+
{ scroll: true });
|
| 28428 |
+
}).fail(function(response) {
|
| 28429 |
+
if (response.errors.length > 0) {
|
| 28430 |
+
MailPoet.Notice.error(
|
| 28431 |
+
response.errors.map(function(error) { return error.message; }),
|
| 28432 |
+
{ scroll: true, static: true }
|
| 28433 |
+
);
|
| 28434 |
+
}
|
| 28435 |
+
});
|
| 28436 |
});
|
| 28437 |
},
|
| 28438 |
});
|
| 28774 |
App.getChannel().trigger('beforeEditorSave', json);
|
| 28775 |
|
| 28776 |
// save newsletter
|
| 28777 |
+
return CommunicationComponent.saveNewsletter(json).done(function(response) {
|
| 28778 |
if(response.success !== undefined && response.success === true) {
|
| 28779 |
// TODO: Handle translations
|
| 28780 |
//MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
|
| 28800 |
});
|
| 28801 |
};
|
| 28802 |
|
| 28803 |
+
// For getting a promise after triggering save event
|
| 28804 |
+
Module.saveAndProvidePromise = function(saveResult) {
|
| 28805 |
+
var promise = Module.save();
|
| 28806 |
+
if (saveResult !== undefined) {
|
| 28807 |
+
saveResult.promise = promise;
|
| 28808 |
+
}
|
| 28809 |
+
};
|
| 28810 |
+
|
| 28811 |
Module.getThumbnail = function(element, options) {
|
| 28812 |
var promise = html2canvas(element, options || {});
|
| 28813 |
|
| 29077 |
};
|
| 29078 |
|
| 29079 |
App.on('before:start', function(options) {
|
| 29080 |
+
App.save = Module.saveAndProvidePromise;
|
| 29081 |
App.getChannel().on('autoSave', Module.autoSave);
|
| 29082 |
|
| 29083 |
window.onbeforeunload = Module.beforeExitWithUnsavedChanges;
|
| 29084 |
|
| 29085 |
+
App.getChannel().on('save', function(saveResult) { App.save(saveResult); });
|
| 29086 |
});
|
| 29087 |
|
| 29088 |
App.on('start', function(options) {
|
lang/index.php
CHANGED
|
@@ -1,3 +0,0 @@
|
|
| 1 |
-
<?php
|
| 2 |
-
|
| 3 |
-
// Silence is golden
|
|
|
|
|
|
|
|
|
lang/mailpoet.pot
CHANGED
|
@@ -4,7 +4,7 @@ msgid ""
|
|
| 4 |
msgstr ""
|
| 5 |
"Project-Id-Version: \n"
|
| 6 |
"Report-Msgid-Bugs-To: http://support.mailpoet.com/\n"
|
| 7 |
-
"POT-Creation-Date: 2016-12-
|
| 8 |
"MIME-Version: 1.0\n"
|
| 9 |
"Content-Type: text/plain; charset=utf-8\n"
|
| 10 |
"Content-Transfer-Encoding: 8bit\n"
|
|
@@ -79,7 +79,7 @@ msgstr ""
|
|
| 79 |
msgid "Copy of %s"
|
| 80 |
msgstr ""
|
| 81 |
|
| 82 |
-
#: lib/API/Endpoints/Mailer.php:26 lib/API/Endpoints/Newsletters.php:
|
| 83 |
msgid "The email could not be sent: %s"
|
| 84 |
msgstr ""
|
| 85 |
|
|
@@ -91,7 +91,7 @@ msgstr ""
|
|
| 91 |
#: lib/API/Endpoints/Newsletters.php:28 lib/API/Endpoints/Newsletters.php:117
|
| 92 |
#: lib/API/Endpoints/Newsletters.php:138 lib/API/Endpoints/Newsletters.php:154
|
| 93 |
#: lib/API/Endpoints/Newsletters.php:170 lib/API/Endpoints/Newsletters.php:184
|
| 94 |
-
#: lib/API/Endpoints/Newsletters.php:216 lib/API/Endpoints/Newsletters.php:
|
| 95 |
#: lib/API/Endpoints/SendingQueue.php:32 lib/API/Endpoints/SendingQueue.php:122
|
| 96 |
#: lib/API/Endpoints/SendingQueue.php:148
|
| 97 |
msgid "This newsletter does not exist."
|
|
@@ -105,7 +105,7 @@ msgstr ""
|
|
| 105 |
msgid "Newsletter data is missing."
|
| 106 |
msgstr ""
|
| 107 |
|
| 108 |
-
#: lib/API/Endpoints/Newsletters.php:
|
| 109 |
msgid "Please specify receiver information."
|
| 110 |
msgstr ""
|
| 111 |
|
|
@@ -959,11 +959,11 @@ msgid ""
|
|
| 959 |
"fix this issue."
|
| 960 |
msgstr ""
|
| 961 |
|
| 962 |
-
#: lib/Config/Shortcodes.php:
|
| 963 |
msgid "Oops! There are no newsletters to display."
|
| 964 |
msgstr ""
|
| 965 |
|
| 966 |
-
#: lib/Config/Shortcodes.php:
|
| 967 |
msgid "Preview in a new tab"
|
| 968 |
msgstr ""
|
| 969 |
|
|
@@ -1132,15 +1132,15 @@ msgstr ""
|
|
| 1132 |
msgid "Create a new form"
|
| 1133 |
msgstr ""
|
| 1134 |
|
| 1135 |
-
#: lib/Mailer/Mailer.php:
|
| 1136 |
msgid "Mailing method does not exist"
|
| 1137 |
msgstr ""
|
| 1138 |
|
| 1139 |
-
#: lib/Mailer/Mailer.php:
|
| 1140 |
msgid "Mailer is not configured"
|
| 1141 |
msgstr ""
|
| 1142 |
|
| 1143 |
-
#: lib/Mailer/Mailer.php:
|
| 1144 |
msgid "Sender name and email are not configured"
|
| 1145 |
msgstr ""
|
| 1146 |
|
|
@@ -1156,12 +1156,12 @@ msgstr ""
|
|
| 1156 |
msgid "Sending frequency limit has been reached."
|
| 1157 |
msgstr ""
|
| 1158 |
|
| 1159 |
-
#: lib/Mailer/Methods/AmazonSES.php:
|
| 1160 |
msgid "Unsupported Amazon SES region."
|
| 1161 |
msgstr ""
|
| 1162 |
|
| 1163 |
-
#: lib/Mailer/Methods/AmazonSES.php:
|
| 1164 |
-
#: lib/Mailer/Methods/SMTP.php:
|
| 1165 |
msgid "%s has returned an unknown error."
|
| 1166 |
msgstr ""
|
| 1167 |
|
|
@@ -1173,18 +1173,18 @@ msgstr ""
|
|
| 1173 |
msgid "Please specify a name"
|
| 1174 |
msgstr ""
|
| 1175 |
|
| 1176 |
-
#: lib/Models/CustomField.php:17 lib/Models/Newsletter.php:
|
| 1177 |
#: views/form/templates/settings/field_form.hbs:16
|
| 1178 |
msgid "Please specify a type"
|
| 1179 |
msgstr ""
|
| 1180 |
|
| 1181 |
-
#: lib/Models/Form.php:50 lib/Models/Newsletter.php:
|
| 1182 |
-
#: lib/Models/Segment.php:126 lib/Models/Subscriber.php:
|
| 1183 |
msgid "All"
|
| 1184 |
msgstr ""
|
| 1185 |
|
| 1186 |
-
#: lib/Models/Form.php:55 lib/Models/Newsletter.php:
|
| 1187 |
-
#: lib/Models/Segment.php:131 lib/Models/Subscriber.php:
|
| 1188 |
#: views/newsletters.html:75 views/segments.html:50
|
| 1189 |
#: views/subscribers/subscribers.html:34
|
| 1190 |
msgid "Trash"
|
|
@@ -1194,34 +1194,38 @@ msgstr ""
|
|
| 1194 |
msgid "Another record already exists. Please specify a different \"%1$s\"."
|
| 1195 |
msgstr ""
|
| 1196 |
|
| 1197 |
-
#: lib/Models/Newsletter.php:
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1198 |
#: lib/Subscribers/ImportExport/Export/Export.php:170
|
| 1199 |
msgid "All Lists"
|
| 1200 |
msgstr ""
|
| 1201 |
|
| 1202 |
-
#: lib/Models/Newsletter.php:
|
| 1203 |
#: views/newsletter/templates/blocks/posts/settingsSelection.hbs:12
|
| 1204 |
msgid "Draft"
|
| 1205 |
msgstr ""
|
| 1206 |
|
| 1207 |
-
#: lib/Models/Newsletter.php:
|
| 1208 |
#: views/newsletter/templates/blocks/posts/settingsSelection.hbs:11
|
| 1209 |
msgid "Scheduled"
|
| 1210 |
msgstr ""
|
| 1211 |
|
| 1212 |
-
#: lib/Models/Newsletter.php:
|
| 1213 |
msgid "Sending"
|
| 1214 |
msgstr ""
|
| 1215 |
|
| 1216 |
-
#: lib/Models/Newsletter.php:
|
| 1217 |
msgid "Sent"
|
| 1218 |
msgstr ""
|
| 1219 |
|
| 1220 |
-
#: lib/Models/Newsletter.php:
|
| 1221 |
msgid "Active"
|
| 1222 |
msgstr ""
|
| 1223 |
|
| 1224 |
-
#: lib/Models/Newsletter.php:
|
| 1225 |
msgid "Not active"
|
| 1226 |
msgstr ""
|
| 1227 |
|
|
@@ -1273,30 +1277,30 @@ msgstr ""
|
|
| 1273 |
msgid "Your email address is invalid!"
|
| 1274 |
msgstr ""
|
| 1275 |
|
| 1276 |
-
#: lib/Models/Subscriber.php:
|
| 1277 |
msgid "You need to wait before subscribing again."
|
| 1278 |
msgstr ""
|
| 1279 |
|
| 1280 |
-
#: lib/Models/Subscriber.php:
|
| 1281 |
msgid "Subscribers without a list (%s)"
|
| 1282 |
msgstr ""
|
| 1283 |
|
| 1284 |
-
#: lib/Models/Subscriber.php:
|
| 1285 |
#: views/segments.html:30 views/subscribers/subscribers.html:51
|
| 1286 |
msgid "Subscribed"
|
| 1287 |
msgstr ""
|
| 1288 |
|
| 1289 |
-
#: lib/Models/Subscriber.php:
|
| 1290 |
#: views/subscribers/subscribers.html:50
|
| 1291 |
msgid "Unconfirmed"
|
| 1292 |
msgstr ""
|
| 1293 |
|
| 1294 |
-
#: lib/Models/Subscriber.php:
|
| 1295 |
#: views/segments.html:32 views/subscribers/subscribers.html:52
|
| 1296 |
msgid "Unsubscribed"
|
| 1297 |
msgstr ""
|
| 1298 |
|
| 1299 |
-
#: lib/Models/Subscriber.php:
|
| 1300 |
#: views/segments.html:33 views/subscribers/subscribers.html:53
|
| 1301 |
msgid "Bounced"
|
| 1302 |
msgstr ""
|
|
@@ -1305,17 +1309,17 @@ msgstr ""
|
|
| 1305 |
msgid "Click here to view media."
|
| 1306 |
msgstr ""
|
| 1307 |
|
| 1308 |
-
#: lib/Newsletter/Shortcodes/Categories/Link.php:
|
| 1309 |
#: views/newsletter/editor.html:1037
|
| 1310 |
msgid "Unsubscribe"
|
| 1311 |
msgstr ""
|
| 1312 |
|
| 1313 |
-
#: lib/Newsletter/Shortcodes/Categories/Link.php:
|
| 1314 |
#: views/newsletter/editor.html:1037
|
| 1315 |
msgid "Manage subscription"
|
| 1316 |
msgstr ""
|
| 1317 |
|
| 1318 |
-
#: lib/Newsletter/Shortcodes/Categories/Link.php:
|
| 1319 |
msgid "View in your browser"
|
| 1320 |
msgstr ""
|
| 1321 |
|
|
@@ -1546,7 +1550,7 @@ msgstr ""
|
|
| 1546 |
msgid "You are now subscribed!"
|
| 1547 |
msgstr ""
|
| 1548 |
|
| 1549 |
-
#: lib/Subscription/Pages.php:177 lib/Subscription/Pages.php:
|
| 1550 |
msgid "Manage your subscription"
|
| 1551 |
msgstr ""
|
| 1552 |
|
|
@@ -1558,31 +1562,31 @@ msgstr ""
|
|
| 1558 |
msgid "Yup, we've added you to our email list. You'll hear from us shortly."
|
| 1559 |
msgstr ""
|
| 1560 |
|
| 1561 |
-
#: lib/Subscription/Pages.php:
|
| 1562 |
msgid "Your lists"
|
| 1563 |
msgstr ""
|
| 1564 |
|
| 1565 |
-
#: lib/Subscription/Pages.php:
|
| 1566 |
#: views/form/editor.html:383 views/newsletter/templates/components/save.hbs:3
|
| 1567 |
#: views/segments.html:55 views/subscribers/subscribers.html:91
|
| 1568 |
msgid "Save"
|
| 1569 |
msgstr ""
|
| 1570 |
|
| 1571 |
-
#: lib/Subscription/Pages.php:
|
| 1572 |
msgid "[link]Edit your profile[/link] to update your email."
|
| 1573 |
msgstr ""
|
| 1574 |
|
| 1575 |
-
#: lib/Subscription/Pages.php:
|
| 1576 |
msgid "[link]Log in to your account[/link] to update your email."
|
| 1577 |
msgstr ""
|
| 1578 |
|
| 1579 |
-
#: lib/Subscription/Pages.php:
|
| 1580 |
msgid ""
|
| 1581 |
"Need to change your email address? Unsubscribe here, then simply sign up "
|
| 1582 |
"again."
|
| 1583 |
msgstr ""
|
| 1584 |
|
| 1585 |
-
#: lib/Subscription/Pages.php:
|
| 1586 |
msgid "Accidentally unsubscribed?"
|
| 1587 |
msgstr ""
|
| 1588 |
|
| 4 |
msgstr ""
|
| 5 |
"Project-Id-Version: \n"
|
| 6 |
"Report-Msgid-Bugs-To: http://support.mailpoet.com/\n"
|
| 7 |
+
"POT-Creation-Date: 2016-12-27 22:02:35+00:00\n"
|
| 8 |
"MIME-Version: 1.0\n"
|
| 9 |
"Content-Type: text/plain; charset=utf-8\n"
|
| 10 |
"Content-Transfer-Encoding: 8bit\n"
|
| 79 |
msgid "Copy of %s"
|
| 80 |
msgstr ""
|
| 81 |
|
| 82 |
+
#: lib/API/Endpoints/Mailer.php:26 lib/API/Endpoints/Newsletters.php:285
|
| 83 |
msgid "The email could not be sent: %s"
|
| 84 |
msgstr ""
|
| 85 |
|
| 91 |
#: lib/API/Endpoints/Newsletters.php:28 lib/API/Endpoints/Newsletters.php:117
|
| 92 |
#: lib/API/Endpoints/Newsletters.php:138 lib/API/Endpoints/Newsletters.php:154
|
| 93 |
#: lib/API/Endpoints/Newsletters.php:170 lib/API/Endpoints/Newsletters.php:184
|
| 94 |
+
#: lib/API/Endpoints/Newsletters.php:216 lib/API/Endpoints/Newsletters.php:247
|
| 95 |
#: lib/API/Endpoints/SendingQueue.php:32 lib/API/Endpoints/SendingQueue.php:122
|
| 96 |
#: lib/API/Endpoints/SendingQueue.php:148
|
| 97 |
msgid "This newsletter does not exist."
|
| 105 |
msgid "Newsletter data is missing."
|
| 106 |
msgstr ""
|
| 107 |
|
| 108 |
+
#: lib/API/Endpoints/Newsletters.php:238
|
| 109 |
msgid "Please specify receiver information."
|
| 110 |
msgstr ""
|
| 111 |
|
| 959 |
"fix this issue."
|
| 960 |
msgstr ""
|
| 961 |
|
| 962 |
+
#: lib/Config/Shortcodes.php:85
|
| 963 |
msgid "Oops! There are no newsletters to display."
|
| 964 |
msgstr ""
|
| 965 |
|
| 966 |
+
#: lib/Config/Shortcodes.php:125
|
| 967 |
msgid "Preview in a new tab"
|
| 968 |
msgstr ""
|
| 969 |
|
| 1132 |
msgid "Create a new form"
|
| 1133 |
msgstr ""
|
| 1134 |
|
| 1135 |
+
#: lib/Mailer/Mailer.php:83
|
| 1136 |
msgid "Mailing method does not exist"
|
| 1137 |
msgstr ""
|
| 1138 |
|
| 1139 |
+
#: lib/Mailer/Mailer.php:91
|
| 1140 |
msgid "Mailer is not configured"
|
| 1141 |
msgstr ""
|
| 1142 |
|
| 1143 |
+
#: lib/Mailer/Mailer.php:108
|
| 1144 |
msgid "Sender name and email are not configured"
|
| 1145 |
msgstr ""
|
| 1146 |
|
| 1156 |
msgid "Sending frequency limit has been reached."
|
| 1157 |
msgstr ""
|
| 1158 |
|
| 1159 |
+
#: lib/Mailer/Methods/AmazonSES.php:34
|
| 1160 |
msgid "Unsupported Amazon SES region."
|
| 1161 |
msgstr ""
|
| 1162 |
|
| 1163 |
+
#: lib/Mailer/Methods/AmazonSES.php:63 lib/Mailer/Methods/PHPMail.php:33
|
| 1164 |
+
#: lib/Mailer/Methods/SMTP.php:47 lib/Mailer/Methods/SendGrid.php:32
|
| 1165 |
msgid "%s has returned an unknown error."
|
| 1166 |
msgstr ""
|
| 1167 |
|
| 1173 |
msgid "Please specify a name"
|
| 1174 |
msgstr ""
|
| 1175 |
|
| 1176 |
+
#: lib/Models/CustomField.php:17 lib/Models/Newsletter.php:27
|
| 1177 |
#: views/form/templates/settings/field_form.hbs:16
|
| 1178 |
msgid "Please specify a type"
|
| 1179 |
msgstr ""
|
| 1180 |
|
| 1181 |
+
#: lib/Models/Form.php:50 lib/Models/Newsletter.php:459
|
| 1182 |
+
#: lib/Models/Segment.php:126 lib/Models/Subscriber.php:332
|
| 1183 |
msgid "All"
|
| 1184 |
msgstr ""
|
| 1185 |
|
| 1186 |
+
#: lib/Models/Form.php:55 lib/Models/Newsletter.php:529
|
| 1187 |
+
#: lib/Models/Segment.php:131 lib/Models/Subscriber.php:357 views/forms.html:56
|
| 1188 |
#: views/newsletters.html:75 views/segments.html:50
|
| 1189 |
#: views/subscribers/subscribers.html:34
|
| 1190 |
msgid "Trash"
|
| 1194 |
msgid "Another record already exists. Please specify a different \"%1$s\"."
|
| 1195 |
msgstr ""
|
| 1196 |
|
| 1197 |
+
#: lib/Models/Newsletter.php:229
|
| 1198 |
+
msgid "Deleted list"
|
| 1199 |
+
msgstr ""
|
| 1200 |
+
|
| 1201 |
+
#: lib/Models/Newsletter.php:360 lib/Models/Subscriber.php:270
|
| 1202 |
#: lib/Subscribers/ImportExport/Export/Export.php:170
|
| 1203 |
msgid "All Lists"
|
| 1204 |
msgstr ""
|
| 1205 |
|
| 1206 |
+
#: lib/Models/Newsletter.php:471
|
| 1207 |
#: views/newsletter/templates/blocks/posts/settingsSelection.hbs:12
|
| 1208 |
msgid "Draft"
|
| 1209 |
msgstr ""
|
| 1210 |
|
| 1211 |
+
#: lib/Models/Newsletter.php:479
|
| 1212 |
#: views/newsletter/templates/blocks/posts/settingsSelection.hbs:11
|
| 1213 |
msgid "Scheduled"
|
| 1214 |
msgstr ""
|
| 1215 |
|
| 1216 |
+
#: lib/Models/Newsletter.php:487
|
| 1217 |
msgid "Sending"
|
| 1218 |
msgstr ""
|
| 1219 |
|
| 1220 |
+
#: lib/Models/Newsletter.php:495
|
| 1221 |
msgid "Sent"
|
| 1222 |
msgstr ""
|
| 1223 |
|
| 1224 |
+
#: lib/Models/Newsletter.php:509 views/newsletters.html:82
|
| 1225 |
msgid "Active"
|
| 1226 |
msgstr ""
|
| 1227 |
|
| 1228 |
+
#: lib/Models/Newsletter.php:517
|
| 1229 |
msgid "Not active"
|
| 1230 |
msgstr ""
|
| 1231 |
|
| 1277 |
msgid "Your email address is invalid!"
|
| 1278 |
msgstr ""
|
| 1279 |
|
| 1280 |
+
#: lib/Models/Subscriber.php:207
|
| 1281 |
msgid "You need to wait before subscribing again."
|
| 1282 |
msgstr ""
|
| 1283 |
|
| 1284 |
+
#: lib/Models/Subscriber.php:276
|
| 1285 |
msgid "Subscribers without a list (%s)"
|
| 1286 |
msgstr ""
|
| 1287 |
|
| 1288 |
+
#: lib/Models/Subscriber.php:337 lib/Subscription/Pages.php:280
|
| 1289 |
#: views/segments.html:30 views/subscribers/subscribers.html:51
|
| 1290 |
msgid "Subscribed"
|
| 1291 |
msgstr ""
|
| 1292 |
|
| 1293 |
+
#: lib/Models/Subscriber.php:342 views/segments.html:31
|
| 1294 |
#: views/subscribers/subscribers.html:50
|
| 1295 |
msgid "Unconfirmed"
|
| 1296 |
msgstr ""
|
| 1297 |
|
| 1298 |
+
#: lib/Models/Subscriber.php:347 lib/Subscription/Pages.php:288
|
| 1299 |
#: views/segments.html:32 views/subscribers/subscribers.html:52
|
| 1300 |
msgid "Unsubscribed"
|
| 1301 |
msgstr ""
|
| 1302 |
|
| 1303 |
+
#: lib/Models/Subscriber.php:352 lib/Subscription/Pages.php:296
|
| 1304 |
#: views/segments.html:33 views/subscribers/subscribers.html:53
|
| 1305 |
msgid "Bounced"
|
| 1306 |
msgstr ""
|
| 1309 |
msgid "Click here to view media."
|
| 1310 |
msgstr ""
|
| 1311 |
|
| 1312 |
+
#: lib/Newsletter/Shortcodes/Categories/Link.php:31
|
| 1313 |
#: views/newsletter/editor.html:1037
|
| 1314 |
msgid "Unsubscribe"
|
| 1315 |
msgstr ""
|
| 1316 |
|
| 1317 |
+
#: lib/Newsletter/Shortcodes/Categories/Link.php:52
|
| 1318 |
#: views/newsletter/editor.html:1037
|
| 1319 |
msgid "Manage subscription"
|
| 1320 |
msgstr ""
|
| 1321 |
|
| 1322 |
+
#: lib/Newsletter/Shortcodes/Categories/Link.php:76
|
| 1323 |
msgid "View in your browser"
|
| 1324 |
msgstr ""
|
| 1325 |
|
| 1550 |
msgid "You are now subscribed!"
|
| 1551 |
msgstr ""
|
| 1552 |
|
| 1553 |
+
#: lib/Subscription/Pages.php:177 lib/Subscription/Pages.php:395
|
| 1554 |
msgid "Manage your subscription"
|
| 1555 |
msgstr ""
|
| 1556 |
|
| 1562 |
msgid "Yup, we've added you to our email list. You'll hear from us shortly."
|
| 1563 |
msgstr ""
|
| 1564 |
|
| 1565 |
+
#: lib/Subscription/Pages.php:319
|
| 1566 |
msgid "Your lists"
|
| 1567 |
msgstr ""
|
| 1568 |
|
| 1569 |
+
#: lib/Subscription/Pages.php:327 views/form/editor.html:29
|
| 1570 |
#: views/form/editor.html:383 views/newsletter/templates/components/save.hbs:3
|
| 1571 |
#: views/segments.html:55 views/subscribers/subscribers.html:91
|
| 1572 |
msgid "Save"
|
| 1573 |
msgstr ""
|
| 1574 |
|
| 1575 |
+
#: lib/Subscription/Pages.php:359
|
| 1576 |
msgid "[link]Edit your profile[/link] to update your email."
|
| 1577 |
msgstr ""
|
| 1578 |
|
| 1579 |
+
#: lib/Subscription/Pages.php:365
|
| 1580 |
msgid "[link]Log in to your account[/link] to update your email."
|
| 1581 |
msgstr ""
|
| 1582 |
|
| 1583 |
+
#: lib/Subscription/Pages.php:369
|
| 1584 |
msgid ""
|
| 1585 |
"Need to change your email address? Unsubscribe here, then simply sign up "
|
| 1586 |
"again."
|
| 1587 |
msgstr ""
|
| 1588 |
|
| 1589 |
+
#: lib/Subscription/Pages.php:383
|
| 1590 |
msgid "Accidentally unsubscribed?"
|
| 1591 |
msgstr ""
|
| 1592 |
|
lib/API/Endpoints/Newsletters.php
CHANGED
|
@@ -220,7 +220,9 @@ class Newsletters extends APIEndpoint {
|
|
| 220 |
$newsletter->save();
|
| 221 |
$subscriber = Subscriber::getCurrentWPUser();
|
| 222 |
$preview_url = NewsletterUrl::getViewInBrowserUrl(
|
| 223 |
-
|
|
|
|
|
|
|
| 224 |
);
|
| 225 |
|
| 226 |
return $this->successResponse(
|
|
@@ -310,7 +312,7 @@ class Newsletters extends APIEndpoint {
|
|
| 310 |
|
| 311 |
if($newsletter->type === Newsletter::TYPE_STANDARD) {
|
| 312 |
$newsletter
|
| 313 |
-
->withSegments()
|
| 314 |
->withSendingQueue()
|
| 315 |
->withStatistics();
|
| 316 |
} else if($newsletter->type === Newsletter::TYPE_WELCOME) {
|
|
@@ -321,11 +323,11 @@ class Newsletters extends APIEndpoint {
|
|
| 321 |
} else if($newsletter->type === Newsletter::TYPE_NOTIFICATION) {
|
| 322 |
$newsletter
|
| 323 |
->withOptions()
|
| 324 |
-
->withSegments()
|
| 325 |
->withChildrenCount();
|
| 326 |
} else if($newsletter->type === Newsletter::TYPE_NOTIFICATION_HISTORY) {
|
| 327 |
$newsletter
|
| 328 |
-
->withSegments()
|
| 329 |
->withSendingQueue()
|
| 330 |
->withStatistics();
|
| 331 |
}
|
|
@@ -339,7 +341,11 @@ class Newsletters extends APIEndpoint {
|
|
| 339 |
// get preview url
|
| 340 |
$subscriber = Subscriber::getCurrentWPUser();
|
| 341 |
$newsletter->preview_url = NewsletterUrl::getViewInBrowserUrl(
|
| 342 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 343 |
|
| 344 |
$data[] = $newsletter->asArray();
|
| 345 |
}
|
| 220 |
$newsletter->save();
|
| 221 |
$subscriber = Subscriber::getCurrentWPUser();
|
| 222 |
$preview_url = NewsletterUrl::getViewInBrowserUrl(
|
| 223 |
+
NewsletterUrl::TYPE_LISTING_EDITOR,
|
| 224 |
+
$newsletter,
|
| 225 |
+
$subscriber
|
| 226 |
);
|
| 227 |
|
| 228 |
return $this->successResponse(
|
| 312 |
|
| 313 |
if($newsletter->type === Newsletter::TYPE_STANDARD) {
|
| 314 |
$newsletter
|
| 315 |
+
->withSegments(true)
|
| 316 |
->withSendingQueue()
|
| 317 |
->withStatistics();
|
| 318 |
} else if($newsletter->type === Newsletter::TYPE_WELCOME) {
|
| 323 |
} else if($newsletter->type === Newsletter::TYPE_NOTIFICATION) {
|
| 324 |
$newsletter
|
| 325 |
->withOptions()
|
| 326 |
+
->withSegments(true)
|
| 327 |
->withChildrenCount();
|
| 328 |
} else if($newsletter->type === Newsletter::TYPE_NOTIFICATION_HISTORY) {
|
| 329 |
$newsletter
|
| 330 |
+
->withSegments(true)
|
| 331 |
->withSendingQueue()
|
| 332 |
->withStatistics();
|
| 333 |
}
|
| 341 |
// get preview url
|
| 342 |
$subscriber = Subscriber::getCurrentWPUser();
|
| 343 |
$newsletter->preview_url = NewsletterUrl::getViewInBrowserUrl(
|
| 344 |
+
NewsletterUrl::TYPE_LISTING_EDITOR,
|
| 345 |
+
$newsletter,
|
| 346 |
+
$subscriber,
|
| 347 |
+
$queue
|
| 348 |
+
);
|
| 349 |
|
| 350 |
$data[] = $newsletter->asArray();
|
| 351 |
}
|
lib/Config/Env.php
CHANGED
|
@@ -29,6 +29,7 @@ class Env {
|
|
| 29 |
static $db_password;
|
| 30 |
static $db_charset;
|
| 31 |
static $db_timezone_offset;
|
|
|
|
| 32 |
|
| 33 |
static function init($file, $version) {
|
| 34 |
global $wpdb;
|
| 29 |
static $db_password;
|
| 30 |
static $db_charset;
|
| 31 |
static $db_timezone_offset;
|
| 32 |
+
static $required_permission = 'manage_options';
|
| 33 |
|
| 34 |
static function init($file, $version) {
|
| 35 |
global $wpdb;
|
lib/Config/Initializer.php
CHANGED
|
@@ -53,8 +53,7 @@ class Initializer {
|
|
| 53 |
\ORM::configure('logging', WP_DEBUG);
|
| 54 |
\ORM::configure('driver_options', array(
|
| 55 |
\PDO::MYSQL_ATTR_INIT_COMMAND =>
|
| 56 |
-
'SET
|
| 57 |
-
'TIME_ZONE = "' . Env::$db_timezone_offset . '", ' .
|
| 58 |
'sql_mode=(SELECT REPLACE(@@sql_mode,"ONLY_FULL_GROUP_BY",""))'
|
| 59 |
));
|
| 60 |
|
| 53 |
\ORM::configure('logging', WP_DEBUG);
|
| 54 |
\ORM::configure('driver_options', array(
|
| 55 |
\PDO::MYSQL_ATTR_INIT_COMMAND =>
|
| 56 |
+
'SET TIME_ZONE = "' . Env::$db_timezone_offset . '", ' .
|
|
|
|
| 57 |
'sql_mode=(SELECT REPLACE(@@sql_mode,"ONLY_FULL_GROUP_BY",""))'
|
| 58 |
));
|
| 59 |
|
lib/Config/Menu.php
CHANGED
|
@@ -45,7 +45,7 @@ class Menu {
|
|
| 45 |
add_menu_page(
|
| 46 |
'MailPoet',
|
| 47 |
'MailPoet',
|
| 48 |
-
|
| 49 |
$main_page_slug,
|
| 50 |
null,
|
| 51 |
$this->assets_url . '/img/menu_icon.png',
|
|
@@ -56,7 +56,7 @@ class Menu {
|
|
| 56 |
$main_page_slug,
|
| 57 |
$this->setPageTitle(__('Emails', 'mailpoet')),
|
| 58 |
__('Emails', 'mailpoet'),
|
| 59 |
-
|
| 60 |
$main_page_slug,
|
| 61 |
array($this, 'newsletters')
|
| 62 |
);
|
|
@@ -76,7 +76,7 @@ class Menu {
|
|
| 76 |
$main_page_slug,
|
| 77 |
$this->setPageTitle(__('Forms', 'mailpoet')),
|
| 78 |
__('Forms', 'mailpoet'),
|
| 79 |
-
|
| 80 |
'mailpoet-forms',
|
| 81 |
array($this, 'forms')
|
| 82 |
);
|
|
@@ -95,7 +95,7 @@ class Menu {
|
|
| 95 |
$main_page_slug,
|
| 96 |
$this->setPageTitle(__('Subscribers', 'mailpoet')),
|
| 97 |
__('Subscribers', 'mailpoet'),
|
| 98 |
-
|
| 99 |
'mailpoet-subscribers',
|
| 100 |
array($this, 'subscribers')
|
| 101 |
);
|
|
@@ -114,7 +114,7 @@ class Menu {
|
|
| 114 |
$main_page_slug,
|
| 115 |
$this->setPageTitle(__('Lists', 'mailpoet')),
|
| 116 |
__('Lists', 'mailpoet'),
|
| 117 |
-
|
| 118 |
'mailpoet-segments',
|
| 119 |
array($this, 'segments')
|
| 120 |
);
|
|
@@ -134,7 +134,7 @@ class Menu {
|
|
| 134 |
$main_page_slug,
|
| 135 |
$this->setPageTitle( __('Settings', 'mailpoet')),
|
| 136 |
__('Settings', 'mailpoet'),
|
| 137 |
-
|
| 138 |
'mailpoet-settings',
|
| 139 |
array($this, 'settings')
|
| 140 |
);
|
|
@@ -142,7 +142,7 @@ class Menu {
|
|
| 142 |
'admin.php?page=mailpoet-subscribers',
|
| 143 |
$this->setPageTitle( __('Import', 'mailpoet')),
|
| 144 |
__('Import', 'mailpoet'),
|
| 145 |
-
|
| 146 |
'mailpoet-import',
|
| 147 |
array($this, 'import')
|
| 148 |
);
|
|
@@ -151,7 +151,7 @@ class Menu {
|
|
| 151 |
true,
|
| 152 |
$this->setPageTitle(__('Export', 'mailpoet')),
|
| 153 |
__('Export', 'mailpoet'),
|
| 154 |
-
|
| 155 |
'mailpoet-export',
|
| 156 |
array($this, 'export')
|
| 157 |
);
|
|
@@ -160,7 +160,7 @@ class Menu {
|
|
| 160 |
true,
|
| 161 |
$this->setPageTitle(__('Welcome', 'mailpoet')),
|
| 162 |
__('Welcome', 'mailpoet'),
|
| 163 |
-
|
| 164 |
'mailpoet-welcome',
|
| 165 |
array($this, 'welcome')
|
| 166 |
);
|
|
@@ -169,7 +169,7 @@ class Menu {
|
|
| 169 |
true,
|
| 170 |
$this->setPageTitle(__('Update', 'mailpoet')),
|
| 171 |
__('Update', 'mailpoet'),
|
| 172 |
-
|
| 173 |
'mailpoet-update',
|
| 174 |
array($this, 'update')
|
| 175 |
);
|
|
@@ -178,7 +178,7 @@ class Menu {
|
|
| 178 |
true,
|
| 179 |
$this->setPageTitle(__('Form Editor', 'mailpoet')),
|
| 180 |
__('Form Editor', 'mailpoet'),
|
| 181 |
-
|
| 182 |
'mailpoet-form-editor',
|
| 183 |
array($this, 'formEditor')
|
| 184 |
);
|
|
@@ -187,7 +187,7 @@ class Menu {
|
|
| 187 |
true,
|
| 188 |
$this->setPageTitle(__('Newsletter', 'mailpoet')),
|
| 189 |
__('Newsletter Editor', 'mailpoet'),
|
| 190 |
-
|
| 191 |
'mailpoet-newsletter-editor',
|
| 192 |
array($this, 'newletterEditor')
|
| 193 |
);
|
| 45 |
add_menu_page(
|
| 46 |
'MailPoet',
|
| 47 |
'MailPoet',
|
| 48 |
+
Env::$required_permission,
|
| 49 |
$main_page_slug,
|
| 50 |
null,
|
| 51 |
$this->assets_url . '/img/menu_icon.png',
|
| 56 |
$main_page_slug,
|
| 57 |
$this->setPageTitle(__('Emails', 'mailpoet')),
|
| 58 |
__('Emails', 'mailpoet'),
|
| 59 |
+
Env::$required_permission,
|
| 60 |
$main_page_slug,
|
| 61 |
array($this, 'newsletters')
|
| 62 |
);
|
| 76 |
$main_page_slug,
|
| 77 |
$this->setPageTitle(__('Forms', 'mailpoet')),
|
| 78 |
__('Forms', 'mailpoet'),
|
| 79 |
+
Env::$required_permission,
|
| 80 |
'mailpoet-forms',
|
| 81 |
array($this, 'forms')
|
| 82 |
);
|
| 95 |
$main_page_slug,
|
| 96 |
$this->setPageTitle(__('Subscribers', 'mailpoet')),
|
| 97 |
__('Subscribers', 'mailpoet'),
|
| 98 |
+
Env::$required_permission,
|
| 99 |
'mailpoet-subscribers',
|
| 100 |
array($this, 'subscribers')
|
| 101 |
);
|
| 114 |
$main_page_slug,
|
| 115 |
$this->setPageTitle(__('Lists', 'mailpoet')),
|
| 116 |
__('Lists', 'mailpoet'),
|
| 117 |
+
Env::$required_permission,
|
| 118 |
'mailpoet-segments',
|
| 119 |
array($this, 'segments')
|
| 120 |
);
|
| 134 |
$main_page_slug,
|
| 135 |
$this->setPageTitle( __('Settings', 'mailpoet')),
|
| 136 |
__('Settings', 'mailpoet'),
|
| 137 |
+
Env::$required_permission,
|
| 138 |
'mailpoet-settings',
|
| 139 |
array($this, 'settings')
|
| 140 |
);
|
| 142 |
'admin.php?page=mailpoet-subscribers',
|
| 143 |
$this->setPageTitle( __('Import', 'mailpoet')),
|
| 144 |
__('Import', 'mailpoet'),
|
| 145 |
+
Env::$required_permission,
|
| 146 |
'mailpoet-import',
|
| 147 |
array($this, 'import')
|
| 148 |
);
|
| 151 |
true,
|
| 152 |
$this->setPageTitle(__('Export', 'mailpoet')),
|
| 153 |
__('Export', 'mailpoet'),
|
| 154 |
+
Env::$required_permission,
|
| 155 |
'mailpoet-export',
|
| 156 |
array($this, 'export')
|
| 157 |
);
|
| 160 |
true,
|
| 161 |
$this->setPageTitle(__('Welcome', 'mailpoet')),
|
| 162 |
__('Welcome', 'mailpoet'),
|
| 163 |
+
Env::$required_permission,
|
| 164 |
'mailpoet-welcome',
|
| 165 |
array($this, 'welcome')
|
| 166 |
);
|
| 169 |
true,
|
| 170 |
$this->setPageTitle(__('Update', 'mailpoet')),
|
| 171 |
__('Update', 'mailpoet'),
|
| 172 |
+
Env::$required_permission,
|
| 173 |
'mailpoet-update',
|
| 174 |
array($this, 'update')
|
| 175 |
);
|
| 178 |
true,
|
| 179 |
$this->setPageTitle(__('Form Editor', 'mailpoet')),
|
| 180 |
__('Form Editor', 'mailpoet'),
|
| 181 |
+
Env::$required_permission,
|
| 182 |
'mailpoet-form-editor',
|
| 183 |
array($this, 'formEditor')
|
| 184 |
);
|
| 187 |
true,
|
| 188 |
$this->setPageTitle(__('Newsletter', 'mailpoet')),
|
| 189 |
__('Newsletter Editor', 'mailpoet'),
|
| 190 |
+
Env::$required_permission,
|
| 191 |
'mailpoet-newsletter-editor',
|
| 192 |
array($this, 'newletterEditor')
|
| 193 |
);
|
lib/Config/Migrator.php
CHANGED
|
@@ -177,6 +177,7 @@ class Migrator {
|
|
| 177 |
function newsletters() {
|
| 178 |
$attributes = array(
|
| 179 |
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
|
|
|
| 180 |
'parent_id mediumint(9) NULL,',
|
| 181 |
'subject varchar(250) NOT NULL DEFAULT "",',
|
| 182 |
'type varchar(20) NOT NULL DEFAULT "standard",',
|
| 177 |
function newsletters() {
|
| 178 |
$attributes = array(
|
| 179 |
'id mediumint(9) NOT NULL AUTO_INCREMENT,',
|
| 180 |
+
'hash varchar(150) NULL DEFAULT NULL,',
|
| 181 |
'parent_id mediumint(9) NULL,',
|
| 182 |
'subject varchar(250) NOT NULL DEFAULT "",',
|
| 183 |
'type varchar(20) NOT NULL DEFAULT "standard",',
|
lib/Config/Shortcodes.php
CHANGED
|
@@ -3,7 +3,6 @@ namespace MailPoet\Config;
|
|
| 3 |
use \MailPoet\Models\Newsletter;
|
| 4 |
use \MailPoet\Models\Subscriber;
|
| 5 |
use \MailPoet\Models\SubscriberSegment;
|
| 6 |
-
use \MailPoet\Subscription;
|
| 7 |
use MailPoet\Newsletter\Url as NewsletterUrl;
|
| 8 |
|
| 9 |
class Shortcodes {
|
|
@@ -33,7 +32,7 @@ class Shortcodes {
|
|
| 33 |
), 2);
|
| 34 |
add_filter('mailpoet_archive_subject', array(
|
| 35 |
$this, 'renderArchiveSubject'
|
| 36 |
-
), 2);
|
| 37 |
}
|
| 38 |
|
| 39 |
function formWidget($params = array()) {
|
|
@@ -78,6 +77,8 @@ class Shortcodes {
|
|
| 78 |
|
| 79 |
$newsletters = Newsletter::getArchives($segment_ids);
|
| 80 |
|
|
|
|
|
|
|
| 81 |
if(empty($newsletters)) {
|
| 82 |
return apply_filters(
|
| 83 |
'mailpoet_archive_no_newsletters',
|
|
@@ -91,12 +92,13 @@ class Shortcodes {
|
|
| 91 |
|
| 92 |
$html .= '<ul class="mailpoet_archive">';
|
| 93 |
foreach($newsletters as $newsletter) {
|
|
|
|
| 94 |
$html .= '<li>'.
|
| 95 |
'<span class="mailpoet_archive_date">'.
|
| 96 |
apply_filters('mailpoet_archive_date', $newsletter).
|
| 97 |
'</span>
|
| 98 |
<span class="mailpoet_archive_subject">'.
|
| 99 |
-
apply_filters('mailpoet_archive_subject', $newsletter).
|
| 100 |
'</span>
|
| 101 |
</li>';
|
| 102 |
}
|
|
@@ -112,13 +114,16 @@ class Shortcodes {
|
|
| 112 |
);
|
| 113 |
}
|
| 114 |
|
| 115 |
-
function renderArchiveSubject($newsletter) {
|
| 116 |
-
$preview_url = NewsletterUrl::getViewInBrowserUrl(
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
return '<a href="'.esc_attr($preview_url).'" target="_blank" title="'
|
| 119 |
.esc_attr(__('Preview in a new tab', 'mailpoet')).'">'
|
| 120 |
.esc_attr($newsletter->subject).
|
| 121 |
'</a>';
|
| 122 |
}
|
| 123 |
-
|
| 124 |
-
}
|
| 3 |
use \MailPoet\Models\Newsletter;
|
| 4 |
use \MailPoet\Models\Subscriber;
|
| 5 |
use \MailPoet\Models\SubscriberSegment;
|
|
|
|
| 6 |
use MailPoet\Newsletter\Url as NewsletterUrl;
|
| 7 |
|
| 8 |
class Shortcodes {
|
| 32 |
), 2);
|
| 33 |
add_filter('mailpoet_archive_subject', array(
|
| 34 |
$this, 'renderArchiveSubject'
|
| 35 |
+
), 2, 3);
|
| 36 |
}
|
| 37 |
|
| 38 |
function formWidget($params = array()) {
|
| 77 |
|
| 78 |
$newsletters = Newsletter::getArchives($segment_ids);
|
| 79 |
|
| 80 |
+
$subscriber = Subscriber::getCurrentWPUser();
|
| 81 |
+
|
| 82 |
if(empty($newsletters)) {
|
| 83 |
return apply_filters(
|
| 84 |
'mailpoet_archive_no_newsletters',
|
| 92 |
|
| 93 |
$html .= '<ul class="mailpoet_archive">';
|
| 94 |
foreach($newsletters as $newsletter) {
|
| 95 |
+
$queue = $newsletter->queue()->findOne();
|
| 96 |
$html .= '<li>'.
|
| 97 |
'<span class="mailpoet_archive_date">'.
|
| 98 |
apply_filters('mailpoet_archive_date', $newsletter).
|
| 99 |
'</span>
|
| 100 |
<span class="mailpoet_archive_subject">'.
|
| 101 |
+
apply_filters('mailpoet_archive_subject', $newsletter, $subscriber, $queue).
|
| 102 |
'</span>
|
| 103 |
</li>';
|
| 104 |
}
|
| 114 |
);
|
| 115 |
}
|
| 116 |
|
| 117 |
+
function renderArchiveSubject($newsletter, $subscriber, $queue) {
|
| 118 |
+
$preview_url = NewsletterUrl::getViewInBrowserUrl(
|
| 119 |
+
NewsletterUrl::TYPE_ARCHIVE,
|
| 120 |
+
$newsletter,
|
| 121 |
+
$subscriber,
|
| 122 |
+
$queue
|
| 123 |
+
);
|
| 124 |
return '<a href="'.esc_attr($preview_url).'" target="_blank" title="'
|
| 125 |
.esc_attr(__('Preview in a new tab', 'mailpoet')).'">'
|
| 126 |
.esc_attr($newsletter->subject).
|
| 127 |
'</a>';
|
| 128 |
}
|
| 129 |
+
}
|
|
|
lib/Form/Block/Select.php
CHANGED
|
@@ -29,11 +29,17 @@ class Select extends Base {
|
|
| 29 |
);
|
| 30 |
|
| 31 |
foreach($options as $option) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
$is_selected = (
|
| 33 |
(isset($option['is_checked']) && $option['is_checked'])
|
| 34 |
||
|
| 35 |
(self::getFieldValue($block) === $option['value'])
|
| 36 |
-
) ? 'selected="selected"' : '';
|
|
|
|
|
|
|
| 37 |
|
| 38 |
if(is_array($option['value'])) {
|
| 39 |
$value = key($option['value']);
|
|
@@ -43,7 +49,7 @@ class Select extends Base {
|
|
| 43 |
$label = $option['value'];
|
| 44 |
}
|
| 45 |
|
| 46 |
-
$html .= '<option value="'.$value.'"
|
| 47 |
$html .= esc_attr($label);
|
| 48 |
$html .= '</option>';
|
| 49 |
}
|
| 29 |
);
|
| 30 |
|
| 31 |
foreach($options as $option) {
|
| 32 |
+
if(!empty($option['is_hidden'])) {
|
| 33 |
+
continue;
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
$is_selected = (
|
| 37 |
(isset($option['is_checked']) && $option['is_checked'])
|
| 38 |
||
|
| 39 |
(self::getFieldValue($block) === $option['value'])
|
| 40 |
+
) ? ' selected="selected"' : '';
|
| 41 |
+
|
| 42 |
+
$is_disabled = (!empty($option['is_disabled'])) ? ' disabled="disabled"' : '';
|
| 43 |
|
| 44 |
if(is_array($option['value'])) {
|
| 45 |
$value = key($option['value']);
|
| 49 |
$label = $option['value'];
|
| 50 |
}
|
| 51 |
|
| 52 |
+
$html .= '<option value="'.$value.'"' . $is_selected . $is_disabled . '>';
|
| 53 |
$html .= esc_attr($label);
|
| 54 |
$html .= '</option>';
|
| 55 |
}
|
lib/Mailer/Mailer.php
CHANGED
|
@@ -10,6 +10,7 @@ class Mailer {
|
|
| 10 |
public $mailer_config;
|
| 11 |
public $sender;
|
| 12 |
public $reply_to;
|
|
|
|
| 13 |
public $mailer_instance;
|
| 14 |
const MAILER_CONFIG_SETTING_NAME = 'mta';
|
| 15 |
const SENDING_LIMIT_INTERVAL_MULTIPLIER = 60;
|
|
@@ -19,10 +20,11 @@ class Mailer {
|
|
| 19 |
const METHOD_PHPMAIL = 'PHPMail';
|
| 20 |
const METHOD_SMTP = 'SMTP';
|
| 21 |
|
| 22 |
-
function __construct($mailer = false, $sender = false, $reply_to = false) {
|
| 23 |
$this->mailer_config = self::getMailerConfig($mailer);
|
| 24 |
$this->sender = $this->getSenderNameAndAddress($sender);
|
| 25 |
$this->reply_to = $this->getReplyToNameAndAddress($reply_to);
|
|
|
|
| 26 |
$this->mailer_instance = $this->buildMailer();
|
| 27 |
}
|
| 28 |
|
|
@@ -39,7 +41,8 @@ class Mailer {
|
|
| 39 |
$this->mailer_config['access_key'],
|
| 40 |
$this->mailer_config['secret_key'],
|
| 41 |
$this->sender,
|
| 42 |
-
$this->reply_to
|
|
|
|
| 43 |
);
|
| 44 |
break;
|
| 45 |
case self::METHOD_MAILPOET:
|
|
@@ -59,7 +62,8 @@ class Mailer {
|
|
| 59 |
case self::METHOD_PHPMAIL:
|
| 60 |
$mailer_instance = new $this->mailer_config['class'](
|
| 61 |
$this->sender,
|
| 62 |
-
$this->reply_to
|
|
|
|
| 63 |
);
|
| 64 |
break;
|
| 65 |
case self::METHOD_SMTP:
|
|
@@ -71,7 +75,8 @@ class Mailer {
|
|
| 71 |
$this->mailer_config['password'],
|
| 72 |
$this->mailer_config['encryption'],
|
| 73 |
$this->sender,
|
| 74 |
-
$this->reply_to
|
|
|
|
| 75 |
);
|
| 76 |
break;
|
| 77 |
default:
|
|
@@ -112,7 +117,7 @@ class Mailer {
|
|
| 112 |
|
| 113 |
function getReplyToNameAndAddress($reply_to = array()) {
|
| 114 |
if(!$reply_to) {
|
| 115 |
-
$reply_to = Setting::getValue('reply_to'
|
| 116 |
$reply_to['name'] = (!empty($reply_to['name'])) ?
|
| 117 |
$reply_to['name'] :
|
| 118 |
$this->sender['from_name'];
|
|
@@ -131,6 +136,12 @@ class Mailer {
|
|
| 131 |
);
|
| 132 |
}
|
| 133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 134 |
function formatSubscriberNameAndEmailAddress($subscriber) {
|
| 135 |
$subscriber = (is_object($subscriber)) ? $subscriber->asArray() : $subscriber;
|
| 136 |
if(!is_array($subscriber)) return $subscriber;
|
| 10 |
public $mailer_config;
|
| 11 |
public $sender;
|
| 12 |
public $reply_to;
|
| 13 |
+
public $return_path;
|
| 14 |
public $mailer_instance;
|
| 15 |
const MAILER_CONFIG_SETTING_NAME = 'mta';
|
| 16 |
const SENDING_LIMIT_INTERVAL_MULTIPLIER = 60;
|
| 20 |
const METHOD_PHPMAIL = 'PHPMail';
|
| 21 |
const METHOD_SMTP = 'SMTP';
|
| 22 |
|
| 23 |
+
function __construct($mailer = false, $sender = false, $reply_to = false, $return_path = false) {
|
| 24 |
$this->mailer_config = self::getMailerConfig($mailer);
|
| 25 |
$this->sender = $this->getSenderNameAndAddress($sender);
|
| 26 |
$this->reply_to = $this->getReplyToNameAndAddress($reply_to);
|
| 27 |
+
$this->return_path = $this->getReturnPathAddress($return_path);
|
| 28 |
$this->mailer_instance = $this->buildMailer();
|
| 29 |
}
|
| 30 |
|
| 41 |
$this->mailer_config['access_key'],
|
| 42 |
$this->mailer_config['secret_key'],
|
| 43 |
$this->sender,
|
| 44 |
+
$this->reply_to,
|
| 45 |
+
$this->return_path
|
| 46 |
);
|
| 47 |
break;
|
| 48 |
case self::METHOD_MAILPOET:
|
| 62 |
case self::METHOD_PHPMAIL:
|
| 63 |
$mailer_instance = new $this->mailer_config['class'](
|
| 64 |
$this->sender,
|
| 65 |
+
$this->reply_to,
|
| 66 |
+
$this->return_path
|
| 67 |
);
|
| 68 |
break;
|
| 69 |
case self::METHOD_SMTP:
|
| 75 |
$this->mailer_config['password'],
|
| 76 |
$this->mailer_config['encryption'],
|
| 77 |
$this->sender,
|
| 78 |
+
$this->reply_to,
|
| 79 |
+
$this->return_path
|
| 80 |
);
|
| 81 |
break;
|
| 82 |
default:
|
| 117 |
|
| 118 |
function getReplyToNameAndAddress($reply_to = array()) {
|
| 119 |
if(!$reply_to) {
|
| 120 |
+
$reply_to = Setting::getValue('reply_to');
|
| 121 |
$reply_to['name'] = (!empty($reply_to['name'])) ?
|
| 122 |
$reply_to['name'] :
|
| 123 |
$this->sender['from_name'];
|
| 136 |
);
|
| 137 |
}
|
| 138 |
|
| 139 |
+
function getReturnPathAddress($return_path) {
|
| 140 |
+
return ($return_path) ?
|
| 141 |
+
$return_path :
|
| 142 |
+
Setting::getValue('bounce.address');
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
function formatSubscriberNameAndEmailAddress($subscriber) {
|
| 146 |
$subscriber = (is_object($subscriber)) ? $subscriber->asArray() : $subscriber;
|
| 147 |
if(!is_array($subscriber)) return $subscriber;
|
lib/Mailer/Methods/AmazonSES.php
CHANGED
|
@@ -17,6 +17,7 @@ class AmazonSES {
|
|
| 17 |
public $url;
|
| 18 |
public $sender;
|
| 19 |
public $reply_to;
|
|
|
|
| 20 |
public $date;
|
| 21 |
public $date_without_time;
|
| 22 |
private $available_regions = array(
|
|
@@ -25,7 +26,7 @@ class AmazonSES {
|
|
| 25 |
'EU (Ireland)' => 'eu-west-1'
|
| 26 |
);
|
| 27 |
|
| 28 |
-
function __construct($region, $access_key, $secret_key, $sender, $reply_to) {
|
| 29 |
$this->aws_access_key = $access_key;
|
| 30 |
$this->aws_secret_key = $secret_key;
|
| 31 |
$this->aws_region = (in_array($region, $this->available_regions)) ? $region : false;
|
|
@@ -40,11 +41,13 @@ class AmazonSES {
|
|
| 40 |
$this->url = 'https://' . $this->aws_endpoint;
|
| 41 |
$this->sender = $sender;
|
| 42 |
$this->reply_to = $reply_to;
|
|
|
|
|
|
|
|
|
|
| 43 |
$this->date = gmdate('Ymd\THis\Z');
|
| 44 |
$this->date_without_time = gmdate('Ymd');
|
| 45 |
}
|
| 46 |
|
| 47 |
-
|
| 48 |
function send($newsletter, $subscriber) {
|
| 49 |
$result = wp_remote_post(
|
| 50 |
$this->url,
|
|
@@ -71,7 +74,7 @@ class AmazonSES {
|
|
| 71 |
'Source' => $this->sender['from_name_email'],
|
| 72 |
'ReplyToAddresses.member.1' => $this->reply_to['reply_to_name_email'],
|
| 73 |
'Message.Subject.Data' => $newsletter['subject'],
|
| 74 |
-
'ReturnPath' => $this->
|
| 75 |
);
|
| 76 |
if(!empty($newsletter['body']['html'])) {
|
| 77 |
$body['Message.Body.Html.Data'] = $newsletter['body']['html'];
|
| 17 |
public $url;
|
| 18 |
public $sender;
|
| 19 |
public $reply_to;
|
| 20 |
+
public $return_path;
|
| 21 |
public $date;
|
| 22 |
public $date_without_time;
|
| 23 |
private $available_regions = array(
|
| 26 |
'EU (Ireland)' => 'eu-west-1'
|
| 27 |
);
|
| 28 |
|
| 29 |
+
function __construct($region, $access_key, $secret_key, $sender, $reply_to, $return_path) {
|
| 30 |
$this->aws_access_key = $access_key;
|
| 31 |
$this->aws_secret_key = $secret_key;
|
| 32 |
$this->aws_region = (in_array($region, $this->available_regions)) ? $region : false;
|
| 41 |
$this->url = 'https://' . $this->aws_endpoint;
|
| 42 |
$this->sender = $sender;
|
| 43 |
$this->reply_to = $reply_to;
|
| 44 |
+
$this->return_path = ($return_path) ?
|
| 45 |
+
$return_path :
|
| 46 |
+
$this->sender['from_email'];
|
| 47 |
$this->date = gmdate('Ymd\THis\Z');
|
| 48 |
$this->date_without_time = gmdate('Ymd');
|
| 49 |
}
|
| 50 |
|
|
|
|
| 51 |
function send($newsletter, $subscriber) {
|
| 52 |
$result = wp_remote_post(
|
| 53 |
$this->url,
|
| 74 |
'Source' => $this->sender['from_name_email'],
|
| 75 |
'ReplyToAddresses.member.1' => $this->reply_to['reply_to_name_email'],
|
| 76 |
'Message.Subject.Data' => $newsletter['subject'],
|
| 77 |
+
'ReturnPath' => $this->return_path
|
| 78 |
);
|
| 79 |
if(!empty($newsletter['body']['html'])) {
|
| 80 |
$body['Message.Body.Html.Data'] = $newsletter['body']['html'];
|
lib/Mailer/Methods/PHPMail.php
CHANGED
|
@@ -8,11 +8,15 @@ if(!defined('ABSPATH')) exit;
|
|
| 8 |
class PHPMail {
|
| 9 |
public $sender;
|
| 10 |
public $reply_to;
|
|
|
|
| 11 |
public $mailer;
|
| 12 |
|
| 13 |
-
function __construct($sender, $reply_to) {
|
| 14 |
$this->sender = $sender;
|
| 15 |
$this->reply_to = $reply_to;
|
|
|
|
|
|
|
|
|
|
| 16 |
$this->mailer = $this->buildMailer();
|
| 17 |
}
|
| 18 |
|
|
@@ -44,6 +48,7 @@ class PHPMail {
|
|
| 44 |
->setReplyTo(array(
|
| 45 |
$this->reply_to['reply_to_email'] => $this->reply_to['reply_to_name']
|
| 46 |
))
|
|
|
|
| 47 |
->setSubject($newsletter['subject']);
|
| 48 |
if(!empty($newsletter['body']['html'])) {
|
| 49 |
$message = $message->setBody($newsletter['body']['html'], 'text/html');
|
| 8 |
class PHPMail {
|
| 9 |
public $sender;
|
| 10 |
public $reply_to;
|
| 11 |
+
public $return_path;
|
| 12 |
public $mailer;
|
| 13 |
|
| 14 |
+
function __construct($sender, $reply_to, $return_path) {
|
| 15 |
$this->sender = $sender;
|
| 16 |
$this->reply_to = $reply_to;
|
| 17 |
+
$this->return_path = ($return_path) ?
|
| 18 |
+
$return_path :
|
| 19 |
+
$this->sender['from_email'];
|
| 20 |
$this->mailer = $this->buildMailer();
|
| 21 |
}
|
| 22 |
|
| 48 |
->setReplyTo(array(
|
| 49 |
$this->reply_to['reply_to_email'] => $this->reply_to['reply_to_name']
|
| 50 |
))
|
| 51 |
+
->setReturnPath($this->return_path)
|
| 52 |
->setSubject($newsletter['subject']);
|
| 53 |
if(!empty($newsletter['body']['html'])) {
|
| 54 |
$message = $message->setBody($newsletter['body']['html'], 'text/html');
|
lib/Mailer/Methods/SMTP.php
CHANGED
|
@@ -14,11 +14,12 @@ class SMTP {
|
|
| 14 |
public $encryption;
|
| 15 |
public $sender;
|
| 16 |
public $reply_to;
|
|
|
|
| 17 |
public $mailer;
|
| 18 |
|
| 19 |
function __construct(
|
| 20 |
$host, $port, $authentication, $login = null, $password = null, $encryption,
|
| 21 |
-
$sender, $reply_to) {
|
| 22 |
$this->host = $host;
|
| 23 |
$this->port = $port;
|
| 24 |
$this->authentication = $authentication;
|
|
@@ -27,6 +28,9 @@ class SMTP {
|
|
| 27 |
$this->encryption = $encryption;
|
| 28 |
$this->sender = $sender;
|
| 29 |
$this->reply_to = $reply_to;
|
|
|
|
|
|
|
|
|
|
| 30 |
$this->mailer = $this->buildMailer();
|
| 31 |
}
|
| 32 |
|
|
@@ -65,6 +69,7 @@ class SMTP {
|
|
| 65 |
->setReplyTo(array(
|
| 66 |
$this->reply_to['reply_to_email'] => $this->reply_to['reply_to_name']
|
| 67 |
))
|
|
|
|
| 68 |
->setSubject($newsletter['subject']);
|
| 69 |
if(!empty($newsletter['body']['html'])) {
|
| 70 |
$message = $message->setBody($newsletter['body']['html'], 'text/html');
|
| 14 |
public $encryption;
|
| 15 |
public $sender;
|
| 16 |
public $reply_to;
|
| 17 |
+
public $return_path;
|
| 18 |
public $mailer;
|
| 19 |
|
| 20 |
function __construct(
|
| 21 |
$host, $port, $authentication, $login = null, $password = null, $encryption,
|
| 22 |
+
$sender, $reply_to, $return_path) {
|
| 23 |
$this->host = $host;
|
| 24 |
$this->port = $port;
|
| 25 |
$this->authentication = $authentication;
|
| 28 |
$this->encryption = $encryption;
|
| 29 |
$this->sender = $sender;
|
| 30 |
$this->reply_to = $reply_to;
|
| 31 |
+
$this->return_path = ($return_path) ?
|
| 32 |
+
$return_path :
|
| 33 |
+
$this->sender['from_email'];
|
| 34 |
$this->mailer = $this->buildMailer();
|
| 35 |
}
|
| 36 |
|
| 69 |
->setReplyTo(array(
|
| 70 |
$this->reply_to['reply_to_email'] => $this->reply_to['reply_to_name']
|
| 71 |
))
|
| 72 |
+
->setReturnPath($this->return_path)
|
| 73 |
->setSubject($newsletter['subject']);
|
| 74 |
if(!empty($newsletter['body']['html'])) {
|
| 75 |
$message = $message->setBody($newsletter['body']['html'], 'text/html');
|
lib/Models/Newsletter.php
CHANGED
|
@@ -2,6 +2,7 @@
|
|
| 2 |
namespace MailPoet\Models;
|
| 3 |
use MailPoet\Newsletter\Renderer\Renderer;
|
| 4 |
use MailPoet\Util\Helpers;
|
|
|
|
| 5 |
|
| 6 |
if(!defined('ABSPATH')) exit;
|
| 7 |
|
|
@@ -11,7 +12,6 @@ class Newsletter extends Model {
|
|
| 11 |
const TYPE_WELCOME = 'welcome';
|
| 12 |
const TYPE_NOTIFICATION = 'notification';
|
| 13 |
const TYPE_NOTIFICATION_HISTORY = 'notification_history';
|
| 14 |
-
|
| 15 |
// standard newsletters
|
| 16 |
const STATUS_DRAFT = 'draft';
|
| 17 |
const STATUS_SCHEDULED = 'scheduled';
|
|
@@ -19,6 +19,7 @@ class Newsletter extends Model {
|
|
| 19 |
const STATUS_SENT = 'sent';
|
| 20 |
// automatic newsletters status
|
| 21 |
const STATUS_ACTIVE = 'active';
|
|
|
|
| 22 |
|
| 23 |
function __construct() {
|
| 24 |
parent::__construct();
|
|
@@ -27,6 +28,10 @@ class Newsletter extends Model {
|
|
| 27 |
));
|
| 28 |
}
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
function save() {
|
| 31 |
if(is_string($this->deleted_at) && strlen(trim($this->deleted_at)) === 0) {
|
| 32 |
$this->set_expr('deleted_at', 'NULL');
|
|
@@ -37,6 +42,12 @@ class Newsletter extends Model {
|
|
| 37 |
? json_encode($this->body)
|
| 38 |
: $this->body
|
| 39 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
return parent::save();
|
| 41 |
}
|
| 42 |
|
|
@@ -74,6 +85,9 @@ class Newsletter extends Model {
|
|
| 74 |
// reset status
|
| 75 |
$duplicate->set('status', self::STATUS_DRAFT);
|
| 76 |
|
|
|
|
|
|
|
|
|
|
| 77 |
$duplicate->save();
|
| 78 |
|
| 79 |
if($duplicate->getErrors() === false) {
|
|
@@ -130,6 +144,9 @@ class Newsletter extends Model {
|
|
| 130 |
$notification_history->set_expr('updated_at', 'NOW()');
|
| 131 |
$notification_history->set_expr('deleted_at', 'NULL');
|
| 132 |
|
|
|
|
|
|
|
|
|
|
| 133 |
$notification_history->save();
|
| 134 |
|
| 135 |
if($notification_history->getErrors() === false) {
|
|
@@ -182,8 +199,39 @@ class Newsletter extends Model {
|
|
| 182 |
);
|
| 183 |
}
|
| 184 |
|
| 185 |
-
function withSegments() {
|
| 186 |
$this->segments = $this->segments()->findArray();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
return $this;
|
| 188 |
}
|
| 189 |
|
|
@@ -634,6 +682,7 @@ class Newsletter extends Model {
|
|
| 634 |
'queues'
|
| 635 |
)
|
| 636 |
->where('queues.status', SendingQueue::STATUS_COMPLETED)
|
|
|
|
| 637 |
->select('queues.processed_at')
|
| 638 |
->orderByDesc('queues.processed_at');
|
| 639 |
|
|
@@ -647,4 +696,17 @@ class Newsletter extends Model {
|
|
| 647 |
}
|
| 648 |
return $orm->findMany();
|
| 649 |
}
|
| 650 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
namespace MailPoet\Models;
|
| 3 |
use MailPoet\Newsletter\Renderer\Renderer;
|
| 4 |
use MailPoet\Util\Helpers;
|
| 5 |
+
use MailPoet\Util\Security;
|
| 6 |
|
| 7 |
if(!defined('ABSPATH')) exit;
|
| 8 |
|
| 12 |
const TYPE_WELCOME = 'welcome';
|
| 13 |
const TYPE_NOTIFICATION = 'notification';
|
| 14 |
const TYPE_NOTIFICATION_HISTORY = 'notification_history';
|
|
|
|
| 15 |
// standard newsletters
|
| 16 |
const STATUS_DRAFT = 'draft';
|
| 17 |
const STATUS_SCHEDULED = 'scheduled';
|
| 19 |
const STATUS_SENT = 'sent';
|
| 20 |
// automatic newsletters status
|
| 21 |
const STATUS_ACTIVE = 'active';
|
| 22 |
+
const NEWSLETTER_HASH_LENGTH = 6;
|
| 23 |
|
| 24 |
function __construct() {
|
| 25 |
parent::__construct();
|
| 28 |
));
|
| 29 |
}
|
| 30 |
|
| 31 |
+
function queue() {
|
| 32 |
+
return $this->has_one(__NAMESPACE__ . '\SendingQueue', 'newsletter_id', 'id');
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
function save() {
|
| 36 |
if(is_string($this->deleted_at) && strlen(trim($this->deleted_at)) === 0) {
|
| 37 |
$this->set_expr('deleted_at', 'NULL');
|
| 42 |
? json_encode($this->body)
|
| 43 |
: $this->body
|
| 44 |
);
|
| 45 |
+
|
| 46 |
+
$this->set('hash',
|
| 47 |
+
($this->hash)
|
| 48 |
+
? $this->hash
|
| 49 |
+
: self::generateHash()
|
| 50 |
+
);
|
| 51 |
return parent::save();
|
| 52 |
}
|
| 53 |
|
| 85 |
// reset status
|
| 86 |
$duplicate->set('status', self::STATUS_DRAFT);
|
| 87 |
|
| 88 |
+
// reset hash
|
| 89 |
+
$duplicate->set('hash', null);
|
| 90 |
+
|
| 91 |
$duplicate->save();
|
| 92 |
|
| 93 |
if($duplicate->getErrors() === false) {
|
| 144 |
$notification_history->set_expr('updated_at', 'NOW()');
|
| 145 |
$notification_history->set_expr('deleted_at', 'NULL');
|
| 146 |
|
| 147 |
+
// reset hash
|
| 148 |
+
$notification_history->set('hash', null);
|
| 149 |
+
|
| 150 |
$notification_history->save();
|
| 151 |
|
| 152 |
if($notification_history->getErrors() === false) {
|
| 199 |
);
|
| 200 |
}
|
| 201 |
|
| 202 |
+
function withSegments($incl_deleted = false) {
|
| 203 |
$this->segments = $this->segments()->findArray();
|
| 204 |
+
if($incl_deleted) {
|
| 205 |
+
$this->withDeletedSegments();
|
| 206 |
+
}
|
| 207 |
+
return $this;
|
| 208 |
+
}
|
| 209 |
+
|
| 210 |
+
// Intermediary table only
|
| 211 |
+
function segmentLinks() {
|
| 212 |
+
return $this->has_many(
|
| 213 |
+
__NAMESPACE__.'\NewsletterSegment',
|
| 214 |
+
'newsletter_id',
|
| 215 |
+
'id'
|
| 216 |
+
);
|
| 217 |
+
}
|
| 218 |
+
|
| 219 |
+
function withDeletedSegments() {
|
| 220 |
+
if(!empty($this->segments)) {
|
| 221 |
+
$segment_ids = Helpers::arrayColumn($this->segments, 'id');
|
| 222 |
+
$links = $this->segmentLinks()
|
| 223 |
+
->whereNotIn('segment_id', $segment_ids)->findArray();
|
| 224 |
+
$deleted_segments = array();
|
| 225 |
+
|
| 226 |
+
foreach($links as $link) {
|
| 227 |
+
$deleted_segments[] = array(
|
| 228 |
+
'id' => $link['segment_id'],
|
| 229 |
+
'name' => __('Deleted list', 'mailpoet')
|
| 230 |
+
);
|
| 231 |
+
}
|
| 232 |
+
$this->segments = array_merge($this->segments, $deleted_segments);
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
return $this;
|
| 236 |
}
|
| 237 |
|
| 682 |
'queues'
|
| 683 |
)
|
| 684 |
->where('queues.status', SendingQueue::STATUS_COMPLETED)
|
| 685 |
+
->whereNull('newsletters.deleted_at')
|
| 686 |
->select('queues.processed_at')
|
| 687 |
->orderByDesc('queues.processed_at');
|
| 688 |
|
| 696 |
}
|
| 697 |
return $orm->findMany();
|
| 698 |
}
|
| 699 |
+
|
| 700 |
+
static function getByHash($hash) {
|
| 701 |
+
return parent::where('hash', $hash)
|
| 702 |
+
->findOne();
|
| 703 |
+
}
|
| 704 |
+
|
| 705 |
+
static function generateHash() {
|
| 706 |
+
return substr(
|
| 707 |
+
md5(AUTH_KEY . Security::generateRandomString(15)),
|
| 708 |
+
0,
|
| 709 |
+
self::NEWSLETTER_HASH_LENGTH
|
| 710 |
+
);
|
| 711 |
+
}
|
| 712 |
+
}
|
lib/Models/SendingQueue.php
CHANGED
|
@@ -70,10 +70,13 @@ class SendingQueue extends Model {
|
|
| 70 |
return $subscribers;
|
| 71 |
}
|
| 72 |
|
| 73 |
-
function getNewsletterRenderedBody() {
|
| 74 |
-
|
| 75 |
$this->newsletter_rendered_body :
|
| 76 |
unserialize($this->newsletter_rendered_body);
|
|
|
|
|
|
|
|
|
|
| 77 |
}
|
| 78 |
|
| 79 |
function isSubscriberProcessed($subscriber_id) {
|
| 70 |
return $subscribers;
|
| 71 |
}
|
| 72 |
|
| 73 |
+
function getNewsletterRenderedBody($type = false) {
|
| 74 |
+
$rendered_newsletter = (!is_serialized($this->newsletter_rendered_body)) ?
|
| 75 |
$this->newsletter_rendered_body :
|
| 76 |
unserialize($this->newsletter_rendered_body);
|
| 77 |
+
return ($type && !empty($rendered_newsletter[$type])) ?
|
| 78 |
+
$rendered_newsletter[$type] :
|
| 79 |
+
$rendered_newsletter;
|
| 80 |
}
|
| 81 |
|
| 82 |
function isSubscriberProcessed($subscriber_id) {
|
lib/Models/Subscriber.php
CHANGED
|
@@ -14,8 +14,8 @@ class Subscriber extends Model {
|
|
| 14 |
const STATUS_UNSUBSCRIBED = 'unsubscribed';
|
| 15 |
const STATUS_UNCONFIRMED = 'unconfirmed';
|
| 16 |
const STATUS_BOUNCED = 'bounced';
|
| 17 |
-
|
| 18 |
const SUBSCRIPTION_LIMIT_COOLDOWN = 60;
|
|
|
|
| 19 |
|
| 20 |
function __construct() {
|
| 21 |
parent::__construct();
|
|
@@ -154,13 +154,17 @@ class Subscriber extends Model {
|
|
| 154 |
|
| 155 |
static function generateToken($email = null) {
|
| 156 |
if($email !== null) {
|
| 157 |
-
return md5(AUTH_KEY
|
| 158 |
}
|
| 159 |
return false;
|
| 160 |
}
|
| 161 |
|
| 162 |
static function verifyToken($email, $token) {
|
| 163 |
-
return call_user_func(
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
}
|
| 165 |
|
| 166 |
static function subscribe($subscriber_data = array(), $segment_ids = array()) {
|
| 14 |
const STATUS_UNSUBSCRIBED = 'unsubscribed';
|
| 15 |
const STATUS_UNCONFIRMED = 'unconfirmed';
|
| 16 |
const STATUS_BOUNCED = 'bounced';
|
|
|
|
| 17 |
const SUBSCRIPTION_LIMIT_COOLDOWN = 60;
|
| 18 |
+
const SUBSCRIBER_TOKEN_LENGTH = 6;
|
| 19 |
|
| 20 |
function __construct() {
|
| 21 |
parent::__construct();
|
| 154 |
|
| 155 |
static function generateToken($email = null) {
|
| 156 |
if($email !== null) {
|
| 157 |
+
return substr(md5(AUTH_KEY . $email), 0, self::SUBSCRIBER_TOKEN_LENGTH);
|
| 158 |
}
|
| 159 |
return false;
|
| 160 |
}
|
| 161 |
|
| 162 |
static function verifyToken($email, $token) {
|
| 163 |
+
return call_user_func(
|
| 164 |
+
'hash_equals',
|
| 165 |
+
self::generateToken($email),
|
| 166 |
+
substr($token, 0, self::SUBSCRIBER_TOKEN_LENGTH)
|
| 167 |
+
);
|
| 168 |
}
|
| 169 |
|
| 170 |
static function subscribe($subscriber_data = array(), $segment_ids = array()) {
|
lib/Newsletter/Links/Links.php
CHANGED
|
@@ -6,6 +6,7 @@ use MailPoet\Router\Router;
|
|
| 6 |
use MailPoet\Router\Endpoints\Track as TrackEndpoint;
|
| 7 |
use MailPoet\Models\NewsletterLink;
|
| 8 |
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
|
|
|
| 9 |
use MailPoet\Util\Security;
|
| 10 |
|
| 11 |
class Links {
|
|
@@ -101,24 +102,19 @@ class Links {
|
|
| 101 |
$preview = false
|
| 102 |
) {
|
| 103 |
// match data tags
|
| 104 |
-
$regex = sprintf(
|
| 105 |
-
'/((%s|%s)(?:-\w+)?)/',
|
| 106 |
-
preg_quote(self::DATA_TAG_CLICK),
|
| 107 |
-
preg_quote(self::DATA_TAG_OPEN)
|
| 108 |
-
);
|
| 109 |
$subscriber = Subscriber::findOne($subscriber_id);
|
| 110 |
-
preg_match_all(
|
| 111 |
foreach($matches[1] as $index => $match) {
|
| 112 |
$hash = null;
|
| 113 |
if(preg_match('/-/', $match)) {
|
| 114 |
list(, $hash) = explode('-', $match);
|
| 115 |
}
|
| 116 |
-
$data =
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
);
|
| 123 |
$router_action = ($matches[2][$index] === self::DATA_TAG_CLICK) ?
|
| 124 |
TrackEndpoint::ACTION_CLICK :
|
|
@@ -144,4 +140,54 @@ class Links {
|
|
| 144 |
$newsletter_link->save();
|
| 145 |
}
|
| 146 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
}
|
| 6 |
use MailPoet\Router\Endpoints\Track as TrackEndpoint;
|
| 7 |
use MailPoet\Models\NewsletterLink;
|
| 8 |
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
| 9 |
+
use MailPoet\Util\Helpers;
|
| 10 |
use MailPoet\Util\Security;
|
| 11 |
|
| 12 |
class Links {
|
| 102 |
$preview = false
|
| 103 |
) {
|
| 104 |
// match data tags
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
$subscriber = Subscriber::findOne($subscriber_id);
|
| 106 |
+
preg_match_all(self::getLinkRegex(), $content, $matches);
|
| 107 |
foreach($matches[1] as $index => $match) {
|
| 108 |
$hash = null;
|
| 109 |
if(preg_match('/-/', $match)) {
|
| 110 |
list(, $hash) = explode('-', $match);
|
| 111 |
}
|
| 112 |
+
$data = self::createUrlDataObject(
|
| 113 |
+
$subscriber->id,
|
| 114 |
+
$subscriber->email,
|
| 115 |
+
$queue_id,
|
| 116 |
+
$hash,
|
| 117 |
+
$preview
|
| 118 |
);
|
| 119 |
$router_action = ($matches[2][$index] === self::DATA_TAG_CLICK) ?
|
| 120 |
TrackEndpoint::ACTION_CLICK :
|
| 140 |
$newsletter_link->save();
|
| 141 |
}
|
| 142 |
}
|
| 143 |
+
|
| 144 |
+
static function convertHashedLinksToShortcodesAndUrls($content, $convert_all = false) {
|
| 145 |
+
preg_match_all(self::getLinkRegex(), $content, $links);
|
| 146 |
+
$links = array_unique(Helpers::flattenArray($links));
|
| 147 |
+
foreach($links as $link) {
|
| 148 |
+
$link_hash = explode('-', $link);
|
| 149 |
+
if(!isset($link_hash[1])) continue;
|
| 150 |
+
$newsletter_link = NewsletterLink::getByHash($link_hash[1]);
|
| 151 |
+
// convert either only link shortcodes or all hashes links if "convert all"
|
| 152 |
+
// option is specified
|
| 153 |
+
if($newsletter_link &&
|
| 154 |
+
(preg_match('/\[link:/', $newsletter_link->url) || $convert_all)
|
| 155 |
+
) {
|
| 156 |
+
$content = str_replace($link, $newsletter_link->url, $content);
|
| 157 |
+
}
|
| 158 |
+
}
|
| 159 |
+
return $content;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
static function getLinkRegex() {
|
| 163 |
+
return sprintf(
|
| 164 |
+
'/((%s|%s)(?:-\w+)?)/',
|
| 165 |
+
preg_quote(self::DATA_TAG_CLICK),
|
| 166 |
+
preg_quote(self::DATA_TAG_OPEN)
|
| 167 |
+
);
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
static function createUrlDataObject(
|
| 171 |
+
$subscriber_id, $subscriber_email, $queue_id, $link_hash, $preview
|
| 172 |
+
) {
|
| 173 |
+
return array(
|
| 174 |
+
$subscriber_id,
|
| 175 |
+
Subscriber::generateToken($subscriber_email),
|
| 176 |
+
$queue_id,
|
| 177 |
+
$link_hash,
|
| 178 |
+
$preview
|
| 179 |
+
);
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
static function transformUrlDataObject($data) {
|
| 183 |
+
reset($data);
|
| 184 |
+
if(!is_int(key($data))) return $data;
|
| 185 |
+
$transformed_data = array();
|
| 186 |
+
$transformed_data['subscriber_id'] = (!empty($data[0])) ? $data[0] : false;
|
| 187 |
+
$transformed_data['subscriber_token'] = (!empty($data[1])) ? $data[1] : false;
|
| 188 |
+
$transformed_data['queue_id'] = (!empty($data[2])) ? $data[2] : false;
|
| 189 |
+
$transformed_data['link_hash'] = (!empty($data[3])) ? $data[3] : false;
|
| 190 |
+
$transformed_data['preview'] = (!empty($data[4])) ? $data[4] : false;
|
| 191 |
+
return $transformed_data;
|
| 192 |
+
}
|
| 193 |
}
|
lib/Newsletter/Renderer/PostProcess/OpenTracking.php
CHANGED
|
@@ -21,7 +21,7 @@ class OpenTracking {
|
|
| 21 |
}
|
| 22 |
|
| 23 |
static function addTrackingImage() {
|
| 24 |
-
add_filter(Renderer::
|
| 25 |
return OpenTracking::process($template);
|
| 26 |
});
|
| 27 |
return true;
|
| 21 |
}
|
| 22 |
|
| 23 |
static function addTrackingImage() {
|
| 24 |
+
add_filter(Renderer::FILTER_POST_PROCESS, function ($template) {
|
| 25 |
return OpenTracking::process($template);
|
| 26 |
});
|
| 27 |
return true;
|
lib/Newsletter/Renderer/Renderer.php
CHANGED
|
@@ -11,7 +11,7 @@ class Renderer {
|
|
| 11 |
public $newsletter;
|
| 12 |
public $preview;
|
| 13 |
const NEWSLETTER_TEMPLATE = 'Template.html';
|
| 14 |
-
const
|
| 15 |
|
| 16 |
function __construct($newsletter, $preview = false) {
|
| 17 |
// TODO: remove ternary condition, refactor to use model objects
|
|
@@ -24,7 +24,7 @@ class Renderer {
|
|
| 24 |
$this->template = file_get_contents(dirname(__FILE__) . '/' . self::NEWSLETTER_TEMPLATE);
|
| 25 |
}
|
| 26 |
|
| 27 |
-
function render() {
|
| 28 |
$newsletter = $this->newsletter;
|
| 29 |
$body = (is_array($newsletter['body']))
|
| 30 |
? $newsletter['body']
|
|
@@ -48,10 +48,14 @@ class Renderer {
|
|
| 48 |
$template = $this->inlineCSSStyles($template);
|
| 49 |
$template = $this->postProcessTemplate($template);
|
| 50 |
|
| 51 |
-
|
| 52 |
'html' => $template,
|
| 53 |
'text' => $this->renderTextVersion($template)
|
| 54 |
);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
}
|
| 56 |
|
| 57 |
function renderBody($content) {
|
|
@@ -124,7 +128,7 @@ class Renderer {
|
|
| 124 |
str_replace('&', '&', $template->html())
|
| 125 |
);
|
| 126 |
$template = apply_filters(
|
| 127 |
-
self::
|
| 128 |
$DOM->__toString()
|
| 129 |
);
|
| 130 |
return $template;
|
| 11 |
public $newsletter;
|
| 12 |
public $preview;
|
| 13 |
const NEWSLETTER_TEMPLATE = 'Template.html';
|
| 14 |
+
const FILTER_POST_PROCESS = 'mailpoet_rendering_post_process';
|
| 15 |
|
| 16 |
function __construct($newsletter, $preview = false) {
|
| 17 |
// TODO: remove ternary condition, refactor to use model objects
|
| 24 |
$this->template = file_get_contents(dirname(__FILE__) . '/' . self::NEWSLETTER_TEMPLATE);
|
| 25 |
}
|
| 26 |
|
| 27 |
+
function render($type = false) {
|
| 28 |
$newsletter = $this->newsletter;
|
| 29 |
$body = (is_array($newsletter['body']))
|
| 30 |
? $newsletter['body']
|
| 48 |
$template = $this->inlineCSSStyles($template);
|
| 49 |
$template = $this->postProcessTemplate($template);
|
| 50 |
|
| 51 |
+
$rendered_newsletter = array(
|
| 52 |
'html' => $template,
|
| 53 |
'text' => $this->renderTextVersion($template)
|
| 54 |
);
|
| 55 |
+
|
| 56 |
+
return ($type && !empty($rendered_newsletter[$type])) ?
|
| 57 |
+
$rendered_newsletter[$type] :
|
| 58 |
+
$rendered_newsletter;
|
| 59 |
}
|
| 60 |
|
| 61 |
function renderBody($content) {
|
| 128 |
str_replace('&', '&', $template->html())
|
| 129 |
);
|
| 130 |
$template = apply_filters(
|
| 131 |
+
self::FILTER_POST_PROCESS,
|
| 132 |
$DOM->__toString()
|
| 133 |
);
|
| 134 |
return $template;
|
lib/Newsletter/Shortcodes/Categories/Link.php
CHANGED
|
@@ -7,19 +7,23 @@ use MailPoet\Statistics\Track\Unsubscribes;
|
|
| 7 |
use MailPoet\Subscription\Url as SubscriptionUrl;
|
| 8 |
|
| 9 |
class Link {
|
| 10 |
-
static function process(
|
|
|
|
| 11 |
$default_value,
|
| 12 |
$newsletter,
|
| 13 |
$subscriber,
|
| 14 |
-
$queue
|
|
|
|
|
|
|
| 15 |
) {
|
| 16 |
switch($action) {
|
| 17 |
case 'subscription_unsubscribe':
|
| 18 |
$action = 'subscription_unsubscribe_url';
|
| 19 |
$url = self::processUrl(
|
| 20 |
$action,
|
| 21 |
-
|
| 22 |
-
$queue
|
|
|
|
| 23 |
);
|
| 24 |
return sprintf(
|
| 25 |
'<a target="_blank" href="%s">%s</a>',
|
|
@@ -31,14 +35,16 @@ class Link {
|
|
| 31 |
return self::processUrl(
|
| 32 |
$action,
|
| 33 |
SubscriptionUrl::getUnsubscribeUrl($subscriber),
|
| 34 |
-
$queue
|
|
|
|
| 35 |
);
|
| 36 |
|
| 37 |
case 'subscription_manage':
|
| 38 |
$url = self::processUrl(
|
| 39 |
$action = 'subscription_manage_url',
|
| 40 |
-
|
| 41 |
-
$queue
|
|
|
|
| 42 |
);
|
| 43 |
return sprintf(
|
| 44 |
'<a target="_blank" href="%s">%s</a>',
|
|
@@ -50,13 +56,20 @@ class Link {
|
|
| 50 |
return self::processUrl(
|
| 51 |
$action,
|
| 52 |
SubscriptionUrl::getManageUrl($subscriber),
|
| 53 |
-
$queue
|
|
|
|
| 54 |
);
|
| 55 |
|
| 56 |
case 'newsletter_view_in_browser':
|
| 57 |
$action = 'newsletter_view_in_browser_url';
|
| 58 |
-
$url =
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
return sprintf(
|
| 61 |
'<a target="_blank" href="%s">%s</a>',
|
| 62 |
$url,
|
|
@@ -64,8 +77,14 @@ class Link {
|
|
| 64 |
);
|
| 65 |
|
| 66 |
case 'newsletter_view_in_browser_url':
|
| 67 |
-
$url = NewsletterUrl::getViewInBrowserUrl(
|
| 68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
|
| 70 |
default:
|
| 71 |
$shortcode = self::getShortcode($action);
|
|
@@ -74,15 +93,17 @@ class Link {
|
|
| 74 |
$shortcode,
|
| 75 |
$newsletter,
|
| 76 |
$subscriber,
|
| 77 |
-
$queue
|
|
|
|
| 78 |
);
|
| 79 |
return ($url !== $shortcode) ?
|
| 80 |
-
self::processUrl($action, $url, $queue) :
|
| 81 |
false;
|
| 82 |
}
|
| 83 |
}
|
| 84 |
|
| 85 |
-
static function processUrl($action, $url, $queue) {
|
|
|
|
| 86 |
return ($queue !== false && (boolean)Setting::getValue('tracking.enabled')) ?
|
| 87 |
self::getShortcode($action) :
|
| 88 |
$url;
|
|
@@ -104,7 +125,12 @@ class Link {
|
|
| 104 |
$url = SubscriptionUrl::getManageUrl($subscriber);
|
| 105 |
break;
|
| 106 |
case 'newsletter_view_in_browser_url':
|
| 107 |
-
$url = NewsletterUrl::getViewInBrowserUrl(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
break;
|
| 109 |
default:
|
| 110 |
$shortcode = self::getShortcode($shortcode_action);
|
| 7 |
use MailPoet\Subscription\Url as SubscriptionUrl;
|
| 8 |
|
| 9 |
class Link {
|
| 10 |
+
static function process(
|
| 11 |
+
$action,
|
| 12 |
$default_value,
|
| 13 |
$newsletter,
|
| 14 |
$subscriber,
|
| 15 |
+
$queue,
|
| 16 |
+
$content,
|
| 17 |
+
$wp_user_preview
|
| 18 |
) {
|
| 19 |
switch($action) {
|
| 20 |
case 'subscription_unsubscribe':
|
| 21 |
$action = 'subscription_unsubscribe_url';
|
| 22 |
$url = self::processUrl(
|
| 23 |
$action,
|
| 24 |
+
SubscriptionUrl::getUnsubscribeUrl($subscriber),
|
| 25 |
+
$queue,
|
| 26 |
+
$wp_user_preview
|
| 27 |
);
|
| 28 |
return sprintf(
|
| 29 |
'<a target="_blank" href="%s">%s</a>',
|
| 35 |
return self::processUrl(
|
| 36 |
$action,
|
| 37 |
SubscriptionUrl::getUnsubscribeUrl($subscriber),
|
| 38 |
+
$queue,
|
| 39 |
+
$wp_user_preview
|
| 40 |
);
|
| 41 |
|
| 42 |
case 'subscription_manage':
|
| 43 |
$url = self::processUrl(
|
| 44 |
$action = 'subscription_manage_url',
|
| 45 |
+
SubscriptionUrl::getManageUrl($subscriber),
|
| 46 |
+
$queue,
|
| 47 |
+
$wp_user_preview
|
| 48 |
);
|
| 49 |
return sprintf(
|
| 50 |
'<a target="_blank" href="%s">%s</a>',
|
| 56 |
return self::processUrl(
|
| 57 |
$action,
|
| 58 |
SubscriptionUrl::getManageUrl($subscriber),
|
| 59 |
+
$queue,
|
| 60 |
+
$wp_user_preview
|
| 61 |
);
|
| 62 |
|
| 63 |
case 'newsletter_view_in_browser':
|
| 64 |
$action = 'newsletter_view_in_browser_url';
|
| 65 |
+
$url = NewsletterUrl::getViewInBrowserUrl(
|
| 66 |
+
$type = null,
|
| 67 |
+
$newsletter,
|
| 68 |
+
$subscriber,
|
| 69 |
+
$queue,
|
| 70 |
+
$wp_user_preview
|
| 71 |
+
);
|
| 72 |
+
$url = self::processUrl($action, $url, $queue, $wp_user_preview);
|
| 73 |
return sprintf(
|
| 74 |
'<a target="_blank" href="%s">%s</a>',
|
| 75 |
$url,
|
| 77 |
);
|
| 78 |
|
| 79 |
case 'newsletter_view_in_browser_url':
|
| 80 |
+
$url = NewsletterUrl::getViewInBrowserUrl(
|
| 81 |
+
$type = null,
|
| 82 |
+
$newsletter,
|
| 83 |
+
$subscriber,
|
| 84 |
+
$queue,
|
| 85 |
+
$wp_user_preview
|
| 86 |
+
);
|
| 87 |
+
return self::processUrl($action, $url, $queue, $wp_user_preview);
|
| 88 |
|
| 89 |
default:
|
| 90 |
$shortcode = self::getShortcode($action);
|
| 93 |
$shortcode,
|
| 94 |
$newsletter,
|
| 95 |
$subscriber,
|
| 96 |
+
$queue,
|
| 97 |
+
$wp_user_preview
|
| 98 |
);
|
| 99 |
return ($url !== $shortcode) ?
|
| 100 |
+
self::processUrl($action, $url, $queue, $wp_user_preview) :
|
| 101 |
false;
|
| 102 |
}
|
| 103 |
}
|
| 104 |
|
| 105 |
+
static function processUrl($action, $url, $queue, $wp_user_preview = false) {
|
| 106 |
+
if($wp_user_preview) return '#';
|
| 107 |
return ($queue !== false && (boolean)Setting::getValue('tracking.enabled')) ?
|
| 108 |
self::getShortcode($action) :
|
| 109 |
$url;
|
| 125 |
$url = SubscriptionUrl::getManageUrl($subscriber);
|
| 126 |
break;
|
| 127 |
case 'newsletter_view_in_browser_url':
|
| 128 |
+
$url = NewsletterUrl::getViewInBrowserUrl(
|
| 129 |
+
$type = null,
|
| 130 |
+
$newsletter,
|
| 131 |
+
$subscriber,
|
| 132 |
+
$queue
|
| 133 |
+
);
|
| 134 |
break;
|
| 135 |
default:
|
| 136 |
$shortcode = self::getShortcode($shortcode_action);
|
lib/Newsletter/Shortcodes/Shortcodes.php
CHANGED
|
@@ -5,16 +5,19 @@ class Shortcodes {
|
|
| 5 |
public $newsletter;
|
| 6 |
public $subscriber;
|
| 7 |
public $queue;
|
|
|
|
| 8 |
const SHORTCODE_CATEGORY_NAMESPACE = 'MailPoet\Newsletter\Shortcodes\Categories\\';
|
| 9 |
|
| 10 |
function __construct(
|
| 11 |
$newsletter = false,
|
| 12 |
$subscriber = false,
|
| 13 |
-
$queue = false
|
|
|
|
| 14 |
) {
|
| 15 |
$this->newsletter = $newsletter;
|
| 16 |
$this->subscriber = $subscriber;
|
| 17 |
$this->queue = $queue;
|
|
|
|
| 18 |
}
|
| 19 |
|
| 20 |
function extract($content, $categories = false) {
|
|
@@ -64,7 +67,8 @@ class Shortcodes {
|
|
| 64 |
$_this->newsletter,
|
| 65 |
$_this->subscriber,
|
| 66 |
$_this->queue,
|
| 67 |
-
$content
|
|
|
|
| 68 |
);
|
| 69 |
return ($custom_shortcode === $shortcode) ?
|
| 70 |
false :
|
|
@@ -76,7 +80,8 @@ class Shortcodes {
|
|
| 76 |
$_this->newsletter,
|
| 77 |
$_this->subscriber,
|
| 78 |
$_this->queue,
|
| 79 |
-
$content
|
|
|
|
| 80 |
);
|
| 81 |
}, $shortcodes);
|
| 82 |
return $processed_shortcodes;
|
| 5 |
public $newsletter;
|
| 6 |
public $subscriber;
|
| 7 |
public $queue;
|
| 8 |
+
public $wp_user_preview;
|
| 9 |
const SHORTCODE_CATEGORY_NAMESPACE = 'MailPoet\Newsletter\Shortcodes\Categories\\';
|
| 10 |
|
| 11 |
function __construct(
|
| 12 |
$newsletter = false,
|
| 13 |
$subscriber = false,
|
| 14 |
+
$queue = false,
|
| 15 |
+
$wp_user_preview = false
|
| 16 |
) {
|
| 17 |
$this->newsletter = $newsletter;
|
| 18 |
$this->subscriber = $subscriber;
|
| 19 |
$this->queue = $queue;
|
| 20 |
+
$this->wp_user_preview = $wp_user_preview;
|
| 21 |
}
|
| 22 |
|
| 23 |
function extract($content, $categories = false) {
|
| 67 |
$_this->newsletter,
|
| 68 |
$_this->subscriber,
|
| 69 |
$_this->queue,
|
| 70 |
+
$content,
|
| 71 |
+
$_this->wp_user_preview
|
| 72 |
);
|
| 73 |
return ($custom_shortcode === $shortcode) ?
|
| 74 |
false :
|
| 80 |
$_this->newsletter,
|
| 81 |
$_this->subscriber,
|
| 82 |
$_this->queue,
|
| 83 |
+
$content,
|
| 84 |
+
$_this->wp_user_preview
|
| 85 |
);
|
| 86 |
}, $shortcodes);
|
| 87 |
return $processed_shortcodes;
|
lib/Newsletter/Url.php
CHANGED
|
@@ -1,52 +1,80 @@
|
|
| 1 |
<?php
|
| 2 |
namespace MailPoet\Newsletter;
|
| 3 |
|
| 4 |
-
use MailPoet\Models\SendingQueue;
|
| 5 |
use MailPoet\Router\Router;
|
| 6 |
use MailPoet\Router\Endpoints\ViewInBrowser as ViewInBrowserEndpoint;
|
| 7 |
-
use MailPoet\Models\
|
|
|
|
| 8 |
|
| 9 |
class Url {
|
|
|
|
|
|
|
|
|
|
| 10 |
static function getViewInBrowserUrl(
|
| 11 |
-
$
|
|
|
|
| 12 |
$subscriber = false,
|
| 13 |
$queue = false,
|
| 14 |
$preview = false
|
| 15 |
) {
|
| 16 |
-
if(
|
| 17 |
-
$
|
| 18 |
-
}
|
| 19 |
-
if(is_object($subscriber)) {
|
| 20 |
-
$subscriber = $subscriber->asArray();
|
| 21 |
-
} else if(!$subscriber) {
|
| 22 |
-
$subscriber = Subscriber::getCurrentWPUser();
|
| 23 |
-
$subscriber = ($subscriber) ? $subscriber->asArray() : false;
|
| 24 |
}
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
}
|
| 31 |
-
$data =
|
| 32 |
-
'newsletter_id' => (!empty($newsletter['id'])) ?
|
| 33 |
-
$newsletter['id'] :
|
| 34 |
-
$newsletter,
|
| 35 |
-
'subscriber_id' => (!empty($subscriber['id'])) ?
|
| 36 |
-
$subscriber['id'] :
|
| 37 |
-
$subscriber,
|
| 38 |
-
'subscriber_token' => (!empty($subscriber['id'])) ?
|
| 39 |
-
Subscriber::generateToken($subscriber['email']) :
|
| 40 |
-
false,
|
| 41 |
-
'queue_id' => (!empty($queue['id'])) ?
|
| 42 |
-
$queue['id'] :
|
| 43 |
-
$queue,
|
| 44 |
-
'preview' => $preview
|
| 45 |
-
);
|
| 46 |
return Router::buildRequest(
|
| 47 |
ViewInBrowserEndpoint::ENDPOINT,
|
| 48 |
ViewInBrowserEndpoint::ACTION_VIEW,
|
| 49 |
$data
|
| 50 |
);
|
| 51 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
}
|
| 1 |
<?php
|
| 2 |
namespace MailPoet\Newsletter;
|
| 3 |
|
|
|
|
| 4 |
use MailPoet\Router\Router;
|
| 5 |
use MailPoet\Router\Endpoints\ViewInBrowser as ViewInBrowserEndpoint;
|
| 6 |
+
use MailPoet\Models\Newsletter as NewsletterModel;
|
| 7 |
+
use MailPoet\Models\Subscriber as SubscriberModel;
|
| 8 |
|
| 9 |
class Url {
|
| 10 |
+
const TYPE_ARCHIVE = 'display_archive';
|
| 11 |
+
const TYPE_LISTING_EDITOR = 'display_listing_editor';
|
| 12 |
+
|
| 13 |
static function getViewInBrowserUrl(
|
| 14 |
+
$type,
|
| 15 |
+
NewsletterModel $newsletter,
|
| 16 |
$subscriber = false,
|
| 17 |
$queue = false,
|
| 18 |
$preview = false
|
| 19 |
) {
|
| 20 |
+
if($subscriber instanceof SubscriberModel) {
|
| 21 |
+
$subscriber->token = SubscriberModel::generateToken($subscriber->email);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
}
|
| 23 |
+
switch($type) {
|
| 24 |
+
case self::TYPE_ARCHIVE:
|
| 25 |
+
// do not expose newsletter id when displaying archive newsletters
|
| 26 |
+
$newsletter->id = null;
|
| 27 |
+
$preview = true;
|
| 28 |
+
break;
|
| 29 |
+
case self::TYPE_LISTING_EDITOR:
|
| 30 |
+
// enable preview and hide newsletter hash when displaying from editor or listings
|
| 31 |
+
$newsletter->hash = null;
|
| 32 |
+
$preview = true;
|
| 33 |
+
break;
|
| 34 |
+
default:
|
| 35 |
+
// hide hash for all other display types
|
| 36 |
+
$newsletter->hash = null;
|
| 37 |
+
break;
|
| 38 |
}
|
| 39 |
+
$data = self::createUrlDataObject($newsletter, $subscriber, $queue, $preview);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
return Router::buildRequest(
|
| 41 |
ViewInBrowserEndpoint::ENDPOINT,
|
| 42 |
ViewInBrowserEndpoint::ACTION_VIEW,
|
| 43 |
$data
|
| 44 |
);
|
| 45 |
}
|
| 46 |
+
|
| 47 |
+
static function createUrlDataObject($newsletter, $subscriber, $queue, $preview) {
|
| 48 |
+
return array(
|
| 49 |
+
(!empty($newsletter->id)) ?
|
| 50 |
+
(int)$newsletter->id :
|
| 51 |
+
0,
|
| 52 |
+
(!empty($newsletter->hash)) ?
|
| 53 |
+
$newsletter->hash :
|
| 54 |
+
0,
|
| 55 |
+
(!empty($subscriber->id)) ?
|
| 56 |
+
(int)$subscriber->id :
|
| 57 |
+
0,
|
| 58 |
+
(!empty($subscriber->token)) ?
|
| 59 |
+
$subscriber->token :
|
| 60 |
+
0,
|
| 61 |
+
(!empty($queue->id)) ?
|
| 62 |
+
(int)$queue->id :
|
| 63 |
+
0,
|
| 64 |
+
(int)$preview
|
| 65 |
+
);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
static function transformUrlDataObject($data) {
|
| 69 |
+
reset($data);
|
| 70 |
+
if (!is_int(key($data))) return $data;
|
| 71 |
+
$transformed_data = array();
|
| 72 |
+
$transformed_data['newsletter_id'] = (!empty($data[0])) ? $data[0] : false;
|
| 73 |
+
$transformed_data['newsletter_hash'] = (!empty($data[1])) ? $data[1] : false;
|
| 74 |
+
$transformed_data['subscriber_id'] = (!empty($data[2])) ? $data[2] : false;
|
| 75 |
+
$transformed_data['subscriber_token'] = (!empty($data[3])) ? $data[3] : false;
|
| 76 |
+
$transformed_data['queue_id'] = (!empty($data[4])) ? $data[4] : false;
|
| 77 |
+
$transformed_data['preview'] = (!empty($data[5])) ? $data[5] : false;
|
| 78 |
+
return $transformed_data;
|
| 79 |
+
}
|
| 80 |
}
|
lib/Newsletter/ViewInBrowser.php
CHANGED
|
@@ -2,13 +2,17 @@
|
|
| 2 |
namespace MailPoet\Newsletter;
|
| 3 |
|
| 4 |
use MailPoet\Models\Setting;
|
|
|
|
| 5 |
use MailPoet\Newsletter\Links\Links;
|
| 6 |
use MailPoet\Newsletter\Renderer\Renderer;
|
| 7 |
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
| 8 |
|
| 9 |
class ViewInBrowser {
|
| 10 |
function view($data) {
|
| 11 |
-
$wp_user_preview = (
|
|
|
|
|
|
|
|
|
|
| 12 |
return $this->renderNewsletter(
|
| 13 |
$data->newsletter,
|
| 14 |
$data->subscriber,
|
|
@@ -18,25 +22,36 @@ class ViewInBrowser {
|
|
| 18 |
}
|
| 19 |
|
| 20 |
function renderNewsletter($newsletter, $subscriber, $queue, $wp_user_preview) {
|
| 21 |
-
if($queue && $queue->
|
| 22 |
-
$newsletter_body = $queue->getNewsletterRenderedBody();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
} else {
|
| 24 |
$renderer = new Renderer($newsletter, $wp_user_preview);
|
| 25 |
-
$newsletter_body = $renderer->render();
|
| 26 |
}
|
| 27 |
$shortcodes = new Shortcodes(
|
| 28 |
$newsletter,
|
| 29 |
$subscriber,
|
| 30 |
-
$queue
|
| 31 |
-
|
| 32 |
);
|
| 33 |
-
$rendered_newsletter = $shortcodes->replace($newsletter_body
|
| 34 |
-
if($queue && (boolean)Setting::getValue('tracking.enabled')) {
|
| 35 |
$rendered_newsletter = Links::replaceSubscriberData(
|
| 36 |
$subscriber->id,
|
| 37 |
$queue->id,
|
| 38 |
-
$rendered_newsletter
|
| 39 |
-
$wp_user_preview
|
| 40 |
);
|
| 41 |
}
|
| 42 |
return $rendered_newsletter;
|
| 2 |
namespace MailPoet\Newsletter;
|
| 3 |
|
| 4 |
use MailPoet\Models\Setting;
|
| 5 |
+
use MailPoet\Models\Subscriber;
|
| 6 |
use MailPoet\Newsletter\Links\Links;
|
| 7 |
use MailPoet\Newsletter\Renderer\Renderer;
|
| 8 |
use MailPoet\Newsletter\Shortcodes\Shortcodes;
|
| 9 |
|
| 10 |
class ViewInBrowser {
|
| 11 |
function view($data) {
|
| 12 |
+
$wp_user_preview = (
|
| 13 |
+
($data->subscriber && $data->subscriber->isWPUser() && $data->preview) ||
|
| 14 |
+
($data->preview && $data->newsletter_hash)
|
| 15 |
+
);
|
| 16 |
return $this->renderNewsletter(
|
| 17 |
$data->newsletter,
|
| 18 |
$data->subscriber,
|
| 22 |
}
|
| 23 |
|
| 24 |
function renderNewsletter($newsletter, $subscriber, $queue, $wp_user_preview) {
|
| 25 |
+
if($queue && $queue->getNewsletterRenderedBody()) {
|
| 26 |
+
$newsletter_body = $queue->getNewsletterRenderedBody('html');
|
| 27 |
+
// rendered newsletter body has shortcodes converted to links; we need to
|
| 28 |
+
// isolate "view in browser", "unsubscribe" and "manage subscription" links
|
| 29 |
+
// and convert them to shortcodes, which later will be replaced with "#" when
|
| 30 |
+
// newsletter is previewed
|
| 31 |
+
if($wp_user_preview && preg_match(Links::getLinkRegex(), $newsletter_body)) {
|
| 32 |
+
$newsletter_body = Links::convertHashedLinksToShortcodesAndUrls(
|
| 33 |
+
$newsletter_body,
|
| 34 |
+
$convert_all = true
|
| 35 |
+
);
|
| 36 |
+
// remove open tracking link
|
| 37 |
+
$newsletter_body = str_replace(Links::DATA_TAG_OPEN, '', $newsletter_body);
|
| 38 |
+
}
|
| 39 |
} else {
|
| 40 |
$renderer = new Renderer($newsletter, $wp_user_preview);
|
| 41 |
+
$newsletter_body = $renderer->render('html');
|
| 42 |
}
|
| 43 |
$shortcodes = new Shortcodes(
|
| 44 |
$newsletter,
|
| 45 |
$subscriber,
|
| 46 |
+
$queue,
|
| 47 |
+
$wp_user_preview
|
| 48 |
);
|
| 49 |
+
$rendered_newsletter = $shortcodes->replace($newsletter_body);
|
| 50 |
+
if(!$wp_user_preview && $queue && $subscriber && (boolean)Setting::getValue('tracking.enabled')) {
|
| 51 |
$rendered_newsletter = Links::replaceSubscriberData(
|
| 52 |
$subscriber->id,
|
| 53 |
$queue->id,
|
| 54 |
+
$rendered_newsletter
|
|
|
|
| 55 |
);
|
| 56 |
}
|
| 57 |
return $rendered_newsletter;
|
lib/Router/Endpoints/Track.php
CHANGED
|
@@ -5,6 +5,7 @@ use MailPoet\Models\Newsletter;
|
|
| 5 |
use MailPoet\Models\NewsletterLink;
|
| 6 |
use MailPoet\Models\SendingQueue;
|
| 7 |
use MailPoet\Models\Subscriber;
|
|
|
|
| 8 |
use MailPoet\Statistics\Track\Clicks;
|
| 9 |
use MailPoet\Statistics\Track\Opens;
|
| 10 |
|
|
@@ -35,10 +36,10 @@ class Track {
|
|
| 35 |
}
|
| 36 |
|
| 37 |
function _processTrackData($data) {
|
| 38 |
-
$data = (object)$data;
|
| 39 |
if(empty($data->queue_id) ||
|
| 40 |
-
|
| 41 |
-
|
| 42 |
) {
|
| 43 |
return false;
|
| 44 |
}
|
| 5 |
use MailPoet\Models\NewsletterLink;
|
| 6 |
use MailPoet\Models\SendingQueue;
|
| 7 |
use MailPoet\Models\Subscriber;
|
| 8 |
+
use MailPoet\Newsletter\Links\Links;
|
| 9 |
use MailPoet\Statistics\Track\Clicks;
|
| 10 |
use MailPoet\Statistics\Track\Opens;
|
| 11 |
|
| 36 |
}
|
| 37 |
|
| 38 |
function _processTrackData($data) {
|
| 39 |
+
$data = (object)Links::transformUrlDataObject($data);
|
| 40 |
if(empty($data->queue_id) ||
|
| 41 |
+
empty($data->subscriber_id) ||
|
| 42 |
+
empty($data->subscriber_token)
|
| 43 |
) {
|
| 44 |
return false;
|
| 45 |
}
|
lib/Router/Endpoints/ViewInBrowser.php
CHANGED
|
@@ -1,13 +1,17 @@
|
|
| 1 |
<?php
|
| 2 |
namespace MailPoet\Router\Endpoints;
|
| 3 |
|
|
|
|
| 4 |
use MailPoet\Models\Newsletter;
|
| 5 |
use MailPoet\Models\SendingQueue;
|
| 6 |
use MailPoet\Models\Subscriber;
|
|
|
|
| 7 |
use MailPoet\Newsletter\ViewInBrowser as NewsletterViewInBrowser;
|
| 8 |
|
| 9 |
if(!defined('ABSPATH')) exit;
|
| 10 |
|
|
|
|
|
|
|
| 11 |
class ViewInBrowser {
|
| 12 |
const ENDPOINT = 'view_in_browser';
|
| 13 |
const ACTION_VIEW = 'view';
|
|
@@ -24,37 +28,52 @@ class ViewInBrowser {
|
|
| 24 |
}
|
| 25 |
|
| 26 |
function _processBrowserPreviewData($data) {
|
| 27 |
-
$data = (object)$data;
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
empty($data->newsletter_id)
|
| 31 |
-
) {
|
| 32 |
$this->_abort();
|
| 33 |
-
} else {
|
| 34 |
-
$data->newsletter = Newsletter::findOne($data->newsletter_id);
|
| 35 |
-
$data->subscriber = Subscriber::findOne($data->subscriber_id);
|
| 36 |
-
$data->queue = ($data->queue_id) ?
|
| 37 |
-
SendingQueue::findOne($data->queue_id) :
|
| 38 |
-
false;
|
| 39 |
-
return ($this->_validateBrowserPreviewData($data)) ?
|
| 40 |
-
$data :
|
| 41 |
-
$this->_abort();
|
| 42 |
-
}
|
| 43 |
}
|
| 44 |
|
| 45 |
function _validateBrowserPreviewData($data) {
|
| 46 |
-
|
| 47 |
-
$
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
if(
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
return $data;
|
| 59 |
}
|
| 60 |
|
| 1 |
<?php
|
| 2 |
namespace MailPoet\Router\Endpoints;
|
| 3 |
|
| 4 |
+
use MailPoet\Config\Env;
|
| 5 |
use MailPoet\Models\Newsletter;
|
| 6 |
use MailPoet\Models\SendingQueue;
|
| 7 |
use MailPoet\Models\Subscriber;
|
| 8 |
+
use MailPoet\Newsletter\Url as NewsletterUrl;
|
| 9 |
use MailPoet\Newsletter\ViewInBrowser as NewsletterViewInBrowser;
|
| 10 |
|
| 11 |
if(!defined('ABSPATH')) exit;
|
| 12 |
|
| 13 |
+
require_once(ABSPATH . 'wp-includes/pluggable.php');
|
| 14 |
+
|
| 15 |
class ViewInBrowser {
|
| 16 |
const ENDPOINT = 'view_in_browser';
|
| 17 |
const ACTION_VIEW = 'view';
|
| 28 |
}
|
| 29 |
|
| 30 |
function _processBrowserPreviewData($data) {
|
| 31 |
+
$data = (object)NewsletterUrl::transformUrlDataObject($data);
|
| 32 |
+
return ($this->_validateBrowserPreviewData($data)) ?
|
| 33 |
+
$data :
|
|
|
|
|
|
|
| 34 |
$this->_abort();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
}
|
| 36 |
|
| 37 |
function _validateBrowserPreviewData($data) {
|
| 38 |
+
// either newsletter ID or hash must be defined, and newsletter must exist
|
| 39 |
+
if(empty($data->newsletter_id) && empty($data->newsletter_hash)) return false;
|
| 40 |
+
$data->newsletter = (!empty($data->newsletter_hash)) ?
|
| 41 |
+
Newsletter::getByHash($data->newsletter_hash) :
|
| 42 |
+
Newsletter::findOne($data->newsletter_id);
|
| 43 |
+
if(!$data->newsletter) return false;
|
| 44 |
+
|
| 45 |
+
// subscriber is optional; if exists, token must validate
|
| 46 |
+
$data->subscriber = (!empty($data->subscriber_id)) ?
|
| 47 |
+
Subscriber::findOne($data->subscriber_id) :
|
| 48 |
+
false;
|
| 49 |
+
if($data->subscriber) {
|
| 50 |
+
if(empty($data->subscriber_token) ||
|
| 51 |
+
!Subscriber::verifyToken($data->subscriber->email, $data->subscriber_token)
|
| 52 |
+
) return false;
|
| 53 |
}
|
| 54 |
+
|
| 55 |
+
// if newsletter ID is defined then subscriber must exist
|
| 56 |
+
if($data->newsletter_id && !$data->subscriber) return false;
|
| 57 |
+
|
| 58 |
+
// queue is optional; if defined, get it
|
| 59 |
+
$data->queue = (!empty($data->queue_id)) ?
|
| 60 |
+
SendingQueue::findOne($data->queue_id) :
|
| 61 |
+
SendingQueue::where('newsletter_id', $data->newsletter->id)->findOne();
|
| 62 |
+
|
| 63 |
+
// allow users with 'manage_options' permission to preview any newsletter
|
| 64 |
+
if(!empty($data->preview) && current_user_can(Env::$required_permission)
|
| 65 |
+
) return $data;
|
| 66 |
+
|
| 67 |
+
// allow others to preview newsletters only when newsletter hash is defined
|
| 68 |
+
if(!empty($data->preview) && empty($data->newsletter_hash)
|
| 69 |
+
) return false;
|
| 70 |
+
|
| 71 |
+
// if queue and subscriber exist, subscriber must have received the newsletter
|
| 72 |
+
if($data->queue &&
|
| 73 |
+
$data->subscriber &&
|
| 74 |
+
!$data->queue->isSubscriberProcessed($data->subscriber->id)
|
| 75 |
+
) return false;
|
| 76 |
+
|
| 77 |
return $data;
|
| 78 |
}
|
| 79 |
|
lib/Subscription/Pages.php
CHANGED
|
@@ -297,6 +297,10 @@ class Pages {
|
|
| 297 |
),
|
| 298 |
'is_checked' => (
|
| 299 |
$subscriber->status === Subscriber::STATUS_BOUNCED
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
)
|
| 301 |
)
|
| 302 |
)
|
| 297 |
),
|
| 298 |
'is_checked' => (
|
| 299 |
$subscriber->status === Subscriber::STATUS_BOUNCED
|
| 300 |
+
),
|
| 301 |
+
'is_disabled' => true,
|
| 302 |
+
'is_hidden' => (
|
| 303 |
+
$subscriber->status !== Subscriber::STATUS_BOUNCED
|
| 304 |
)
|
| 305 |
)
|
| 306 |
)
|
mailpoet.php
CHANGED
|
@@ -5,7 +5,7 @@ use MailPoet\Config\Initializer;
|
|
| 5 |
|
| 6 |
/*
|
| 7 |
* Plugin Name: MailPoet
|
| 8 |
-
* Version: 3.0.0-beta.
|
| 9 |
* Plugin URI: http://www.mailpoet.com
|
| 10 |
* Description: Create and send beautiful email newsletters, autoresponders, and post notifications without leaving WordPress. This is a beta version of our brand new plugin!
|
| 11 |
* Author: MailPoet
|
|
@@ -24,7 +24,7 @@ use MailPoet\Config\Initializer;
|
|
| 24 |
$mailpoet_loader = dirname(__FILE__) . '/vendor/autoload.php';
|
| 25 |
if(file_exists($mailpoet_loader)) {
|
| 26 |
require $mailpoet_loader;
|
| 27 |
-
define('MAILPOET_VERSION', '3.0.0-beta.
|
| 28 |
$initializer = new Initializer(
|
| 29 |
array(
|
| 30 |
'file' => __FILE__,
|
| 5 |
|
| 6 |
/*
|
| 7 |
* Plugin Name: MailPoet
|
| 8 |
+
* Version: 3.0.0-beta.10
|
| 9 |
* Plugin URI: http://www.mailpoet.com
|
| 10 |
* Description: Create and send beautiful email newsletters, autoresponders, and post notifications without leaving WordPress. This is a beta version of our brand new plugin!
|
| 11 |
* Author: MailPoet
|
| 24 |
$mailpoet_loader = dirname(__FILE__) . '/vendor/autoload.php';
|
| 25 |
if(file_exists($mailpoet_loader)) {
|
| 26 |
require $mailpoet_loader;
|
| 27 |
+
define('MAILPOET_VERSION', '3.0.0-beta.10');
|
| 28 |
$initializer = new Initializer(
|
| 29 |
array(
|
| 30 |
'file' => __FILE__,
|
readme.txt
CHANGED
|
@@ -3,7 +3,7 @@ Contributors: mailpoet, wysija
|
|
| 3 |
Tags: newsletter, email, welcome email, post notification, autoresponder, mailchimp, signup, smtp
|
| 4 |
Requires at least: 4.6
|
| 5 |
Tested up to: 4.7
|
| 6 |
-
Stable tag: 3.0.0-beta.
|
| 7 |
Create and send beautiful emails and newsletters from WordPress.
|
| 8 |
|
| 9 |
== Description ==
|
|
@@ -83,13 +83,22 @@ Our [support site](https://docs.mailpoet.com/) has plenty of articles. You can w
|
|
| 83 |
|
| 84 |
== Changelog ==
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
= 3.0.0-beta.9 - 2016-12-20 =
|
| 87 |
-
* Improved: the plugin is now tested up to WP 4.7
|
| 88 |
-
* Improved: MailPoet's sending service bounce status API update
|
| 89 |
-
* Improved: change duplicate subscribers import message to be more descriptive
|
| 90 |
-
* Fixed: database character set and time zone setup
|
| 91 |
-
* Fixed: alignment of post titles inside notificaiton emails
|
| 92 |
-
* Fixed: partially generated or missing translations from .pot file
|
| 93 |
|
| 94 |
= 3.0.0-beta.8 - 2016-12-13 =
|
| 95 |
* Added: MailPoet's sending service can now sync hard bounced addresses with the plugin to keep your lists tidy and clean;
|
|
@@ -151,12 +160,12 @@ Our [support site](https://docs.mailpoet.com/) has plenty of articles. You can w
|
|
| 151 |
* Fixed newsletter number shortcode for notification newsletters;
|
| 152 |
* Enhanced HelpScout support beacon report with extra support data;
|
| 153 |
* Fixed email renderer to not throw entity warnings on earlier PHP versions;
|
| 154 |
-
* Fixed newsletter preview incompatibility errors for earlier PHP versions
|
| 155 |
|
| 156 |
= 3.0.0-beta.2 - 2016-10 =
|
| 157 |
|
| 158 |
* Fixed compatibility issues with PHP versions earlier than PHP 5.6;
|
| 159 |
-
* Renamed 'Emails' email type to 'Newsletters'
|
| 160 |
|
| 161 |
= 3.0.0-beta.1 - 2016-10 =
|
| 162 |
|
| 3 |
Tags: newsletter, email, welcome email, post notification, autoresponder, mailchimp, signup, smtp
|
| 4 |
Requires at least: 4.6
|
| 5 |
Tested up to: 4.7
|
| 6 |
+
Stable tag: 3.0.0-beta.10
|
| 7 |
Create and send beautiful emails and newsletters from WordPress.
|
| 8 |
|
| 9 |
== Description ==
|
| 83 |
|
| 84 |
== Changelog ==
|
| 85 |
|
| 86 |
+
= 3.0.0-beta.10 - 2016-12-27 =
|
| 87 |
+
* Improved: newsletter is saved prior to sending an email preview;
|
| 88 |
+
* Improved: subscription management page conditionally displays the "bounced" status;
|
| 89 |
+
* Improved: deleted lists are displayed in newsletter listings;
|
| 90 |
+
* Fixed: newsletter/subscriber/list/form dates are properly formatted according to WP settings;
|
| 91 |
+
* Fixed: emails' "Return-path" header is set to the bounce address configured in Settings->Advanced;
|
| 92 |
+
* Fixed: archived newsletters' shortcode works for site visitors;
|
| 93 |
+
* Fixed: unicode support for newsletters.
|
| 94 |
+
|
| 95 |
= 3.0.0-beta.9 - 2016-12-20 =
|
| 96 |
+
* Improved: the plugin is now tested up to WP 4.7;
|
| 97 |
+
* Improved: MailPoet's sending service bounce status API update;
|
| 98 |
+
* Improved: change duplicate subscribers import message to be more descriptive;
|
| 99 |
+
* Fixed: database character set and time zone setup;
|
| 100 |
+
* Fixed: alignment of post titles inside notificaiton emails;
|
| 101 |
+
* Fixed: partially generated or missing translations from .pot file.
|
| 102 |
|
| 103 |
= 3.0.0-beta.8 - 2016-12-13 =
|
| 104 |
* Added: MailPoet's sending service can now sync hard bounced addresses with the plugin to keep your lists tidy and clean;
|
| 160 |
* Fixed newsletter number shortcode for notification newsletters;
|
| 161 |
* Enhanced HelpScout support beacon report with extra support data;
|
| 162 |
* Fixed email renderer to not throw entity warnings on earlier PHP versions;
|
| 163 |
+
* Fixed newsletter preview incompatibility errors for earlier PHP versions.
|
| 164 |
|
| 165 |
= 3.0.0-beta.2 - 2016-10 =
|
| 166 |
|
| 167 |
* Fixed compatibility issues with PHP versions earlier than PHP 5.6;
|
| 168 |
+
* Renamed 'Emails' email type to 'Newsletters'.
|
| 169 |
|
| 170 |
= 3.0.0-beta.1 - 2016-10 =
|
| 171 |
|
vendor/autoload.php
CHANGED
|
@@ -4,4 +4,4 @@
|
|
| 4 |
|
| 5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
| 6 |
|
| 7 |
-
return
|
| 4 |
|
| 5 |
require_once __DIR__ . '/composer/autoload_real.php';
|
| 6 |
|
| 7 |
+
return ComposerAutoloaderInit9478c93be8e1a8371b13c6b1b2795224::getLoader();
|
vendor/composer/ClassLoader.php
CHANGED
|
@@ -55,6 +55,7 @@ class ClassLoader
|
|
| 55 |
private $classMap = array();
|
| 56 |
private $classMapAuthoritative = false;
|
| 57 |
private $missingClasses = array();
|
|
|
|
| 58 |
|
| 59 |
public function getPrefixes()
|
| 60 |
{
|
|
@@ -271,6 +272,26 @@ class ClassLoader
|
|
| 271 |
return $this->classMapAuthoritative;
|
| 272 |
}
|
| 273 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
/**
|
| 275 |
* Registers this instance as an autoloader.
|
| 276 |
*
|
|
@@ -313,11 +334,6 @@ class ClassLoader
|
|
| 313 |
*/
|
| 314 |
public function findFile($class)
|
| 315 |
{
|
| 316 |
-
// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
|
| 317 |
-
if ('\\' == $class[0]) {
|
| 318 |
-
$class = substr($class, 1);
|
| 319 |
-
}
|
| 320 |
-
|
| 321 |
// class map lookup
|
| 322 |
if (isset($this->classMap[$class])) {
|
| 323 |
return $this->classMap[$class];
|
|
@@ -325,6 +341,12 @@ class ClassLoader
|
|
| 325 |
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
| 326 |
return false;
|
| 327 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
|
| 329 |
$file = $this->findFileWithExtension($class, '.php');
|
| 330 |
|
|
@@ -333,6 +355,10 @@ class ClassLoader
|
|
| 333 |
$file = $this->findFileWithExtension($class, '.hh');
|
| 334 |
}
|
| 335 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
if (false === $file) {
|
| 337 |
// Remember that this class does not exist.
|
| 338 |
$this->missingClasses[$class] = true;
|
| 55 |
private $classMap = array();
|
| 56 |
private $classMapAuthoritative = false;
|
| 57 |
private $missingClasses = array();
|
| 58 |
+
private $apcuPrefix;
|
| 59 |
|
| 60 |
public function getPrefixes()
|
| 61 |
{
|
| 272 |
return $this->classMapAuthoritative;
|
| 273 |
}
|
| 274 |
|
| 275 |
+
/**
|
| 276 |
+
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
| 277 |
+
*
|
| 278 |
+
* @param string|null $apcuPrefix
|
| 279 |
+
*/
|
| 280 |
+
public function setApcuPrefix($apcuPrefix)
|
| 281 |
+
{
|
| 282 |
+
$this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
/**
|
| 286 |
+
* The APCu prefix in use, or null if APCu caching is not enabled.
|
| 287 |
+
*
|
| 288 |
+
* @return string|null
|
| 289 |
+
*/
|
| 290 |
+
public function getApcuPrefix()
|
| 291 |
+
{
|
| 292 |
+
return $this->apcuPrefix;
|
| 293 |
+
}
|
| 294 |
+
|
| 295 |
/**
|
| 296 |
* Registers this instance as an autoloader.
|
| 297 |
*
|
| 334 |
*/
|
| 335 |
public function findFile($class)
|
| 336 |
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 337 |
// class map lookup
|
| 338 |
if (isset($this->classMap[$class])) {
|
| 339 |
return $this->classMap[$class];
|
| 341 |
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
| 342 |
return false;
|
| 343 |
}
|
| 344 |
+
if (null !== $this->apcuPrefix) {
|
| 345 |
+
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
| 346 |
+
if ($hit) {
|
| 347 |
+
return $file;
|
| 348 |
+
}
|
| 349 |
+
}
|
| 350 |
|
| 351 |
$file = $this->findFileWithExtension($class, '.php');
|
| 352 |
|
| 355 |
$file = $this->findFileWithExtension($class, '.hh');
|
| 356 |
}
|
| 357 |
|
| 358 |
+
if (null !== $this->apcuPrefix) {
|
| 359 |
+
apcu_add($this->apcuPrefix.$class, $file);
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
if (false === $file) {
|
| 363 |
// Remember that this class does not exist.
|
| 364 |
$this->missingClasses[$class] = true;
|
vendor/composer/autoload_real.php
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
// autoload_real.php @generated by Composer
|
| 4 |
|
| 5 |
-
class
|
| 6 |
{
|
| 7 |
private static $loader;
|
| 8 |
|
|
@@ -19,15 +19,15 @@ class ComposerAutoloaderInitba2ce543f2a04ae11d74280bc49a2040
|
|
| 19 |
return self::$loader;
|
| 20 |
}
|
| 21 |
|
| 22 |
-
spl_autoload_register(array('
|
| 23 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
| 24 |
-
spl_autoload_unregister(array('
|
| 25 |
|
| 26 |
-
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
|
| 27 |
if ($useStaticLoader) {
|
| 28 |
require_once __DIR__ . '/autoload_static.php';
|
| 29 |
|
| 30 |
-
call_user_func(\Composer\Autoload\
|
| 31 |
} else {
|
| 32 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
| 33 |
foreach ($map as $namespace => $path) {
|
|
@@ -48,19 +48,19 @@ class ComposerAutoloaderInitba2ce543f2a04ae11d74280bc49a2040
|
|
| 48 |
$loader->register(true);
|
| 49 |
|
| 50 |
if ($useStaticLoader) {
|
| 51 |
-
$includeFiles = Composer\Autoload\
|
| 52 |
} else {
|
| 53 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
| 54 |
}
|
| 55 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
| 56 |
-
|
| 57 |
}
|
| 58 |
|
| 59 |
return $loader;
|
| 60 |
}
|
| 61 |
}
|
| 62 |
|
| 63 |
-
function
|
| 64 |
{
|
| 65 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
| 66 |
require $file;
|
| 2 |
|
| 3 |
// autoload_real.php @generated by Composer
|
| 4 |
|
| 5 |
+
class ComposerAutoloaderInit9478c93be8e1a8371b13c6b1b2795224
|
| 6 |
{
|
| 7 |
private static $loader;
|
| 8 |
|
| 19 |
return self::$loader;
|
| 20 |
}
|
| 21 |
|
| 22 |
+
spl_autoload_register(array('ComposerAutoloaderInit9478c93be8e1a8371b13c6b1b2795224', 'loadClassLoader'), true, true);
|
| 23 |
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
| 24 |
+
spl_autoload_unregister(array('ComposerAutoloaderInit9478c93be8e1a8371b13c6b1b2795224', 'loadClassLoader'));
|
| 25 |
|
| 26 |
+
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
| 27 |
if ($useStaticLoader) {
|
| 28 |
require_once __DIR__ . '/autoload_static.php';
|
| 29 |
|
| 30 |
+
call_user_func(\Composer\Autoload\ComposerStaticInit9478c93be8e1a8371b13c6b1b2795224::getInitializer($loader));
|
| 31 |
} else {
|
| 32 |
$map = require __DIR__ . '/autoload_namespaces.php';
|
| 33 |
foreach ($map as $namespace => $path) {
|
| 48 |
$loader->register(true);
|
| 49 |
|
| 50 |
if ($useStaticLoader) {
|
| 51 |
+
$includeFiles = Composer\Autoload\ComposerStaticInit9478c93be8e1a8371b13c6b1b2795224::$files;
|
| 52 |
} else {
|
| 53 |
$includeFiles = require __DIR__ . '/autoload_files.php';
|
| 54 |
}
|
| 55 |
foreach ($includeFiles as $fileIdentifier => $file) {
|
| 56 |
+
composerRequire9478c93be8e1a8371b13c6b1b2795224($fileIdentifier, $file);
|
| 57 |
}
|
| 58 |
|
| 59 |
return $loader;
|
| 60 |
}
|
| 61 |
}
|
| 62 |
|
| 63 |
+
function composerRequire9478c93be8e1a8371b13c6b1b2795224($fileIdentifier, $file)
|
| 64 |
{
|
| 65 |
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
| 66 |
require $file;
|
vendor/composer/autoload_static.php
CHANGED
|
@@ -4,7 +4,7 @@
|
|
| 4 |
|
| 5 |
namespace Composer\Autoload;
|
| 6 |
|
| 7 |
-
class
|
| 8 |
{
|
| 9 |
public static $files = array (
|
| 10 |
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
|
@@ -555,10 +555,10 @@ class ComposerStaticInitba2ce543f2a04ae11d74280bc49a2040
|
|
| 555 |
public static function getInitializer(ClassLoader $loader)
|
| 556 |
{
|
| 557 |
return \Closure::bind(function () use ($loader) {
|
| 558 |
-
$loader->prefixLengthsPsr4 =
|
| 559 |
-
$loader->prefixDirsPsr4 =
|
| 560 |
-
$loader->prefixesPsr0 =
|
| 561 |
-
$loader->classMap =
|
| 562 |
|
| 563 |
}, null, ClassLoader::class);
|
| 564 |
}
|
| 4 |
|
| 5 |
namespace Composer\Autoload;
|
| 6 |
|
| 7 |
+
class ComposerStaticInit9478c93be8e1a8371b13c6b1b2795224
|
| 8 |
{
|
| 9 |
public static $files = array (
|
| 10 |
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
| 555 |
public static function getInitializer(ClassLoader $loader)
|
| 556 |
{
|
| 557 |
return \Closure::bind(function () use ($loader) {
|
| 558 |
+
$loader->prefixLengthsPsr4 = ComposerStaticInit9478c93be8e1a8371b13c6b1b2795224::$prefixLengthsPsr4;
|
| 559 |
+
$loader->prefixDirsPsr4 = ComposerStaticInit9478c93be8e1a8371b13c6b1b2795224::$prefixDirsPsr4;
|
| 560 |
+
$loader->prefixesPsr0 = ComposerStaticInit9478c93be8e1a8371b13c6b1b2795224::$prefixesPsr0;
|
| 561 |
+
$loader->classMap = ComposerStaticInit9478c93be8e1a8371b13c6b1b2795224::$classMap;
|
| 562 |
|
| 563 |
}, null, ClassLoader::class);
|
| 564 |
}
|
vendor/composer/installed.json
CHANGED
|
@@ -14,7 +14,7 @@
|
|
| 14 |
"reference": "b0c1bda3be5a35da44ba1ac28cc61c67d2ada465",
|
| 15 |
"shasum": ""
|
| 16 |
},
|
| 17 |
-
"time": "2015-11-
|
| 18 |
"type": "library",
|
| 19 |
"installation-source": "dist",
|
| 20 |
"autoload": {
|
|
@@ -52,7 +52,7 @@
|
|
| 52 |
"require": {
|
| 53 |
"php": ">=5.2.0"
|
| 54 |
},
|
| 55 |
-
"time": "2014-06-
|
| 56 |
"type": "library",
|
| 57 |
"installation-source": "dist",
|
| 58 |
"autoload": {
|
|
@@ -113,7 +113,7 @@
|
|
| 113 |
"j4mie/idiorm": "1.5.*",
|
| 114 |
"php": ">=5.2.0"
|
| 115 |
},
|
| 116 |
-
"time": "2014-09-
|
| 117 |
"type": "library",
|
| 118 |
"installation-source": "dist",
|
| 119 |
"autoload": {
|
|
@@ -177,7 +177,7 @@
|
|
| 177 |
"require-dev": {
|
| 178 |
"phpunit/phpunit": "~4.0|~5.0"
|
| 179 |
},
|
| 180 |
-
"time": "2016-01-
|
| 181 |
"type": "library",
|
| 182 |
"installation-source": "dist",
|
| 183 |
"autoload": {
|
|
@@ -223,7 +223,7 @@
|
|
| 223 |
"suggest": {
|
| 224 |
"ext-mbstring": "For best performance"
|
| 225 |
},
|
| 226 |
-
"time": "2016-05-
|
| 227 |
"type": "library",
|
| 228 |
"extra": {
|
| 229 |
"branch-alias": {
|
|
@@ -296,7 +296,7 @@
|
|
| 296 |
"symfony/config": "",
|
| 297 |
"symfony/yaml": ""
|
| 298 |
},
|
| 299 |
-
"time": "2016-10-
|
| 300 |
"type": "library",
|
| 301 |
"extra": {
|
| 302 |
"branch-alias": {
|
|
@@ -351,7 +351,7 @@
|
|
| 351 |
"require-dev": {
|
| 352 |
"phpunit/phpunit": "~4.0|~5.0"
|
| 353 |
},
|
| 354 |
-
"time": "2015-11-
|
| 355 |
"type": "library",
|
| 356 |
"installation-source": "dist",
|
| 357 |
"autoload": {
|
|
@@ -402,7 +402,7 @@
|
|
| 402 |
"phpunit/phpunit": ">=4.0",
|
| 403 |
"soundasleep/component-tests": "dev-master"
|
| 404 |
},
|
| 405 |
-
"time": "2016-07-
|
| 406 |
"type": "library",
|
| 407 |
"installation-source": "dist",
|
| 408 |
"autoload": {
|
|
@@ -454,7 +454,7 @@
|
|
| 454 |
"require-dev": {
|
| 455 |
"mockery/mockery": "~0.9.1"
|
| 456 |
},
|
| 457 |
-
"time": "2016-07-
|
| 458 |
"type": "library",
|
| 459 |
"extra": {
|
| 460 |
"branch-alias": {
|
|
@@ -509,7 +509,7 @@
|
|
| 509 |
"require-dev": {
|
| 510 |
"htmlawed/htmlawed": "dev-master"
|
| 511 |
},
|
| 512 |
-
"time": "2016-01-
|
| 513 |
"type": "library",
|
| 514 |
"installation-source": "dist",
|
| 515 |
"autoload": {
|
|
@@ -564,7 +564,7 @@
|
|
| 564 |
"symfony/debug": "~2.7",
|
| 565 |
"symfony/phpunit-bridge": "~2.7"
|
| 566 |
},
|
| 567 |
-
"time": "2016-10-
|
| 568 |
"type": "library",
|
| 569 |
"extra": {
|
| 570 |
"branch-alias": {
|
| 14 |
"reference": "b0c1bda3be5a35da44ba1ac28cc61c67d2ada465",
|
| 15 |
"shasum": ""
|
| 16 |
},
|
| 17 |
+
"time": "2015-11-28T21:47:43+00:00",
|
| 18 |
"type": "library",
|
| 19 |
"installation-source": "dist",
|
| 20 |
"autoload": {
|
| 52 |
"require": {
|
| 53 |
"php": ">=5.2.0"
|
| 54 |
},
|
| 55 |
+
"time": "2014-06-23T13:08:57+00:00",
|
| 56 |
"type": "library",
|
| 57 |
"installation-source": "dist",
|
| 58 |
"autoload": {
|
| 113 |
"j4mie/idiorm": "1.5.*",
|
| 114 |
"php": ">=5.2.0"
|
| 115 |
},
|
| 116 |
+
"time": "2014-09-23T10:49:36+00:00",
|
| 117 |
"type": "library",
|
| 118 |
"installation-source": "dist",
|
| 119 |
"autoload": {
|
| 177 |
"require-dev": {
|
| 178 |
"phpunit/phpunit": "~4.0|~5.0"
|
| 179 |
},
|
| 180 |
+
"time": "2016-01-26T21:23:30+00:00",
|
| 181 |
"type": "library",
|
| 182 |
"installation-source": "dist",
|
| 183 |
"autoload": {
|
| 223 |
"suggest": {
|
| 224 |
"ext-mbstring": "For best performance"
|
| 225 |
},
|
| 226 |
+
"time": "2016-05-18T14:26:46+00:00",
|
| 227 |
"type": "library",
|
| 228 |
"extra": {
|
| 229 |
"branch-alias": {
|
| 296 |
"symfony/config": "",
|
| 297 |
"symfony/yaml": ""
|
| 298 |
},
|
| 299 |
+
"time": "2016-10-18T04:28:30+00:00",
|
| 300 |
"type": "library",
|
| 301 |
"extra": {
|
| 302 |
"branch-alias": {
|
| 351 |
"require-dev": {
|
| 352 |
"phpunit/phpunit": "~4.0|~5.0"
|
| 353 |
},
|
| 354 |
+
"time": "2015-11-04T20:07:17+00:00",
|
| 355 |
"type": "library",
|
| 356 |
"installation-source": "dist",
|
| 357 |
"autoload": {
|
| 402 |
"phpunit/phpunit": ">=4.0",
|
| 403 |
"soundasleep/component-tests": "dev-master"
|
| 404 |
},
|
| 405 |
+
"time": "2016-07-28T01:09:53+00:00",
|
| 406 |
"type": "library",
|
| 407 |
"installation-source": "dist",
|
| 408 |
"autoload": {
|
| 454 |
"require-dev": {
|
| 455 |
"mockery/mockery": "~0.9.1"
|
| 456 |
},
|
| 457 |
+
"time": "2016-07-08T11:51:25+00:00",
|
| 458 |
"type": "library",
|
| 459 |
"extra": {
|
| 460 |
"branch-alias": {
|
| 509 |
"require-dev": {
|
| 510 |
"htmlawed/htmlawed": "dev-master"
|
| 511 |
},
|
| 512 |
+
"time": "2016-01-14T20:55:00+00:00",
|
| 513 |
"type": "library",
|
| 514 |
"installation-source": "dist",
|
| 515 |
"autoload": {
|
| 564 |
"symfony/debug": "~2.7",
|
| 565 |
"symfony/phpunit-bridge": "~2.7"
|
| 566 |
},
|
| 567 |
+
"time": "2016-10-25T19:17:17+00:00",
|
| 568 |
"type": "library",
|
| 569 |
"extra": {
|
| 570 |
"branch-alias": {
|
