Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
817bea9057 |
@@ -3,7 +3,6 @@ import { Step } from '../../../../../editor/components/automation/types';
|
||||
import { storeName } from '../../../../../editor/store';
|
||||
|
||||
const transactionalTriggers = [
|
||||
'mailpoet:custom-trigger',
|
||||
'woocommerce:order-status-changed',
|
||||
'woocommerce:order-created',
|
||||
'woocommerce:order-completed',
|
||||
|
8
mailpoet/assets/js/src/global.d.ts
vendored
8
mailpoet/assets/js/src/global.d.ts
vendored
@@ -39,13 +39,13 @@ interface JQuery {
|
||||
declare module '@woocommerce/blocks-checkout' {
|
||||
type CheckboxControlProps = {
|
||||
className?: string;
|
||||
label?: string | React.ReactNode;
|
||||
label?: string;
|
||||
id?: string;
|
||||
onChange: (value: boolean) => void;
|
||||
children?: React.ReactChildren;
|
||||
instanceId?: string;
|
||||
onChange?: (value: boolean) => void;
|
||||
children?: React.ReactChildren | React.ReactElement;
|
||||
hasError?: boolean;
|
||||
checked?: boolean;
|
||||
disabled?: string | boolean | undefined;
|
||||
};
|
||||
export const CheckboxControl: (props: CheckboxControlProps) => JSX.Element;
|
||||
}
|
||||
|
@@ -1,142 +0,0 @@
|
||||
import { ExternalLink } from '@wordpress/components';
|
||||
import { select, dispatch } from '@wordpress/data';
|
||||
import { store as coreDataStore, useEntityProp } from '@wordpress/core-data';
|
||||
import { store as editorStore } from '@wordpress/editor';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { createInterpolateElement } from '@wordpress/element';
|
||||
import classnames from 'classnames';
|
||||
|
||||
const previewTextMaxLength = 150;
|
||||
const previewTextRecommendedLength = 80;
|
||||
|
||||
export function EmailSidebarExtensionBody({ RichTextWithButton }) {
|
||||
const [mailpoetEmailData] = useEntityProp(
|
||||
'postType',
|
||||
'mailpoet_email',
|
||||
'mailpoet_data',
|
||||
);
|
||||
|
||||
const updateEmailMailPoetProperty = (name: string, value: string) => {
|
||||
const postId = select(editorStore).getCurrentPostId();
|
||||
const currentPostType = 'mailpoet_email'; // only for mailpoet_email post-type
|
||||
|
||||
const editedPost = select(coreDataStore).getEditedEntityRecord(
|
||||
'postType',
|
||||
currentPostType,
|
||||
postId,
|
||||
);
|
||||
|
||||
// @ts-expect-error Property 'mailpoet_data' does not exist on type 'Updatable<Attachment<any>>'.
|
||||
const mailpoetData = editedPost?.mailpoet_data || {};
|
||||
void dispatch(coreDataStore).editEntityRecord(
|
||||
'postType',
|
||||
currentPostType,
|
||||
postId,
|
||||
{
|
||||
mailpoet_data: {
|
||||
...mailpoetData,
|
||||
[name]: value,
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const subjectHelp = createInterpolateElement(
|
||||
__(
|
||||
'Use personalization tags to personalize your email, or learn more about <bestPracticeLink>best practices</bestPracticeLink> and using <emojiLink>emoji in subject lines</emojiLink>.',
|
||||
'mailpoet',
|
||||
),
|
||||
{
|
||||
bestPracticeLink: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href="https://www.mailpoet.com/blog/17-email-subject-line-best-practices-to-boost-engagement/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
),
|
||||
emojiLink: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href="https://www.mailpoet.com/blog/tips-using-emojis-in-subject-lines/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
const previewTextLength = mailpoetEmailData?.preheader?.length ?? 0;
|
||||
|
||||
const preheaderHelp = createInterpolateElement(
|
||||
__(
|
||||
'<link>This text</link> will appear in the inbox, underneath the subject line.',
|
||||
'mailpoet',
|
||||
),
|
||||
{
|
||||
link: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href={new URL(
|
||||
'article/418-preview-text',
|
||||
'https://kb.mailpoet.com/',
|
||||
).toString()}
|
||||
key="preview-text-kb"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
/>
|
||||
),
|
||||
},
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<RichTextWithButton
|
||||
attributeName="subject"
|
||||
attributeValue={mailpoetEmailData?.subject}
|
||||
updateProperty={updateEmailMailPoetProperty}
|
||||
label={__('Subject', 'mailpoet')}
|
||||
labelSuffix={
|
||||
<ExternalLink href="https://kb.mailpoet.com/article/435-a-guide-to-personalisation-tags-for-tailored-newsletters#list">
|
||||
{__('Guide', 'mailpoet')}
|
||||
</ExternalLink>
|
||||
}
|
||||
help={subjectHelp}
|
||||
placeholder={__('Eg. The summer sale is here!', 'mailpoet')}
|
||||
/>
|
||||
|
||||
<br />
|
||||
|
||||
<RichTextWithButton
|
||||
attributeName="preheader"
|
||||
attributeValue={mailpoetEmailData?.preheader}
|
||||
updateProperty={updateEmailMailPoetProperty}
|
||||
label={__('Preview text', 'mailpoet')}
|
||||
labelSuffix={
|
||||
<span
|
||||
className={classnames(
|
||||
'mailpoet-settings-panel__preview-text-length',
|
||||
{
|
||||
'mailpoet-settings-panel__preview-text-length-warning':
|
||||
previewTextLength > previewTextRecommendedLength,
|
||||
'mailpoet-settings-panel__preview-text-length-error':
|
||||
previewTextLength > previewTextMaxLength,
|
||||
},
|
||||
)}
|
||||
>
|
||||
{previewTextLength}/{previewTextMaxLength}
|
||||
</span>
|
||||
}
|
||||
help={preheaderHelp}
|
||||
placeholder={__(
|
||||
"Add a preview text to capture subscribers' attention and increase open rates.",
|
||||
'mailpoet',
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export function EmailSidebarExtension(RichTextWithButton: JSX.Element) {
|
||||
return <EmailSidebarExtensionBody RichTextWithButton={RichTextWithButton} />;
|
||||
}
|
@@ -5,7 +5,6 @@
|
||||
import { addFilter, addAction } from '@wordpress/hooks';
|
||||
import { MailPoet } from 'mailpoet';
|
||||
import { withSatismeterSurvey } from './satismeter-survey';
|
||||
import { EmailSidebarExtension } from './email-sidebar-extension';
|
||||
import './index.scss';
|
||||
import { useValidationRules } from './validate-email-content';
|
||||
|
||||
@@ -13,12 +12,10 @@ addFilter('mailpoet_email_editor_wrap_editor_component', 'mailpoet', (editor) =>
|
||||
withSatismeterSurvey(editor),
|
||||
);
|
||||
|
||||
// validate email editor content using the defined validation rules
|
||||
// content is first validated when the "Send" button is clicked and revalidated on "Save Draft"
|
||||
addFilter(
|
||||
'mailpoet_email_editor_content_validation_rules',
|
||||
'mailpoet',
|
||||
() => useValidationRules(), // returns a memorized set of rules (array of rules)
|
||||
(validationRules: []) => [...validationRules, ...useValidationRules()],
|
||||
);
|
||||
|
||||
const EVENTS_TO_TRACK = [
|
||||
@@ -56,22 +53,3 @@ addFilter(
|
||||
'mailpoet',
|
||||
() => !!window.mailpoet_analytics_enabled,
|
||||
);
|
||||
|
||||
// integration point for settings sidebar
|
||||
addFilter(
|
||||
'mailpoet_email_editor_setting_sidebar_extension_component',
|
||||
'mailpoet',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
(RichTextWithButton) => EmailSidebarExtension.bind(null, RichTextWithButton),
|
||||
);
|
||||
|
||||
// use mailpoet data subject if available
|
||||
addFilter(
|
||||
'mailpoet_email_editor_preferred_template_title',
|
||||
'mailpoet',
|
||||
(...args) => {
|
||||
const [, post] = args;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||
return post?.mailpoet_data?.subject || ''; // use MailPoet subject as title
|
||||
},
|
||||
);
|
||||
|
@@ -64,14 +64,12 @@ export function useValidationRules() {
|
||||
label: __('Insert link', 'mailpoet'),
|
||||
onClick: () => {
|
||||
if (!hasFooter) {
|
||||
// update the email content
|
||||
void dispatch(blockEditorStore).insertBlock(
|
||||
linksParagraphBlock,
|
||||
undefined,
|
||||
contentBlockId,
|
||||
);
|
||||
} else {
|
||||
// update the template
|
||||
void dispatch(coreDataStore).editEntityRecord(
|
||||
'postType',
|
||||
'wp_template',
|
||||
|
@@ -30,10 +30,8 @@ export function FrontendBlock({
|
||||
}
|
||||
|
||||
return (
|
||||
<CheckboxControl
|
||||
checked={checked}
|
||||
onChange={setChecked}
|
||||
label={<RawHTML>{text || defaultText}</RawHTML>}
|
||||
/>
|
||||
<CheckboxControl checked={checked} onChange={setChecked}>
|
||||
<RawHTML>{text || defaultText}</RawHTML>
|
||||
</CheckboxControl>
|
||||
);
|
||||
}
|
||||
|
@@ -56,11 +56,7 @@ export function Edit({
|
||||
<div {...blockProps}>
|
||||
{optinEnabled ? (
|
||||
<div className="wc-block-checkout__marketing">
|
||||
<CheckboxControl
|
||||
id="mailpoet-marketing-optin"
|
||||
checked={false}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
<CheckboxControl id="mailpoet-marketing-optin" checked={false} />
|
||||
<RichText
|
||||
value={currentText}
|
||||
onChange={(value) => setAttributes({ text: value })}
|
||||
|
@@ -41,36 +41,24 @@ export const initializeSatismeterSurvey = (writeId = null) => {
|
||||
} else {
|
||||
writeKey = oldUsersPollId;
|
||||
}
|
||||
const traits = {
|
||||
name: window.mailpoet_current_wp_user.user_nicename,
|
||||
email: window.mailpoet_current_wp_user.user_email,
|
||||
mailpoetVersion: window.mailpoet_version,
|
||||
mailpoetPremiumIsActive: window.mailpoet_premium_active,
|
||||
createdAt: trackingData.installedAtIso,
|
||||
newslettersSent: trackingData.newslettersSent,
|
||||
welcomeEmails: trackingData.welcomeEmails,
|
||||
postnotificationEmails: trackingData.postnotificationEmails,
|
||||
woocommerceEmails: trackingData.woocommerceEmails,
|
||||
subscribers: trackingData.subscribers,
|
||||
lists: trackingData.lists,
|
||||
sendingMethod: trackingData.sendingMethod,
|
||||
woocommerceIsInstalled: trackingData.woocommerceIsInstalled,
|
||||
woocommerceVersion: trackingData.woocommerceVersion,
|
||||
WordPressVersion: trackingData.WordPressVersion,
|
||||
blockTheme: trackingData.blockTheme,
|
||||
themeVersion: trackingData.themeVersion,
|
||||
theme: trackingData.theme,
|
||||
};
|
||||
if (trackingData.gutenbergVersion) {
|
||||
traits.gutenbergVersion = trackingData.gutenbergVersion;
|
||||
}
|
||||
if (trackingData.wooCommerceVersion) {
|
||||
traits.wooCommerceVersion = trackingData.wooCommerceVersion;
|
||||
}
|
||||
satismeter({
|
||||
writeKey,
|
||||
userId: window.mailpoet_current_wp_user.ID + window.mailpoet_site_url,
|
||||
traits,
|
||||
traits: {
|
||||
name: window.mailpoet_current_wp_user.user_nicename,
|
||||
email: window.mailpoet_current_wp_user.user_email,
|
||||
mailpoetVersion: window.mailpoet_version,
|
||||
mailpoetPremiumIsActive: window.mailpoet_premium_active,
|
||||
createdAt: trackingData.installedAtIso,
|
||||
newslettersSent: trackingData.newslettersSent,
|
||||
welcomeEmails: trackingData.welcomeEmails,
|
||||
postnotificationEmails: trackingData.postnotificationEmails,
|
||||
woocommerceEmails: trackingData.woocommerceEmails,
|
||||
subscribers: trackingData.subscribers,
|
||||
lists: trackingData.lists,
|
||||
sendingMethod: trackingData.sendingMethod,
|
||||
woocommerceIsInstalled: trackingData.woocommerceIsInstalled,
|
||||
},
|
||||
events: {
|
||||
submit: (response) => {
|
||||
if (response.rating >= 9 && response.completed) {
|
||||
|
@@ -1,9 +1,5 @@
|
||||
== Changelog ==
|
||||
|
||||
= 5.7.1 - 2025-02-17 =
|
||||
* Improved: Apply get_the_excerpt filter to MailPoets post excerpts;
|
||||
* Fixed: conflict with Rank Math plugin breaking "Scheduled Actions" page.
|
||||
|
||||
= 5.7.0 - 2025-02-11 =
|
||||
* Improved: Rename Review Trigger in Automations;
|
||||
* Changed: minimum required WooCommerce is 9.5;
|
||||
|
@@ -5,7 +5,7 @@
|
||||
"dragonmantank/cron-expression": "^3.3",
|
||||
"mailpoet/email-editor": "*",
|
||||
"mixpanel/mixpanel-php": "2.*",
|
||||
"woocommerce/action-scheduler": "3.9.2"
|
||||
"woocommerce/action-scheduler": "^3.8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-gd": "*",
|
||||
@@ -61,9 +61,6 @@
|
||||
"MailPoet\\Test\\DataGenerator\\": "tests/DataGenerator"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
"soundasleep/html2text": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"pre-install-cmd": [
|
||||
"@php tools/install.php",
|
||||
|
26
mailpoet/composer.lock
generated
26
mailpoet/composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a95263a51332277e75d7147e3dc4551e",
|
||||
"content-hash": "9957467448f215809ac9dc8fbcab2b00",
|
||||
"packages": [
|
||||
{
|
||||
"name": "dragonmantank/cron-expression",
|
||||
@@ -73,11 +73,10 @@
|
||||
"dist": {
|
||||
"type": "path",
|
||||
"url": "../packages/php/email-editor",
|
||||
"reference": "53577c5aa3a97e82c58284d48c3aa339cb2a15d4"
|
||||
"reference": "311798cfd57b26bb5df1fc7f97b5732e45603419"
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.4",
|
||||
"soundasleep/html2text": "^2.1"
|
||||
"php": ">=7.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
@@ -102,9 +101,6 @@
|
||||
],
|
||||
"code-style-fix": [
|
||||
"../../../mailpoet/tasks/code_sniffer/vendor/bin/phpcbf -p"
|
||||
],
|
||||
"phpstan": [
|
||||
"php ./tasks/run-phpstan.php"
|
||||
]
|
||||
},
|
||||
"description": "Email editor based on WordPress Gutenberg package.",
|
||||
@@ -221,20 +217,20 @@
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/action-scheduler",
|
||||
"version": "3.9.2",
|
||||
"version": "3.8.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/action-scheduler.git",
|
||||
"reference": "efbb7953f72a433086335b249292f280dd43ddfe"
|
||||
"reference": "99cd7981f51c98883082534d4852491858d72834"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/efbb7953f72a433086335b249292f280dd43ddfe",
|
||||
"reference": "efbb7953f72a433086335b249292f280dd43ddfe",
|
||||
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/99cd7981f51c98883082534d4852491858d72834",
|
||||
"reference": "99cd7981f51c98883082534d4852491858d72834",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
"php": ">=5.6"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.5",
|
||||
@@ -258,9 +254,9 @@
|
||||
"homepage": "https://actionscheduler.org/",
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/action-scheduler/issues",
|
||||
"source": "https://github.com/woocommerce/action-scheduler/tree/3.9.2"
|
||||
"source": "https://github.com/woocommerce/action-scheduler/tree/3.8.0"
|
||||
},
|
||||
"time": "2025-02-03T09:09:30+00:00"
|
||||
"time": "2024-05-22T13:50:29+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
@@ -8343,7 +8339,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": {},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
@@ -417,15 +417,11 @@ class Reporter {
|
||||
}
|
||||
|
||||
public function getTrackingData() {
|
||||
global $wp_version, $woocommerce; // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
$newsletters = $this->newslettersRepository->getAnalytics();
|
||||
$segments = $this->segmentsRepository->getCountsPerType();
|
||||
$mta = $this->settings->get('mta', []);
|
||||
$installedAt = new Carbon($this->settings->get('installed_at'));
|
||||
$theme = $this->wp->wpGetTheme();
|
||||
$result = [
|
||||
'WordPressVersion' => $wp_version, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'pluginGutenberg' => $this->wp->isPluginActive('gutenberg/gutenberg.php'),
|
||||
return [
|
||||
'installedAtIso' => $installedAt->format(Carbon::ISO8601),
|
||||
'newslettersSent' => $newsletters['sent_newsletters_count'],
|
||||
'welcomeEmails' => $newsletters['welcome_newsletters_count'],
|
||||
@@ -433,19 +429,9 @@ class Reporter {
|
||||
'woocommerceEmails' => $newsletters['automatic_emails_count'],
|
||||
'subscribers' => $this->subscribersFeature->getSubscribersCount(),
|
||||
'lists' => isset($segments['default']) ? (int)$segments['default'] : 0,
|
||||
'sendingMethod' => $mta['method'] ?? null,
|
||||
'sendingMethod' => isset($mta['method']) ? $mta['method'] : null,
|
||||
'woocommerceIsInstalled' => $this->woocommerceHelper->isWooCommerceActive(),
|
||||
'blockTheme' => $this->wp->wpIsBlockTheme(),
|
||||
'theme' => $theme->Name, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
'themeVersion' => $theme->Version, // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
];
|
||||
if (defined('GUTENBERG_VERSION')) {
|
||||
$result['gutenbergVersion'] = GUTENBERG_VERSION;
|
||||
}
|
||||
if ($this->woocommerceHelper->isWooCommerceActive()) {
|
||||
$result['wooCommerceVersion'] = $woocommerce->version;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function isFilterTypeActive(string $filterType, string $action): bool {
|
||||
|
@@ -62,7 +62,6 @@ class SendEmailAction implements Action {
|
||||
private const OPTIN_RETRIES = 'optin_retries';
|
||||
|
||||
private const TRANSACTIONAL_TRIGGERS = [
|
||||
'mailpoet:custom-trigger',
|
||||
'woocommerce:order-status-changed',
|
||||
'woocommerce:order-created',
|
||||
'woocommerce:order-completed',
|
||||
|
@@ -35,7 +35,7 @@ class EmailEditorPreviewEmail {
|
||||
}
|
||||
|
||||
private function validateData($data) {
|
||||
if (empty($data['email']) || empty($data['postId'])) {
|
||||
if (empty($data['email']) || empty($data['postId']) || empty($data['newsletterId'])) {
|
||||
throw new \InvalidArgumentException(esc_html__('Missing required data', 'mailpoet'));
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ class EmailEditorPreviewEmail {
|
||||
* @throws \Exception
|
||||
*/
|
||||
private function fetchNewsletter($postData): NewsletterEntity {
|
||||
$newsletter = $this->newslettersRepository->findOneBy(['wpPost' => (int)$postData['postId']]);
|
||||
$newsletter = $this->newslettersRepository->findOneById((int)$postData['newsletterId']);
|
||||
|
||||
if (!$newsletter instanceof NewsletterEntity) {
|
||||
if (!$newsletter) {
|
||||
throw new \Exception(esc_html__('This email does not exist.', 'mailpoet'));
|
||||
}
|
||||
|
||||
|
@@ -39,13 +39,7 @@ class PostContentManager {
|
||||
if ($this->wp->hasExcerpt($post)) {
|
||||
return self::stripShortCodes($this->wp->getTheExcerpt($post));
|
||||
}
|
||||
return self::stripShortCodes(
|
||||
$this->wp->applyFilters(
|
||||
'get_the_excerpt',
|
||||
$this->generateExcerpt($post->post_content), // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
$post
|
||||
)
|
||||
);
|
||||
return $this->generateExcerpt($post->post_content); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
}
|
||||
return self::stripShortCodes($post->post_content); // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
/*
|
||||
* Plugin Name: MailPoet
|
||||
* Version: 5.7.1
|
||||
* Version: 5.7.0
|
||||
* Plugin URI: https://www.mailpoet.com
|
||||
* Description: Create and send newsletters, post notifications and welcome emails from your WordPress.
|
||||
* Author: MailPoet
|
||||
@@ -20,7 +20,7 @@
|
||||
*/
|
||||
|
||||
$mailpoetPlugin = [
|
||||
'version' => '5.7.1',
|
||||
'version' => '5.7.0',
|
||||
'filename' => __FILE__,
|
||||
'path' => dirname(__FILE__),
|
||||
'autoloader' => dirname(__FILE__) . '/vendor/autoload.php',
|
||||
|
@@ -3,7 +3,7 @@ Contributors: mailpoet, woocommerce, automattic
|
||||
Tags: email marketing, post notification, woocommerce emails, email automation, newsletter
|
||||
Requires at least: 6.6
|
||||
Tested up to: 6.7
|
||||
Stable tag: 5.7.1
|
||||
Stable tag: 5.7.0
|
||||
Requires PHP: 7.4
|
||||
License: GPLv3
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
@@ -222,8 +222,11 @@ Check our [Knowledge Base](https://kb.mailpoet.com) or contact us through our [s
|
||||
|
||||
== Changelog ==
|
||||
|
||||
= 5.7.1 - 2025-02-17 =
|
||||
* Improved: Apply get_the_excerpt filter to MailPoets post excerpts;
|
||||
* Fixed: conflict with Rank Math plugin breaking "Scheduled Actions" page.
|
||||
= 5.7.0 - 2025-02-11 =
|
||||
* Improved: Rename Review Trigger in Automations;
|
||||
* Changed: minimum required WooCommerce is 9.5;
|
||||
* Fixed: Automation UI shows wrong saved status after failed activation;
|
||||
* Fixed: Email preview does not work with sent emails;
|
||||
* Fixed: email content patterns are mixed with page starter patterns.
|
||||
|
||||
[See the changelog for all versions.](https://github.com/mailpoet/mailpoet/blob/trunk/mailpoet/changelog.txt)
|
||||
|
@@ -55,8 +55,7 @@ class CreateAndSendEmailUsingGutenbergCest {
|
||||
$i->click('Save Draft');
|
||||
$i->waitForText('Saved');
|
||||
$i->waitForText('Email saved!');
|
||||
$i->click('[aria-label="Close sidebar"]'); // close the sidebar. It sometimes interferes with the send button click
|
||||
$i->click('[data-automation-id="email_editor_send_button"]');
|
||||
$i->click('Send');
|
||||
$i->waitForElement('[name="subject"]');
|
||||
$subject = $i->grabValueFrom('[name="subject"]');
|
||||
verify($subject)->equals('My New Subject');
|
||||
|
@@ -4,7 +4,7 @@ This folder contains the code for the MailPoet Email Editor JS Package.
|
||||
We aim to extract the package as an independent library, so it can be used in other projects.
|
||||
As we are still in an exploration phase, we keep it together with the MailPoet codebase.
|
||||
|
||||
You can try the email editor in [the WordPress Playground](https://playground.wordpress.net/?blueprint-url=https://raw.githubusercontent.com/mailpoet/mailpoet/refs/heads/trunk/packages/js/email-editor/blueprint.json).
|
||||
You can try the email editor in [the WordPress Playground](https://playground.wordpress.net/?mode=seamless#%7B%22preferredVersions%22:%7B%22php%22:%228.2%22,%22wp%22:%22latest%22%7D,%22phpExtensionBundles%22:%5B%22kitchen-sink%22%5D,%22features%22:%7B%7D,%22landingPage%22:%22/wp-admin/admin.php?page=mailpoet-newsletters%22,%22steps%22:%5B%7B%22step%22:%22login%22,%22username%22:%22admin%22,%22password%22:%22password%22%7D,%7B%22step%22:%22installPlugin%22,%22pluginData%22:%7B%22resource%22:%22url%22,%22url%22:%22https://account.mailpoet.com/playground/plugin-proxy/branch:trunk%22%7D%7D,%7B%22step%22:%22mkdir%22,%22path%22:%22wordpress/wp-content/mu-plugins%22%7D,%7B%22step%22:%22writeFile%22,%22path%22:%22wordpress/wp-content/mu-plugins/addFilter-2.php%22,%22data%22:%22%3C?php%20%5Cnuse%20MailPoet%5C%5CDI%5C%5CContainerWrapper;%5Cnuse%20MailPoet%5C%5CFeatures%5C%5CFeatureFlagsRepository;%5Cnuse%20MailPoet%5C%5CFeatures%5C%5CFeaturesController;%5Cnadd_filter('mailpoet_skip_welcome_wizard',%20'__return_true');%22%7D%5D%7D).
|
||||
|
||||
You can locate the PHP package here `packages/php/email-editor`
|
||||
|
||||
@@ -98,13 +98,11 @@ We may add, update and delete any of them.
|
||||
|
||||
### Filters
|
||||
|
||||
| Name | Argument | Return | Description |
|
||||
|-----------------------------------------------------------------|----------------------------------|--------------------------------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||
| `mailpoet_email_editor_events_tracking_enabled` | `boolean` (false-default) | `boolean` | Used to enable the email editor events tracking and collection |
|
||||
| `mailpoet_email_editor_wrap_editor_component` | `JSX.Element` Editor | `JSX.Element` Editor | The main editor component. Custom component can wrap the editor and provide additional functionality |
|
||||
| `mailpoet_email_editor_send_button_label` | `string` 'Send' | `string` 'Send' (default) | Email editor send button label. The `Send` text can be updated using this filter |
|
||||
| `mailpoet_email_editor_send_action_callback` | `function` sendAction | `function` sendAction | Action to perform when the Send button is clicked |
|
||||
| `mailpoet_email_editor_content_validation_rules` | `array` rules | `EmailContentValidationRule[]` rules | Email editor content validation rules. The validation is done on `send btton` click and revalidated on `save draft` |
|
||||
| `mailpoet_email_editor_check_sending_method_configuration_link` | `string` link | `string` link | Edit or remove the sending configuration link message |
|
||||
| `mailpoet_email_editor_setting_sidebar_extension_component` | `JSX.Element` RichTextWithButton | `JSX.Element` Sidebar extension component | Add components to the Email settings sidebar |
|
||||
| `mailpoet_email_editor_preferred_template_title` | `string` '', `Post` post | `string` custom (preferred) template title | Custom title for Email preset template selector |
|
||||
| Name | Argument | Return | Description |
|
||||
|--------------------------------------------------|---------------------------|--------------------------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||
| `mailpoet_email_editor_events_tracking_enabled` | `boolean` (false-default) | `boolean` | Used to enable the email editor events tracking and collection |
|
||||
| `mailpoet_email_editor_wrap_editor_component` | `JSX.Element` Editor | `JSX.Element` Editor | The main editor component. Custom component can wrap the editor and provide additional functionality |
|
||||
| `mailpoet_email_editor_send_button_label` | `string` 'Send' | `string` 'Send' (default) | Email editor send button label. The `Send` text can be updated using this filter |
|
||||
| `mailpoet_email_editor_send_action_callback` | `function` sendAction | `function` sendAction | Action to perform when the Send button is clicked |
|
||||
| `mailpoet_email_editor_content_validation_rules` | `array` rules | `EmailContentValidationRule[]` rules | Email editor content validation rules. The validation is done on `send btton` click and revalidated on `save draft` |
|
||||
|
||||
|
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"landingPage": "/wp-admin/admin.php?page=mailpoet-newsletters",
|
||||
"preferredVersions": {
|
||||
"php": "8.2",
|
||||
"wp": "latest"
|
||||
},
|
||||
"features": {
|
||||
"networking": true
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"step": "login",
|
||||
"username": "admin"
|
||||
},
|
||||
{
|
||||
"step": "installPlugin",
|
||||
"pluginData": {
|
||||
"resource": "url",
|
||||
"url": "https://account.mailpoet.com/playground/plugin-proxy/branch:trunk"
|
||||
}
|
||||
},
|
||||
{
|
||||
"step": "mkdir",
|
||||
"path": "wordpress/wp-content/mu-plugins"
|
||||
},
|
||||
{
|
||||
"step": "writeFile",
|
||||
"path": "wordpress/wp-content/mu-plugins/addFilter-2.php",
|
||||
"data": "<?php \nadd_filter('mailpoet_skip_welcome_wizard', '__return_true');"
|
||||
}
|
||||
]
|
||||
}
|
@@ -35,7 +35,8 @@ export function MoreMenu(): JSX.Element {
|
||||
editorCurrentPostType,
|
||||
'status'
|
||||
);
|
||||
const { saveEditedEmail } = useDispatch( storeName );
|
||||
const { saveEditedEmail, updateEmailMailPoetProperty } =
|
||||
useDispatch( storeName );
|
||||
const goToListings = () => {
|
||||
window.location.href = urls.listings;
|
||||
};
|
||||
@@ -131,6 +132,10 @@ export function MoreMenu(): JSX.Element {
|
||||
<MenuItem
|
||||
onClick={ async () => {
|
||||
await setStatus( 'draft' );
|
||||
await updateEmailMailPoetProperty(
|
||||
'deleted_at',
|
||||
''
|
||||
);
|
||||
await saveEditedEmail();
|
||||
recordEvent(
|
||||
'header_more_menu_restore_from_trash_button_clicked'
|
||||
|
@@ -63,7 +63,6 @@ export function SendButton( { validateContent, isContentInvalid } ) {
|
||||
}
|
||||
} }
|
||||
disabled={ isDisabled }
|
||||
data-automation-id="email_editor_send_button"
|
||||
>
|
||||
{ label }
|
||||
</Button>
|
||||
|
@@ -18,7 +18,7 @@ type Replacement = {
|
||||
function getChildElement( rootElement: HTMLElement ): HTMLElement | null {
|
||||
let currentElement: HTMLElement | null = rootElement;
|
||||
|
||||
while ( currentElement && currentElement?.children?.length > 0 ) {
|
||||
while ( currentElement && currentElement.children.length > 0 ) {
|
||||
// Traverse into the first child element
|
||||
currentElement = currentElement.children[ 0 ] as HTMLElement;
|
||||
}
|
||||
|
@@ -1,10 +1,10 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import type { ReactNode } from 'react';
|
||||
import { BaseControl, Button } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { useCallback, useRef, useState } from '@wordpress/element';
|
||||
import { useEntityProp } from '@wordpress/core-data';
|
||||
import { create, insert, toHTMLString } from '@wordpress/rich-text';
|
||||
import { RichText } from '@wordpress/block-editor';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
@@ -17,32 +17,25 @@ import {
|
||||
getCursorPosition,
|
||||
replacePersonalizationTagsWithHTMLComments,
|
||||
} from './rich-text-utils';
|
||||
import { storeName } from '../../store';
|
||||
import { storeName, editorCurrentPostType } from '../../store';
|
||||
import { PersonalizationTagsPopover } from './personalization-tags-popover';
|
||||
import { recordEvent, recordEventOnce } from '../../events';
|
||||
|
||||
type Props = {
|
||||
label: string;
|
||||
labelSuffix?: ReactNode;
|
||||
help?: ReactNode;
|
||||
placeholder: string;
|
||||
attributeName: string;
|
||||
attributeValue?: string;
|
||||
updateProperty: (
|
||||
theAttributeName: string,
|
||||
theUpdatedValue: string
|
||||
) => void;
|
||||
};
|
||||
|
||||
export function RichTextWithButton( {
|
||||
label,
|
||||
labelSuffix,
|
||||
help,
|
||||
placeholder,
|
||||
attributeName,
|
||||
attributeValue,
|
||||
updateProperty = () => {},
|
||||
}: Props ) {
|
||||
} ) {
|
||||
const [ mailpoetEmailData ] = useEntityProp(
|
||||
'postType',
|
||||
editorCurrentPostType,
|
||||
'mailpoet_data'
|
||||
);
|
||||
|
||||
const { updateEmailMailPoetProperty } = useDispatch( storeName );
|
||||
|
||||
const [ selectionRange, setSelectionRange ] = useState( null );
|
||||
const [ isModalOpened, setIsModalOpened ] = useState( false );
|
||||
const list = useSelect(
|
||||
@@ -68,11 +61,11 @@ export function RichTextWithButton( {
|
||||
const updatedValue = toHTMLString( { value: richTextValue } );
|
||||
|
||||
// Update the corresponding property
|
||||
updateProperty( attributeName, updatedValue );
|
||||
updateEmailMailPoetProperty( attributeName, updatedValue );
|
||||
|
||||
setSelectionRange( null );
|
||||
},
|
||||
[ attributeName, updateProperty ]
|
||||
[ attributeName, updateEmailMailPoetProperty ]
|
||||
);
|
||||
|
||||
const finalLabel = (
|
||||
@@ -97,13 +90,13 @@ export function RichTextWithButton( {
|
||||
</>
|
||||
);
|
||||
|
||||
if ( ! attributeName ) {
|
||||
if ( ! mailpoetEmailData ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<BaseControl
|
||||
id="" // See https://github.com/mailpoet/mailpoet/pull/6089#discussion_r1952126850 to understand why the ID is empty
|
||||
id={ `mailpoet-settings-panel__${ attributeName }` }
|
||||
label={ finalLabel }
|
||||
className={ `mailpoet-settings-panel__${ attributeName }-text` }
|
||||
help={ help }
|
||||
@@ -114,7 +107,7 @@ export function RichTextWithButton( {
|
||||
onInsert={ ( value ) => {
|
||||
handleInsertPersonalizationTag(
|
||||
value,
|
||||
attributeValue ?? '',
|
||||
mailpoetEmailData[ attributeName ] ?? '',
|
||||
selectionRange
|
||||
);
|
||||
setIsModalOpened( false );
|
||||
@@ -132,13 +125,17 @@ export function RichTextWithButton( {
|
||||
<PersonalizationTagsPopover
|
||||
contentRef={ richTextRef }
|
||||
onUpdate={ ( originalTag, updatedTag ) => {
|
||||
const currentValue = attributeValue ?? '';
|
||||
const currentValue =
|
||||
mailpoetEmailData[ attributeName ] ?? '';
|
||||
// When we update the tag, we need to add brackets to the tag, because the popover removes them
|
||||
const updatedContent = currentValue.replace(
|
||||
`<!--[${ originalTag }]-->`,
|
||||
`<!--[${ updatedTag }]-->`
|
||||
);
|
||||
updateProperty( attributeName, updatedContent );
|
||||
updateEmailMailPoetProperty(
|
||||
attributeName,
|
||||
updatedContent
|
||||
);
|
||||
} }
|
||||
/>
|
||||
<RichText
|
||||
@@ -147,17 +144,26 @@ export function RichTextWithButton( {
|
||||
placeholder={ placeholder }
|
||||
onFocus={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition( richTextRef, attributeValue ?? '' )
|
||||
getCursorPosition(
|
||||
richTextRef,
|
||||
mailpoetEmailData[ attributeName ] ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onKeyUp={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition( richTextRef, attributeValue ?? '' )
|
||||
getCursorPosition(
|
||||
richTextRef,
|
||||
mailpoetEmailData[ attributeName ] ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onClick={ () => {
|
||||
setSelectionRange(
|
||||
getCursorPosition( richTextRef, attributeValue ?? '' )
|
||||
getCursorPosition(
|
||||
richTextRef,
|
||||
mailpoetEmailData[ attributeName ] ?? ''
|
||||
)
|
||||
);
|
||||
} }
|
||||
onChange={ ( value ) => {
|
||||
@@ -165,7 +171,7 @@ export function RichTextWithButton( {
|
||||
value ?? '',
|
||||
list
|
||||
);
|
||||
updateProperty( attributeName, value );
|
||||
updateEmailMailPoetProperty( attributeName, value );
|
||||
recordEventOnce(
|
||||
'rich_text_with_button_input_field_updated',
|
||||
{
|
||||
@@ -173,7 +179,7 @@ export function RichTextWithButton( {
|
||||
}
|
||||
);
|
||||
} }
|
||||
value={ attributeValue ?? '' }
|
||||
value={ mailpoetEmailData[ attributeName ] ?? '' }
|
||||
data-automation-id={ `email_${ attributeName }` }
|
||||
/>
|
||||
</BaseControl>
|
||||
|
@@ -4,7 +4,7 @@
|
||||
import { Button, Modal, TextControl } from '@wordpress/components';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { check, Icon } from '@wordpress/icons';
|
||||
import { __, sprintf } from '@wordpress/i18n';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import {
|
||||
useEffect,
|
||||
useRef,
|
||||
@@ -13,23 +13,19 @@ import {
|
||||
} from '@wordpress/element';
|
||||
import { ENTER } from '@wordpress/keycodes';
|
||||
import { isEmail } from '@wordpress/url';
|
||||
import { applyFilters } from '@wordpress/hooks';
|
||||
import { useEntityProp } from '@wordpress/core-data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import {
|
||||
MailPoetEmailData,
|
||||
SendingPreviewStatus,
|
||||
storeName,
|
||||
editorCurrentPostType,
|
||||
} from '../../store';
|
||||
import { recordEvent, recordEventOnce } from '../../events';
|
||||
|
||||
const sendingMethodConfigurationLink = applyFilters(
|
||||
'mailpoet_email_editor_check_sending_method_configuration_link',
|
||||
'admin.php?page=mailpoet-settings#mta'
|
||||
) as string;
|
||||
|
||||
function RawSendPreviewEmail() {
|
||||
const sendToRef = useRef( null );
|
||||
|
||||
@@ -44,11 +40,19 @@ function RawSendPreviewEmail() {
|
||||
isSendingPreviewEmail,
|
||||
sendingPreviewStatus,
|
||||
isModalOpened,
|
||||
errorMessage,
|
||||
} = useSelect( ( select ) => select( storeName ).getPreviewState(), [] );
|
||||
|
||||
const [ mailpoetEmailData ] = useEntityProp(
|
||||
'postType',
|
||||
editorCurrentPostType,
|
||||
'mailpoet_data'
|
||||
) as [ MailPoetEmailData, unknown, unknown ];
|
||||
|
||||
const handleSendPreviewEmail = () => {
|
||||
void requestSendingNewsletterPreview( previewToEmail );
|
||||
void requestSendingNewsletterPreview(
|
||||
mailpoetEmailData.id,
|
||||
previewToEmail
|
||||
);
|
||||
};
|
||||
|
||||
const closeCallback = () => {
|
||||
@@ -77,48 +81,33 @@ function RawSendPreviewEmail() {
|
||||
>
|
||||
{ sendingPreviewStatus === SendingPreviewStatus.ERROR ? (
|
||||
<div className="mailpoet-send-preview-modal-notice-error">
|
||||
<p>
|
||||
{ __(
|
||||
'Sorry, we were unable to send this email.',
|
||||
'mailpoet'
|
||||
) }
|
||||
</p>
|
||||
|
||||
<strong>
|
||||
{ errorMessage &&
|
||||
sprintf(
|
||||
// translators: %s is an error message.
|
||||
__( 'Error: %s', 'mailpoet' ),
|
||||
errorMessage
|
||||
) }
|
||||
</strong>
|
||||
|
||||
{ __(
|
||||
'Sorry, we were unable to send this email.',
|
||||
'mailpoet'
|
||||
) }
|
||||
<ul>
|
||||
<li>
|
||||
{ sendingMethodConfigurationLink &&
|
||||
createInterpolateElement(
|
||||
__(
|
||||
'Please check your <link>sending method configuration</link> with your hosting provider.',
|
||||
'mailpoet'
|
||||
{ createInterpolateElement(
|
||||
__(
|
||||
'Please check your <link>sending method configuration</link> with your hosting provider.',
|
||||
'mailpoet'
|
||||
),
|
||||
{
|
||||
link: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href="admin.php?page=mailpoet-settings#mta"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={ () =>
|
||||
recordEvent(
|
||||
'send_preview_email_modal_check_sending_method_configuration_link_clicked'
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
{
|
||||
link: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href={
|
||||
sendingMethodConfigurationLink
|
||||
}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={ () =>
|
||||
recordEvent(
|
||||
'send_preview_email_modal_check_sending_method_configuration_link_clicked'
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
) }
|
||||
}
|
||||
) }
|
||||
</li>
|
||||
<li>
|
||||
{ createInterpolateElement(
|
||||
@@ -130,7 +119,10 @@ function RawSendPreviewEmail() {
|
||||
link: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href={ `https://account.mailpoet.com/?s=1&g=1&utm_source=mailpoet_email_editor&utm_medium=plugin&utm_source_platform=${ editorCurrentPostType }` }
|
||||
href={ new URL(
|
||||
'free-plan',
|
||||
'https://www.mailpoet.com/'
|
||||
).toString() }
|
||||
key="sign-up-for-free"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
|
@@ -1,22 +1,92 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { PanelBody } from '@wordpress/components';
|
||||
import { ExternalLink, PanelBody } from '@wordpress/components';
|
||||
import { useEntityProp } from '@wordpress/core-data';
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { applyFilters } from '@wordpress/hooks';
|
||||
import { createInterpolateElement } from '@wordpress/element';
|
||||
import classnames from 'classnames';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { editorCurrentPostType } from '../../store';
|
||||
import { recordEvent } from '../../events';
|
||||
import { RichTextWithButton } from '../personalization-tags/rich-text-with-button';
|
||||
|
||||
const SidebarExtensionComponent = applyFilters(
|
||||
'mailpoet_email_editor_setting_sidebar_extension_component',
|
||||
RichTextWithButton
|
||||
) as () => JSX.Element;
|
||||
const previewTextMaxLength = 150;
|
||||
const previewTextRecommendedLength = 80;
|
||||
|
||||
export function DetailsPanel() {
|
||||
const [ mailpoetEmailData ] = useEntityProp(
|
||||
'postType',
|
||||
editorCurrentPostType,
|
||||
'mailpoet_data'
|
||||
);
|
||||
|
||||
const subjectHelp = createInterpolateElement(
|
||||
__(
|
||||
'Use personalization tags to personalize your email, or learn more about <bestPracticeLink>best practices</bestPracticeLink> and using <emojiLink>emoji in subject lines</emojiLink>.',
|
||||
'mailpoet'
|
||||
),
|
||||
{
|
||||
bestPracticeLink: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href="https://www.mailpoet.com/blog/17-email-subject-line-best-practices-to-boost-engagement/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={ () =>
|
||||
recordEvent(
|
||||
'details_panel_subject_help_best_practice_link_clicked'
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
emojiLink: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href="https://www.mailpoet.com/blog/tips-using-emojis-in-subject-lines/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={ () =>
|
||||
recordEvent(
|
||||
'details_panel_subject_help_emoji_in_subject_lines_link_clicked'
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
const previewTextLength = mailpoetEmailData?.preheader?.length ?? 0;
|
||||
|
||||
const preheaderHelp = createInterpolateElement(
|
||||
__(
|
||||
'<link>This text</link> will appear in the inbox, underneath the subject line.',
|
||||
'mailpoet'
|
||||
),
|
||||
{
|
||||
link: (
|
||||
// eslint-disable-next-line jsx-a11y/anchor-has-content, jsx-a11y/control-has-associated-label
|
||||
<a
|
||||
href={ new URL(
|
||||
'article/418-preview-text',
|
||||
'https://kb.mailpoet.com/'
|
||||
).toString() }
|
||||
key="preview-text-kb"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
onClick={ () =>
|
||||
recordEvent(
|
||||
'details_panel_preheader_help_text_link_clicked'
|
||||
)
|
||||
}
|
||||
/>
|
||||
),
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<PanelBody
|
||||
title={ __( 'Details', 'mailpoet' ) }
|
||||
@@ -25,7 +95,50 @@ export function DetailsPanel() {
|
||||
recordEvent( 'details_panel_body_toggle', { opened: data } )
|
||||
}
|
||||
>
|
||||
<>{ <SidebarExtensionComponent /> }</>
|
||||
<RichTextWithButton
|
||||
attributeName="subject"
|
||||
label={ __( 'Subject', 'mailpoet' ) }
|
||||
labelSuffix={
|
||||
<ExternalLink
|
||||
href="https://kb.mailpoet.com/article/435-a-guide-to-personalisation-tags-for-tailored-newsletters#list"
|
||||
onClick={ () =>
|
||||
recordEvent(
|
||||
'details_panel_personalisation_tags_guide_link_clicked'
|
||||
)
|
||||
}
|
||||
>
|
||||
{ __( 'Guide', 'mailpoet' ) }
|
||||
</ExternalLink>
|
||||
}
|
||||
help={ subjectHelp }
|
||||
placeholder={ __( 'Eg. The summer sale is here!', 'mailpoet' ) }
|
||||
/>
|
||||
|
||||
<RichTextWithButton
|
||||
attributeName="preheader"
|
||||
label={ __( 'Preview text', 'mailpoet' ) }
|
||||
labelSuffix={
|
||||
<span
|
||||
className={ classnames(
|
||||
'mailpoet-settings-panel__preview-text-length',
|
||||
{
|
||||
'mailpoet-settings-panel__preview-text-length-warning':
|
||||
previewTextLength >
|
||||
previewTextRecommendedLength,
|
||||
'mailpoet-settings-panel__preview-text-length-error':
|
||||
previewTextLength > previewTextMaxLength,
|
||||
}
|
||||
) }
|
||||
>
|
||||
{ previewTextLength }/{ previewTextMaxLength }
|
||||
</span>
|
||||
}
|
||||
help={ preheaderHelp }
|
||||
placeholder={ __(
|
||||
"Add a preview text to capture subscribers' attention and increase open rates.",
|
||||
'mailpoet'
|
||||
) }
|
||||
/>
|
||||
</PanelBody>
|
||||
);
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ import { useMemo } from '@wordpress/element';
|
||||
import { parse } from '@wordpress/blocks';
|
||||
import { BlockInstance } from '@wordpress/blocks/index';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { applyFilters } from '@wordpress/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
@@ -164,11 +163,6 @@ export function usePreviewTemplates(
|
||||
|
||||
const allEmailPosts = useMemo( () => {
|
||||
return emailPosts?.map( ( post: EmailEditorPostType ) => {
|
||||
const preferredTitle = applyFilters(
|
||||
'mailpoet_email_editor_preferred_template_title',
|
||||
'',
|
||||
post
|
||||
);
|
||||
const { postTemplateContent } = generateTemplateContent(
|
||||
post,
|
||||
allTemplates
|
||||
@@ -186,8 +180,9 @@ export function usePreviewTemplates(
|
||||
const template = {
|
||||
...post,
|
||||
title: {
|
||||
raw: post.title.raw,
|
||||
rendered: preferredTitle || post.title.rendered,
|
||||
raw: post?.mailpoet_data?.subject || post.title.raw,
|
||||
rendered:
|
||||
post?.mailpoet_data?.subject || post.title.rendered, // use MailPoet subject as title
|
||||
},
|
||||
};
|
||||
return {
|
||||
|
@@ -134,6 +134,29 @@ export function* saveEditedEmail() {
|
||||
} );
|
||||
}
|
||||
|
||||
export function* updateEmailMailPoetProperty( name: string, value: string ) {
|
||||
const postId = select( storeName ).getEmailPostId();
|
||||
// There can be a better way how to get the edited post data
|
||||
const editedPost = select( coreDataStore ).getEditedEntityRecord(
|
||||
'postType',
|
||||
editorCurrentPostType,
|
||||
postId
|
||||
);
|
||||
// @ts-expect-error Property 'mailpoet_data' does not exist on type 'Updatable<Attachment<any>>'.
|
||||
const mailpoetData = editedPost?.mailpoet_data || {};
|
||||
yield dispatch( coreDataStore ).editEntityRecord(
|
||||
'postType',
|
||||
editorCurrentPostType,
|
||||
postId,
|
||||
{
|
||||
mailpoet_data: {
|
||||
...mailpoetData,
|
||||
[ name ]: value,
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export const setTemplateToPost =
|
||||
( templateSlug ) =>
|
||||
async ( { registry } ) => {
|
||||
@@ -145,7 +168,10 @@ export const setTemplateToPost =
|
||||
} );
|
||||
};
|
||||
|
||||
export function* requestSendingNewsletterPreview( email: string ) {
|
||||
export function* requestSendingNewsletterPreview(
|
||||
newsletterId: number,
|
||||
email: string
|
||||
) {
|
||||
// If preview is already sending do nothing
|
||||
const previewState = select( storeName ).getPreviewState();
|
||||
if ( previewState.isSendingPreviewEmail ) {
|
||||
@@ -166,6 +192,7 @@ export function* requestSendingNewsletterPreview( email: string ) {
|
||||
path: '/mailpoet-email-editor/v1/send_preview_email',
|
||||
method: 'POST',
|
||||
data: {
|
||||
newsletterId,
|
||||
email,
|
||||
postId,
|
||||
},
|
||||
@@ -186,9 +213,6 @@ export function* requestSendingNewsletterPreview( email: string ) {
|
||||
state: {
|
||||
sendingPreviewStatus: SendingPreviewStatus.ERROR,
|
||||
isSendingPreviewEmail: false,
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
errorMessage: JSON.stringify( errorResponse?.error ),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@@ -96,8 +96,13 @@ export const isEmpty = createRegistrySelector( ( select ) => (): boolean => {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { content, title } = post;
|
||||
return ! content.raw && ! title.raw;
|
||||
const { content, mailpoet_data: mailpoetData, title } = post;
|
||||
return (
|
||||
! content.raw &&
|
||||
! mailpoetData.subject &&
|
||||
! mailpoetData.preheader &&
|
||||
! title.raw
|
||||
);
|
||||
} );
|
||||
|
||||
export const hasEmptyContent = createRegistrySelector(
|
||||
|
@@ -202,7 +202,6 @@ export type State = {
|
||||
isModalOpened: boolean;
|
||||
isSendingPreviewEmail: boolean;
|
||||
sendingPreviewStatus: SendingPreviewStatus | null;
|
||||
errorMessage?: string;
|
||||
};
|
||||
personalizationTags: {
|
||||
list: PersonalizationTag[];
|
||||
@@ -210,6 +209,13 @@ export type State = {
|
||||
};
|
||||
};
|
||||
|
||||
export type MailPoetEmailData = {
|
||||
id: number;
|
||||
subject: string;
|
||||
preheader: string;
|
||||
preview_url: string;
|
||||
};
|
||||
|
||||
export type EmailTemplate = {
|
||||
id: string;
|
||||
slug: string;
|
||||
@@ -261,6 +267,7 @@ export type MailPoetEmailPostContentExtended = {
|
||||
|
||||
export type EmailEditorPostType = Omit< Post, 'type' > & {
|
||||
type: string;
|
||||
mailpoet_data?: MailPoetEmailPostContentExtended;
|
||||
};
|
||||
|
||||
export type EmailContentValidationAction = {
|
||||
|
@@ -75,3 +75,5 @@ We may add, update and delete any of them.
|
||||
| `mailpoet_email_editor_send_preview_email` | `Array` $postData | `boolean` Result of processing. Was email sent successfully? | Allows override of the send preview mail function. Folks may choose to use custom implementation |
|
||||
| `mailpoet_email_editor_post_sent_status_args` | `Array` `sent` post status args | `Array` register_post_status args | Allows update of the argument for the sent post status |
|
||||
|
||||
## TODO
|
||||
- We use `mailpoet_data` in some section of the codebase. This will be updated.
|
||||
|
@@ -5,7 +5,10 @@
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
],
|
||||
"files": [
|
||||
"src/exceptions.php"
|
||||
]
|
||||
},
|
||||
"autoload-dev": {
|
||||
"classmap": [
|
||||
@@ -26,7 +29,7 @@
|
||||
"unit-test": "../../../tests_env/vendor/bin/codecept run unit",
|
||||
"integration-test": "cd ../../../tests_env/docker && COMPOSE_HTTP_TIMEOUT=200 docker compose run -e SKIP_DEPS=1 -e SKIP_PLUGINS=1 -e PACKAGE_NAME=email-editor codeception_integration",
|
||||
"code-style": "../../../mailpoet/tasks/code_sniffer/vendor/bin/phpcs -ps",
|
||||
"code-style-fix": "../../../mailpoet/tasks/code_sniffer/vendor/bin/phpcbf -p",
|
||||
"phpstan": "php ./tasks/run-phpstan.php"
|
||||
"code-style-fix": "../../../mailpoet/tasks/code_sniffer/vendor/bin/phpcbf -p",
|
||||
"phpstan": "php ./tasks/run-phpstan.php"
|
||||
}
|
||||
}
|
||||
|
@@ -66,6 +66,7 @@ class Email_Api_Controller {
|
||||
* $data - Post Data
|
||||
* format
|
||||
* [_locale] => user
|
||||
* [newsletterId] => NEWSLETTER_ID
|
||||
* [email] => Provided email address
|
||||
* [postId] => POST_ID
|
||||
*/
|
||||
|
Reference in New Issue
Block a user