Compare commits

..

71 Commits

Author SHA1 Message Date
b0cdbe2db4 merge upstream 2025-03-09 11:12:56 +00:00
0064f32cd8 Fix deprecation: Optional parameter declared before required parameter
[MAILPOET-6491]
2025-03-06 10:40:17 +01:00
0a3d49c0cd Apply non-implicit nullables rule
[MAILPOET-6491]
2025-03-06 10:40:17 +01:00
540feb6364 Add PHPCS rule to avoid implicit nullables
[MAILPOET-6491]
2025-03-06 10:40:17 +01:00
0508874349 Translate WooCommerce email templates
[MAILPOET-6484]
2025-03-05 13:12:41 +01:00
fce1ac1e4a Load new email templates if email improvements feature is enabled in WooCommerce
[MAILPOET-6484]
2025-03-05 13:12:41 +01:00
40d61001d4 Check if email improvements feature is enabled in WooCommerce
[MAILPOET-6484]
2025-03-05 13:12:41 +01:00
a6beaa7025 Add new Woo email templates
These are copy-pasted from WC email preview without any change

[MAILPOET-6484]
2025-03-05 13:12:41 +01:00
370f7f83ef Change hook to inject MailPoet customizer overlay in WC settings
This will insert it above "Email template" section, which is then used as anchor to absolutely position from, instead of positioning it absolutely against the whole settings page

[MAILPOET-6484]
2025-03-05 13:12:41 +01:00
a57cc91438 Increase MailPoet overlay readability by decreasing opacity of content behind
[MAILPOET-6484]
2025-03-05 13:12:41 +01:00
5ac069813f Hide all WooCommerce email settings when MailPoet customizer is enabled
[MAILPOET-6484]
2025-03-05 13:12:41 +01:00
a2e164a832 Release 5.8.1 2025-03-04 15:21:09 +01:00
c424fe4b7a Fix showing Captcha title in themes that rely on single_post_title, such as Rufous or Silverstorm
[MAILPOET-6485]
2025-03-03 11:02:55 +01:00
bf3bc6d8cb Fix captcha title being applied to menu items in non-block based themes
[MAILPOET-6485]
2025-03-03 11:02:55 +01:00
afc27fabd6 Prevent auto refreshing MailPoet listing components when any items are selected
This is to prevent losing the selected state

[MAILPOET-6493]
2025-02-28 17:34:40 +01:00
c57844cb05 Update tests with oldest supported WC version
[MAILPOET-6493]
2025-02-28 17:34:40 +01:00
83b1f90b53 Bump WooCommerce minimum version to 9.6
[MAILPOET-6490]
2025-02-27 08:33:51 +01:00
355baa9a9b Release 5.8.0 2025-02-25 13:53:51 +01:00
19abc8963a Update used Automate Woo plugin in Circle CI
- latest version: 6.1.7
 - previous version: 6.0.33
2025-02-24 20:46:53 +01:00
e23e4c4974 Add tests
[MAILPOET-6384]
2025-02-24 12:50:34 +01:00
a3ec922ca5 Add an option to restrict coupon to current subscriber
[MAILPOET-6384]
2025-02-24 12:50:34 +01:00
1d28e8d7c6 Run QIT workflow weekly (Wednesday at 2am) 2025-02-24 10:38:10 +01:00
0e680d50e1 Add QIT workflow to CircleCI 2025-02-24 10:38:10 +01:00
c05670a860 Add additional QIT test commands
[MAILPOET-6487]
2025-02-24 10:38:10 +01:00
e9baeadd6e Make sure the navigation links are hidden on all themes
[MAILPOET-6424]
2025-02-20 13:42:08 +01:00
838d896cec Update the page capability type
MailPoet default pages were created with the default post capability type.

This means post navigation links (previous and next) are often found on the default pages, such as the unsubscribe and the manage subscription page.

[MAILPOET-6424]
2025-02-20 13:42:08 +01:00
919a855f34 Update mocha [MAILPOET-6486] 2025-02-20 11:55:49 +01:00
119a4f8280 merge upstream 2025-02-20 07:23:31 +00:00
336b63b43c Update used WooCommerce Subscriptions plugin in Circle CI
- latest version: 7.2.1
 - previous version: 7.1.0
2025-02-19 09:28:01 +03:00
fec3ae1239 Update used Automate Woo plugin in Circle CI
- latest version: 6.1.6
 - previous version: 6.0.33
2025-02-19 09:28:01 +03:00
227aab5c47 Update used WooCommerce plugin in Circle CI
- latest version: 9.6.2
 - previous version: 9.5.2
2025-02-19 09:28:01 +03:00
96eb805c5c Update used WordPress images in Circle CI
- latest version: 6.7.2-php8.3
 - previous version: 6.6.2
2025-02-19 09:28:01 +03:00
40f946f25c Release 5.7.1 2025-02-18 15:21:23 +03:00
cd44c42a1d Upgrade Action Scheduler to 3.9.2
[MAILPOET-6467]
2025-02-17 15:47:23 +01:00
a4d67a319a Properly set block opt-in checkbox label to render with correct classes
This fixes verical misalignment between the checkbox and the label

[MAILPOET-6436]
2025-02-17 13:08:29 +01:00
1372de1bba Update CheckboxControl types
6b8a403a03/plugins/woocommerce-blocks/packages/components/checkbox-control/index.tsx (L12-L21)

[MAILPOET-6436]
2025-02-17 13:08:29 +01:00
0b1eb4bea3 change subscriber limit at the source 2025-02-14 23:07:20 -06:00
6d017c9298 add more memory to node js so it doesnt brap out
All checks were successful
Make release / release (push) Successful in 56m59s
2025-02-14 19:17:12 -06:00
fe6d4603a2 verifying that pnpm is installed
All checks were successful
Make release / release (push) Successful in 43m35s
2025-02-14 07:04:25 -06:00
7959333fb1 further converge release.yml
All checks were successful
Make release / release (push) Successful in 30m17s
2025-02-14 04:17:41 -06:00
1cea4342f7 Revert "npx update-browserslist-db@latest because the build is frozen without it"
This reverts commit a5fb8b8171.
2025-02-14 04:16:34 -06:00
a5fb8b8171 npx update-browserslist-db@latest because the build is frozen without it
Some checks failed
Make release / release (push) Failing after 6m2s
2025-02-14 04:06:12 -06:00
22eb47d8bc simplify build 2025-02-14 03:48:25 -06:00
6fe58a2445 merge upstream 2025-02-14 09:37:47 +00:00
f5a4a300af make sure env files are set up
Some checks failed
Make release / release (push) Failing after 8m28s
2025-02-14 03:35:06 -06:00
d2723938aa keep development bloat to prevent crash
All checks were successful
Make release / release (push) Successful in 21m15s
2025-02-14 02:31:57 -06:00
4de580b7df Update Readme and add missing text. 2025-02-14 09:06:09 +01:00
642733ee0c Update Readme with information about the email editor js filters 2025-02-14 09:06:09 +01:00
80f354b860 Simplify the WordPress Playground link and add networking support 2025-02-14 09:06:09 +01:00
25262cff80 remove redundant zipping
All checks were successful
Make release / release (push) Successful in 6m56s
2025-02-13 20:16:29 -06:00
c31795cd4d Unfuck the generated release
All checks were successful
Make release / release (push) Successful in 32m25s
It was grabbing a whole lot of extra junk and didn't put it in the proper format for a wordpress plugin, this should fix it.
2025-02-13 18:47:56 -06:00
73d410eb86 Add Release Smith Support
All checks were successful
Make release / release (push) Successful in 16m16s
2025-02-13 13:31:40 -06:00
b8a25410d4 make sure golang is installed 2025-02-13 13:31:40 -06:00
85e067e7dc merge upstream 2025-02-13 19:27:13 +00:00
4afd82778a remove upstream tests
Some checks failed
Make release / release (push) Failing after 35m7s
2025-02-13 03:40:55 -06:00
84c95293e0 use gitea release-actions
Some checks failed
Email Editor Package Tests / build (7.4) (push) Has been cancelled
Email Editor Package Tests / build (8.2) (push) Has been cancelled
Email Editor Package Tests / code-style (push) Has been cancelled
Email Editor Package Tests / phpstan-static-analysis (7.4) (push) Has been cancelled
Email Editor Package Tests / phpstan-static-analysis (8.2) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Make release / release (push) Failing after 13m1s
2025-02-13 03:35:23 -06:00
f96e74cf96 Remove unnecessary early return in the RichTextWithButton component
MAILPOET-6431
2025-02-13 10:27:43 +01:00
ee513fd871 Add clarifying comments to sections of the codebase
MAILPOET-6431
2025-02-13 10:27:43 +01:00
2adc6d23ca Update acceptance test case to fix failing test
MAILPOET-6431
2025-02-13 10:27:43 +01:00
5023bbb6f2 Fix Incorrect use of <label for=FORM_ELEMENT notice
https://github.com/user-attachments/assets/9a996747-9ab1-453f-bd11-deffea36a468

MAILPOET-6431
2025-02-13 10:27:43 +01:00
b16071ee0e Remove unnecessary use of updateEmailMailPoetProperty
MAILPOET-6431
2025-02-13 10:27:43 +01:00
e5498b6baf Remove unnecessary use of mailpoet_data
MAILPOET-6431
2025-02-13 10:27:43 +01:00
9ad79e75b5 Update email editor mailpoet sidebar implementation
MAILPOET-6431
2025-02-13 10:27:43 +01:00
aa17e90365 Refactor RichTextWithButton component and ensure it is reusable
We also removed the usage of mailpoet_data

MAILPOET-6431
2025-02-13 10:27:43 +01:00
4b917dd1b3 Move current MailPoet specific information on the sidebar to an extension point
MAILPOET-6431
2025-02-13 10:27:43 +01:00
7c425cb04d Refactor send email preview component to remove hardcoded text and add point for extensibility
MAILPOET-6431
2025-02-13 10:27:43 +01:00
43990d9e8b Remove mailpoet_data use from send email preview feature
MAILPOET-6431
2025-02-13 10:27:43 +01:00
8024708c1d all tags cause the thing to build
Some checks failed
CodeQL / Analyze (javascript) (push) Waiting to run
Email Editor Package Tests / build (7.4) (push) Has been cancelled
Email Editor Package Tests / build (8.2) (push) Has been cancelled
Email Editor Package Tests / code-style (push) Has been cancelled
Email Editor Package Tests / phpstan-static-analysis (7.4) (push) Has been cancelled
Email Editor Package Tests / phpstan-static-analysis (8.2) (push) Has been cancelled
2025-02-13 03:06:53 -06:00
95a750365b remove test Run email-editor package integration tests
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
Email Editor Package Tests / code-style (push) Has been cancelled
Email Editor Package Tests / phpstan-static-analysis (7.4) (push) Has been cancelled
Email Editor Package Tests / phpstan-static-analysis (8.2) (push) Has been cancelled
Email Editor Package Tests / build (7.4) (push) Has been cancelled
Email Editor Package Tests / build (8.2) (push) Has been cancelled
2025-02-13 03:03:22 -06:00
2cee607749 add release building action
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 14m6s
Email Editor Package Tests / build (7.4) (push) Failing after 28m12s
Email Editor Package Tests / code-style (push) Successful in 6m48s
Email Editor Package Tests / build (8.2) (push) Failing after 27m41s
Email Editor Package Tests / phpstan-static-analysis (7.4) (push) Successful in 50m0s
Email Editor Package Tests / phpstan-static-analysis (8.2) (push) Successful in 45m35s
2025-02-12 23:27:20 -06:00
9c9193eeed Revert "add woodpecker for auto-building + releases"
Some checks failed
CodeQL / Analyze (javascript) (push) Failing after 10m17s
Email Editor Package Tests / build (8.2) (push) Failing after 31m17s
Email Editor Package Tests / code-style (push) Successful in 4m14s
Email Editor Package Tests / phpstan-static-analysis (7.4) (push) Successful in 45m11s
Email Editor Package Tests / build (7.4) (push) Failing after 2h2m50s
Email Editor Package Tests / phpstan-static-analysis (8.2) (push) Successful in 44m49s
This reverts commit 10c82b687d.
2025-02-12 19:43:11 -06:00
191 changed files with 1577 additions and 1206 deletions

View File

@ -197,10 +197,10 @@ jobs:
- run:
name: Download additional WP Plugins for tests
command: |
./do download:woo-commerce-zip 9.6.1
./do download:woo-commerce-subscriptions-zip 7.1.0
./do download:woo-commerce-zip 9.7.0
./do download:woo-commerce-subscriptions-zip 7.2.1
./do download:woo-commerce-memberships-zip 1.26.5
./do download:automate-woo-zip 6.1.5
./do download:automate-woo-zip 6.1.7
- run:
name: Dump tests ENV variables for acceptance tests
command: |
@ -888,6 +888,146 @@ jobs:
- store_artifacts:
path: tests/_output
qit_malware_scan:
executor: wpcli_php_latest
steps:
- attach_workspace:
at: /home/circleci
- run:
name: 'Set up environment'
command: |
# Copy built ZIP to local directory for easier access from QIT
cp /home/circleci/mailpoet/mailpoet.zip .
# Authenticate in QIT
./vendor/bin/qit partner:add --user="${QIT_PARTNER_USER}" --application_password="${QIT_PARTNER_SECRET}"
- run:
name: 'QIT Malware Scan'
command: ./do qa:qit-malware | tee tests/_output/qit-malware
- run:
name: 'Retrieve test results'
command: |
grep "Result Url" tests/_output/qit-malware | awk '{ print $3 }' | xargs curl -o tests/_output/report.html
when: always
- store_artifacts:
path: tests/_output
qit_php_compatibility:
executor: wpcli_php_latest
steps:
- attach_workspace:
at: /home/circleci
- run:
name: 'Set up environment'
command: |
# Copy built ZIP to local directory for easier access from QIT
cp /home/circleci/mailpoet/mailpoet.zip .
# Authenticate in QIT
./vendor/bin/qit partner:add --user="${QIT_PARTNER_USER}" --application_password="${QIT_PARTNER_SECRET}"
- run:
name: 'QIT PHP Compatibility Check'
command: ./do qa:qit-php-compatibility | tee tests/_output/qit-php-compatibility
- run:
name: 'Retrieve test results'
command: |
grep "Result Url" tests/_output/qit-php-compatibility | awk '{ print $3 }' | xargs curl -o tests/_output/report.html
when: always
- store_artifacts:
path: tests/_output
qit_activation_tests:
executor: wpcli_php_latest
steps:
- attach_workspace:
at: /home/circleci
- run:
name: 'Set up environment'
command: |
# Copy built ZIP to local directory for easier access from QIT
cp /home/circleci/mailpoet/mailpoet.zip .
# Authenticate in QIT
./vendor/bin/qit partner:add --user="${QIT_PARTNER_USER}" --application_password="${QIT_PARTNER_SECRET}"
- run:
name: 'QIT Activation Tests'
command: |
LATEST_WC_BETA=$(../.circleci/check_woocommerce_beta.sh | grep 'LATEST_BETA' | cut -d'=' -f2)
if [ -z "$LATEST_WC_BETA" ]; then
echo "No WooCommerce Beta version found. Using stable."
./do qa:qit-activation | tee tests/_output/qit-activation
else
echo "Using WooCommerce Beta Version: $LATEST_WC_BETA"
./do qa:qit-activation --wc=$LATEST_WC_BETA | tee tests/_output/qit-activation
fi
- run:
name: 'Retrieve test results'
command: |
grep "Result Url" tests/_output/qit-activation | awk '{ print $3 }' | xargs curl -o tests/_output/report.html
when: always
- store_artifacts:
path: tests/_output
qit_woo_api_tests:
executor: wpcli_php_latest
steps:
- attach_workspace:
at: /home/circleci
- run:
name: 'Set up environment'
command: |
# Copy built ZIP to local directory for easier access from QIT
cp /home/circleci/mailpoet/mailpoet.zip .
# Authenticate in QIT
./vendor/bin/qit partner:add --user="${QIT_PARTNER_USER}" --application_password="${QIT_PARTNER_SECRET}"
- run:
name: 'QIT Woo API Tests'
command: |
LATEST_WC_BETA=$(../.circleci/check_woocommerce_beta.sh | grep 'LATEST_BETA' | cut -d'=' -f2)
if [ -z "$LATEST_WC_BETA" ]; then
echo "No WooCommerce Beta version found. Using stable."
./do qa:qit-woo-api | tee tests/_output/qit-woo-api
else
echo "Using WooCommerce Beta Version: $LATEST_WC_BETA"
./do qa:qit-woo-api --wc=$LATEST_WC_BETA | tee tests/_output/qit-woo-api
fi
- run:
name: 'Retrieve test results'
command: |
grep "Result Url" tests/_output/qit-woo-api | awk '{ print $3 }' | xargs curl -o tests/_output/report.html
when: always
- store_artifacts:
path: tests/_output
qit_woo_e2e_tests:
executor: wpcli_php_latest
steps:
- attach_workspace:
at: /home/circleci
- run:
name: 'Set up environment'
command: |
# Copy built ZIP to local directory for easier access from QIT
cp /home/circleci/mailpoet/mailpoet.zip .
# Authenticate in QIT
./vendor/bin/qit partner:add --user="${QIT_PARTNER_USER}" --application_password="${QIT_PARTNER_SECRET}"
- run:
name: 'QIT Woo E2E Tests'
no_output_timeout: 1h # Woo E2E tests usually takes ~45m
command: |
LATEST_WC_BETA=$(../.circleci/check_woocommerce_beta.sh | grep 'LATEST_BETA' | cut -d'=' -f2)
if [ -z "$LATEST_WC_BETA" ]; then
echo "No WooCommerce Beta version found. Using stable."
./do qa:qit-woo-e2e | tee tests/_output/qit-woo-e2e
else
echo "Using WooCommerce Beta Version: $LATEST_WC_BETA"
./do qa:qit-woo-e2e --wc=$LATEST_WC_BETA | tee tests/_output/qit-woo-e2e
fi
- run:
name: 'Retrieve test results'
command: |
grep "Result Url" tests/_output/qit-woo-e2e | awk '{ print $3 }' | xargs curl -o tests/_output/report.html
when: always
- store_artifacts:
path: tests/_output
workflows:
build_and_test:
jobs:
@ -1082,8 +1222,8 @@ workflows:
- acceptance_tests:
<<: *slack-fail-post-step
name: acceptance_oldest
woo_core_version: 9.5.2
woo_subscriptions_version: 7.0.0
woo_core_version: 9.6.2
woo_subscriptions_version: 7.1.0
woo_memberships_version: 1.25.2
automate_woo_version: 6.0.33
mysql_command: --max_allowed_packet=100M
@ -1123,8 +1263,8 @@ workflows:
- integration_tests:
<<: *slack-fail-post-step
name: integration_oldest
woo_core_version: 9.5.2
woo_subscriptions_version: 7.0.0
woo_core_version: 9.6.2
woo_subscriptions_version: 7.1.0
woo_memberships_version: 1.25.2
automate_woo_version: 6.0.33
codeception_image_version: 7.4-cli_20220605.0
@ -1186,8 +1326,8 @@ workflows:
- acceptance_tests:
<<: *slack-fail-post-step
name: acceptance_with_premium_oldest
woo_core_version: 9.5.2
woo_subscriptions_version: 7.0.0
woo_core_version: 9.6.2
woo_subscriptions_version: 7.1.0
woo_memberships_version: 1.25.2
automate_woo_version: 6.0.33
codeception_image_version: 7.4-cli_20220605.0
@ -1198,8 +1338,8 @@ workflows:
- integration_tests:
<<: *slack-fail-post-step
name: integration_with_premium_oldest
woo_core_version: 9.5.2
woo_subscriptions_version: 7.0.0
woo_core_version: 9.6.2
woo_subscriptions_version: 7.1.0
woo_memberships_version: 1.25.2
automate_woo_version: 6.0.33
codeception_image_version: 7.4-cli_20220605.0
@ -1209,3 +1349,43 @@ workflows:
mysql_image: mysql:5.5
requires:
- build_premium
nightly_qit:
triggers:
- schedule:
cron: '0 2 * * 3' # Every Wednesday at 2am UTC
filters:
branches:
only:
- trunk
jobs:
- build:
<<: *slack-fail-post-step
- build_release_zip:
<<: *slack-fail-post-step
requires:
- build
- qit_security_scan:
<<: *slack-fail-post-step
requires:
- build_release_zip
- qit_activation_tests:
<<: *slack-fail-post-step
requires:
- build_release_zip
- qit_malware_scan:
<<: *slack-fail-post-step
requires:
- build_release_zip
- qit_php_compatibility:
<<: *slack-fail-post-step
requires:
- build_release_zip
- qit_woo_api_tests:
<<: *slack-fail-post-step
requires:
- build_release_zip
- qit_woo_e2e_tests:
<<: *slack-fail-post-step
requires:
- build_release_zip

View File

@ -1,71 +0,0 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: 'CodeQL'
on:
push:
branches: [trunk]
pull_request:
# The branches below must be a subset of the branches above
branches: [trunk]
schedule:
- cron: '0 17 * * 4'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['javascript']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1

View File

@ -1,212 +0,0 @@
name: Email Editor Package Tests
on:
push:
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['7.4', '8.2']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Composer vendor dependencies for MailPoet
id: composer-mailpoet-cache
uses: actions/cache@v4
with:
path: mailpoet/vendor
key: ${{ runner.os }}-composer-mailpoet-${{ matrix.php-version }}-${{ hashFiles('mailpoet/composer.lock') }}-${{ hashFiles('mailpoet/composer.json') }}
- name: Cache Composer vendor-prefixed dependencies for MailPoet
id: vendor-prefixed-cache
uses: actions/cache@v4
with:
path: mailpoet/vendor-prefixed
key: ${{ runner.os }}-vendor-prefixed-${{ matrix.php-version }}-${{ hashFiles('mailpoet/prefixer/composer.lock') }}-${{ hashFiles('mailpoet/prefixer/composer.json') }}
- name: Cache Composer vendor for test environment
id: composer-tests-env-cache
uses: actions/cache@v4
with:
path: tests_env/vendor
key: ${{ runner.os }}-composer-mailpoet-${{ matrix.php-version }}-${{ hashFiles('tests_env/composer.lock') }}-${{ hashFiles('tests_env/composer.json') }}
- name: Cache Composer dependencies for Email Editor
id: composer-email-editor-cache
uses: actions/cache@v4
with:
path: packages/php/email-editor/vendor
key: ${{ runner.os }}-composer-email-editor-${{ matrix.php-version }}-${{ hashFiles('packages/php/email-editor/composer.lock') }}-${{ hashFiles('packages/php/email-editor/composer.json') }}
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: gd
- name: Install tools
run: |
COMPOSER_DEV_MODE=1 php tools/install.php
touch .env
working-directory: mailpoet
# Install Test Environment dependencies only if the cache was not hit
- name: Install test environment dependencies
if: steps.composer-tests-env-cache.outputs.cache-hit != 'true'
run: ../mailpoet/tools/vendor/composer.phar install
working-directory: tests_env
# Install MailPoet dependencies only if the cache was not hit
- name: Install mailpoet dependencies
if: |
steps.composer-mailpoet-cache.outputs.cache-hit != 'true' || steps.vendor-prefixed-cache.outputs.cache-hit != 'true'
run: ./tools/vendor/composer.phar install
working-directory: mailpoet
# Install Email Editor dependencies only if the cache was not hit
- name: Install email-editor dependencies
if: steps.composer-email-editor-cache.outputs.cache-hit != 'true'
run: ../../../mailpoet/tools/vendor/composer.phar install
working-directory: packages/php/email-editor
# Dump Email Editor autoload
# This is needed to refresh classmap autoload when the composer cache is hit
- name: Dump email-editor autoload
run: ../../../mailpoet/tools/vendor/composer.phar dump-autoload
working-directory: packages/php/email-editor
# Dump MailPoet autoload
# This is needed to refresh classmap autoload when the composer cache is hit
- name: Dump MailPoet autoload
run: ./tools/vendor/composer.phar dump-autoload
working-directory: mailpoet
# Run Email Editor unit tests
- name: Run email-editor package unit tests
run: ../../../tests_env/vendor/bin/codecept build && ../../../mailpoet/tools/vendor/composer.phar unit-test
working-directory: packages/php/email-editor
- name: Run email-editor package integration tests
run: ../../../mailpoet/tools/vendor/composer.phar integration-test
working-directory: packages/php/email-editor
code-style:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- name: Install tools
run: |
COMPOSER_DEV_MODE=1 php tools/install.php
touch .env
working-directory: mailpoet
- name: Install composer dependencies
run: ../../tools/vendor/composer.phar install
working-directory: mailpoet/tasks/code_sniffer
- name: Run code style check
run: ../../../mailpoet/tools/vendor/composer.phar code-style
working-directory: packages/php/email-editor
phpstan-static-analysis:
runs-on: ubuntu-latest
strategy:
matrix:
php-version: ['7.4', '8.2']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Composer vendor dependencies for MailPoet
id: composer-mailpoet-cache
uses: actions/cache@v4
with:
path: mailpoet/vendor
key: ${{ runner.os }}-composer-mailpoet-${{ matrix.php-version }}-${{ hashFiles('mailpoet/composer.lock') }}-${{ hashFiles('mailpoet/composer.json') }}
- name: Cache Composer vendor-prefixed dependencies for MailPoet
id: vendor-prefixed-cache
uses: actions/cache@v4
with:
path: mailpoet/vendor-prefixed
key: ${{ runner.os }}-vendor-prefixed-${{ matrix.php-version }}-${{ hashFiles('mailpoet/prefixer/composer.lock') }}-${{ hashFiles('mailpoet/prefixer/composer.json') }}
- name: Cache Composer vendor for test environment
id: composer-tests-env-cache
uses: actions/cache@v4
with:
path: tests_env/vendor
key: ${{ runner.os }}-composer-mailpoet-${{ matrix.php-version }}-${{ hashFiles('tests_env/composer.lock') }}-${{ hashFiles('tests_env/composer.json') }}
- name: Cache Composer dependencies for Email Editor
id: composer-email-editor-cache
uses: actions/cache@v4
with:
path: packages/php/email-editor/vendor
key: ${{ runner.os }}-composer-email-editor-${{ matrix.php-version }}-${{ hashFiles('packages/php/email-editor/composer.lock') }}-${{ hashFiles('packages/php/email-editor/composer.json') }}
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
extensions: gd
- name: Install tools
run: |
COMPOSER_DEV_MODE=1 php tools/install.php
touch .env
working-directory: mailpoet
# Install Test Environment dependencies only if the cache was not hit
- name: Install test environment dependencies
if: steps.composer-tests-env-cache.outputs.cache-hit != 'true'
run: ../mailpoet/tools/vendor/composer.phar install
working-directory: tests_env
# Install MailPoet dependencies only if the cache was not hit
- name: Install mailpoet dependencies
if: |
steps.composer-mailpoet-cache.outputs.cache-hit != 'true' || steps.vendor-prefixed-cache.outputs.cache-hit != 'true'
run: ./tools/vendor/composer.phar install
working-directory: mailpoet
# Install Email Editor dependencies only if the cache was not hit
- name: Install email-editor dependencies
if: steps.composer-email-editor-cache.outputs.cache-hit != 'true'
run: ../../../mailpoet/tools/vendor/composer.phar install
working-directory: packages/php/email-editor
- name: Install composer dependencies
run: ../../tools/vendor/composer.phar install
working-directory: mailpoet/tasks/phpstan
# Dump Email Editor autoload
# This is needed to refresh classmap autoload when the composer cache is hit
- name: Dump email-editor autoload
run: ../../../mailpoet/tools/vendor/composer.phar dump-autoload
working-directory: packages/php/email-editor
# Dump MailPoet autoload
# This is needed to refresh classmap autoload when the composer cache is hit
- name: Dump MailPoet autoload
run: ./tools/vendor/composer.phar dump-autoload
working-directory: mailpoet
- name: Run code phpstan
run: ../../../mailpoet/tools/vendor/composer.phar phpstan -- --php-version=${{ matrix.php-version == '7.4' && '70400' || '80200' }}
working-directory: packages/php/email-editor

39
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,39 @@
name: Make release
on:
push:
tags:
- '*' # Trigger on any tag push
env:
NODE_OPTIONS: "--max-old-space-size=8192"
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Pre-reqs
run: |
sudo apt update
sudo apt install -y php php-symfony composer php-cli php-gd php-mysql golang npm
npm install -g pnpm
- name: Build zip file
run: |
cd mailpoet || { echo "Directory 'mailpoet' not found"; exit 1; }
cp .env.sample .env
composer update
sh build.sh
mkdir -p ../output
mv mailpoet.zip ../output
- name: Upload Release
uses: https://gitea.com/actions/release-action@main
with:
files: |-
output/mailpoet.zip
api_key: '${{secrets.RELEASE_SMITH_TOKEN}}'

View File

@ -1,182 +0,0 @@
name: Check new versions of plugins and WordPress
on:
schedule:
- cron: '0 6 * * 1' # At 06:00 on Monday
workflow_dispatch: # Allows manual triggering
jobs:
check-versions:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3' # Specify the PHP version you want to use
# Updating used WordPress
- name: Check WordPress Docker Versions
run: php .github/workflows/scripts/check_wordpress_versions.php
- name: Check for WordPress changes
id: check_wp_changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
if [ "$(git status --porcelain)" != "" ]; then
echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
echo "WORDPRESS_CHANGES=true" >> $GITHUB_ENV
fi
- name: Get WordPress versions from files
id: get_wp_versions
run: |
echo "WORDPRESS_LATEST_VERSION=$(cat /tmp/latest_wordpress_version.txt)" >> $GITHUB_ENV
echo "WORDPRESS_PREVIOUS_VERSION=$(cat /tmp/previous_wordpress_version.txt)" >> $GITHUB_ENV
- name: Commit WordPress changes
if: env.WORDPRESS_CHANGES == 'true'
run: |
git add .
git commit -m $'Update used WordPress images in Circle CI\n\n - latest version: ${{ env.WORDPRESS_LATEST_VERSION }}\n - previous version: ${{ env.WORDPRESS_PREVIOUS_VERSION }}'
# Updating used WooCommerce plugin
- name: Check WooCommerce Versions
run: php .github/workflows/scripts/check_woocommerce_versions.php
- name: Check for WooCommerce changes
id: check_wc_changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
if [ "$(git status --porcelain)" != "" ]; then
echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
echo "WOOCOMMERCE_CHANGES=true" >> $GITHUB_ENV
fi
- name: Get WooCommerce versions from files
id: get_wc_versions
run: |
echo "WOOCOMMERCE_LATEST_VERSION=$(cat /tmp/latest_woocommerce_version.txt)" >> $GITHUB_ENV
echo "WOOCOMMERCE_PREVIOUS_VERSION=$(cat /tmp/previous_woocommerce_version.txt)" >> $GITHUB_ENV
- name: Commit WooCommerce changes
if: env.WOOCOMMERCE_CHANGES == 'true'
run: |
git add .
git commit -m $'Update used WooCommerce plugin in Circle CI\n\n - latest version: ${{ env.WOOCOMMERCE_LATEST_VERSION }}\n - previous version: ${{ env.WOOCOMMERCE_PREVIOUS_VERSION }}'
# Updating used Automate Woo plugin
- name: Check Automate Woo Versions
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: php .github/workflows/scripts/check_automate_woo_versions.php
- name: Check for Automate Woo changes
id: check_aw_changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
if [ "$(git status --porcelain)" != "" ]; then
echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
echo "AUTOMATE_WOO_CHANGES=true" >> $GITHUB_ENV
fi
- name: Get Automate Woo versions from files
id: get_aw_versions
run: |
echo "AUTOMATE_WOO_LATEST_VERSION=$(cat /tmp/latest_automate_woo_version.txt)" >> $GITHUB_ENV
echo "AUTOMATE_WOO_PREVIOUS_VERSION=$(cat /tmp/previous_automate_woo_version.txt)" >> $GITHUB_ENV
- name: Commit Automate Woo changes
if: env.AUTOMATE_WOO_CHANGES == 'true'
run: |
git add .
git commit -m $'Update used Automate Woo plugin in Circle CI\n\n - latest version: ${{ env.AUTOMATE_WOO_LATEST_VERSION }}\n - previous version: ${{ env.AUTOMATE_WOO_PREVIOUS_VERSION }}'
# Updating used WooCommerce Subscriptions plugin
- name: Check WooCommerce Subscriptions Versions
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: php .github/workflows/scripts/check_woocommerce_subscriptions_versions.php
- name: Check for WooCommerce Subscriptions changes
id: check_ws_changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
if [ "$(git status --porcelain)" != "" ]; then
echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
echo "SUBSCRIPTIONS_CHANGES=true" >> $GITHUB_ENV
fi
- name: Get WooCommerce Subscriptions versions from files
id: get_ws_versions
run: |
echo "WOOCOMMERCE_SUBSCRIPTIONS_LATEST_VERSION=$(cat /tmp/latest_woocommerce_subscriptions_version.txt)" >> $GITHUB_ENV
echo "WOOCOMMERCE_SUBSCRIPTIONS_PREVIOUS_VERSION=$(cat /tmp/previous_woocommerce_subscriptions_version.txt)" >> $GITHUB_ENV
- name: Commit WooCommerce Subscriptions changes
if: env.SUBSCRIPTIONS_CHANGES == 'true'
run: |
git add .
git commit -m $'Update used WooCommerce Subscriptions plugin in Circle CI\n\n - latest version: ${{ env.WOOCOMMERCE_SUBSCRIPTIONS_LATEST_VERSION }}\n - previous version: ${{ env.WOOCOMMERCE_SUBSCRIPTIONS_PREVIOUS_VERSION }}'
# Updating used WooCommerce Memberships plugin
- name: Check WooCommerce Memberships Versions
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: php .github/workflows/scripts/check_woocommerce_memberships_versions.php
- name: Check for WooCommerce Memberships changes
id: check_wm_changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
if [ "$(git status --porcelain)" != "" ]; then
echo "CHANGES_DETECTED=true" >> $GITHUB_ENV
echo "MEMBERSHIPS_CHANGES=true" >> $GITHUB_ENV
fi
- name: Get WooCommerce Memberships versions from files
id: get_wm_versions
run: |
echo "WOOCOMMERCE_MEMBERSHIPS_LATEST_VERSION=$(cat /tmp/latest_woocommerce_memberships_version.txt)" >> $GITHUB_ENV
echo "WOOCOMMERCE_MEMBERSHIPS_PREVIOUS_VERSION=$(cat /tmp/previous_woocommerce_memberships_version.txt)" >> $GITHUB_ENV
- name: Commit WooCommerce Memberships changes
if: env.MEMBERSHIPS_CHANGES == 'true'
run: |
git add .
git commit -m $'Update used WooCommerce Memberships plugin in Circle CI\n\n - latest version: ${{ env.WOOCOMMERCE_MEMBERSHIPS_LATEST_VERSION }}\n - previous version: ${{ env.WOOCOMMERCE_MEMBERSHIPS_PREVIOUS_VERSION }}'
# Push all changes at the end if any changes were detected
#
# For local testing with act tool add following:
# env:
# GH_PAT: ${{ secrets.GH_TOKEN }}
# run: |
# git remote set-url origin https://${GH_PAT}@github.com/mailpoet/mailpoet
# git push -f origin HEAD:refs/heads/update-plugins-and-wordpress-test
- name: Push changes
if: env.CHANGES_DETECTED == 'true'
run: |
git push -f origin HEAD:refs/heads/update-plugins-and-wordpress
# Create a pull request if there are changes
- name: Create Pull Request
if: env.CHANGES_DETECTED == 'true'
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GH_TOKEN }}
branch: update-plugins-and-wordpress
title: Update WordPress and plugins in CI jobs
base: trunk
labels: automated, check-versions
body: |
1. If all checks passed, you can merge this PR.
2. If the build failed, please investigate the failure and either address the issues or delegate the job. Then, make sure these changes are merged.

View File

@ -1,27 +0,0 @@
name: Add link to WordPress Playground preview
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
add-wp-playground-link:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Check and append description
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER="${{ github.event.pull_request.number }}"
BRANCH_NAME="${{ github.head_ref }}"
DESCRIPTION="$(gh pr view $PR_NUMBER --json body -q .body)"
HEADING="## Preview"
CONTENT="$(printf "${HEADING}\n\n[Preview in WordPress Playground](https://account.mailpoet.com/playground/new/branch:${BRANCH_NAME})\n\n_The latest successful build from \`${BRANCH_NAME}\` will be used. If none is available, the link won't work._")"
if [[ "$DESCRIPTION" != *"$HEADING"* ]]; then
gh pr edit $PR_NUMBER --body "$(printf "${DESCRIPTION}\n\n${CONTENT}")"
fi

View File

@ -1,30 +0,0 @@
clone:
git:
image: woodpeckerci/plugin-git
settings:
depth: 1
steps:
build:
image: node:current-bookworm-slim
commands:
- apt update
- apt install php php-symfony bash -y
- npm install pnpm
- cd mailpoet
- bash build.sh
- mkdir ../output
- mv mailpoet.zip ../output
- cd ..
release:
image: woodpeckerci/plugin-gitea-release:latest
settings:
base_url: https://git.cavemanon.xyz
api_key:
from_secret: releasesmithapikey
files: "output/"
prerelease: false
title: "${CI_COMMIT_TAG}"
when:
- event: tag

View File

@ -815,6 +815,47 @@ class RoboFile extends \Robo\Tasks {
return $this->_exec('./vendor/bin/qit run:security mailpoet --zip=mailpoet.zip --wait');
}
public function qaQitMalware() {
return $this->_exec('./vendor/bin/qit run:malware mailpoet --zip=mailpoet.zip --wait');
}
public function qaQitPhpCompatibility() {
return $this->_exec('./vendor/bin/qit run:phpcompatibility mailpoet --zip=mailpoet.zip --wait');
}
public function qaQitActivation($opts = ['wp' => 'stable', 'wc' => 'stable']) {
$command = './vendor/bin/qit run:activation mailpoet --zip=mailpoet.zip --wait';
if ($opts['wp']) {
$command .= ' --wordpress_version=' . $opts['wp'];
}
if ($opts['wc']) {
$command .= ' --woocommerce_version=' . $opts['wc'];
}
return $this->_exec($command);
}
public function qaQitWooApi($opts = ['wp' => 'stable', 'wc' => 'stable']) {
$command = './vendor/bin/qit run:woo-api mailpoet --zip=mailpoet.zip --wait';
if ($opts['wp']) {
$command .= ' --wordpress_version=' . $opts['wp'];
}
if ($opts['wc']) {
$command .= ' --woocommerce_version=' . $opts['wc'];
}
return $this->_exec($command);
}
public function qaQitWooE2e($opts = ['wp' => 'stable', 'wc' => 'stable']) {
$command = './vendor/bin/qit run:woo-e2e mailpoet --zip=mailpoet.zip --wait';
if ($opts['wp']) {
$command .= ' --wordpress_version=' . $opts['wp'];
}
if ($opts['wc']) {
$command .= ' --woocommerce_version=' . $opts['wc'];
}
return $this->_exec($command);
}
public function svnCheckout() {
$svnDir = ".mp_svn";
@ -1342,7 +1383,7 @@ class RoboFile extends \Robo\Tasks {
$this->say("Release '$version[name]' info was published on Slack.");
}
public function releaseRerunCircleWorkflow(string $project = null) {
public function releaseRerunCircleWorkflow(?string $project = null) {
$circleciController = $this->createCircleCiController();
$result = $circleciController->rerunLatestWorkflow($project);
// Sometimes can be useful to know which Circle project workflow was restarted

View File

@ -39,13 +39,13 @@ interface JQuery {
declare module '@woocommerce/blocks-checkout' {
type CheckboxControlProps = {
className?: string;
label?: string;
label?: string | React.ReactNode;
id?: string;
instanceId?: string;
onChange?: (value: boolean) => void;
children?: React.ReactChildren | React.ReactElement;
onChange: (value: boolean) => void;
children?: React.ReactChildren;
hasError?: boolean;
checked?: boolean;
disabled?: string | boolean | undefined;
};
export const CheckboxControl: (props: CheckboxControlProps) => JSX.Element;
}

View File

@ -34,7 +34,10 @@ class ListingComponent extends Component {
if (autoRefresh) {
jQuery(document).on('heartbeat-tick.mailpoet', () => {
this.getItems();
// Skip auto-refresh if any items are selected for bulk editing
if (this.state.selected_ids.length === 0) {
this.getItems();
}
});
}

View File

@ -0,0 +1,142 @@
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} />;
}

View File

@ -5,6 +5,7 @@
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';
@ -12,10 +13,12 @@ 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',
(validationRules: []) => [...validationRules, ...useValidationRules()],
() => useValidationRules(), // returns a memorized set of rules (array of rules)
);
const EVENTS_TO_TRACK = [
@ -53,3 +56,22 @@ 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
},
);

View File

@ -64,12 +64,14 @@ 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',

View File

@ -30,8 +30,10 @@ export function FrontendBlock({
}
return (
<CheckboxControl checked={checked} onChange={setChecked}>
<RawHTML>{text || defaultText}</RawHTML>
</CheckboxControl>
<CheckboxControl
checked={checked}
onChange={setChecked}
label={<RawHTML>{text || defaultText}</RawHTML>}
/>
);
}

View File

@ -56,7 +56,11 @@ export function Edit({
<div {...blockProps}>
{optinEnabled ? (
<div className="wc-block-checkout__marketing">
<CheckboxControl id="mailpoet-marketing-optin" checked={false} />
<CheckboxControl
id="mailpoet-marketing-optin"
checked={false}
onChange={() => {}}
/>
<RichText
value={currentText}
onChange={(value) => setAttributes({ text: value })}

View File

@ -19,6 +19,7 @@ Module.CouponBlockModel = base.BlockModel.extend({
return this._getDefaults(
{
isStandardEmail: App.getNewsletter().isStandardEmail(),
isAutomationEmail: App.getNewsletter().isAutomationEmail(),
productIds: [], // selected product ids,
excludedProductIds: [],
productCategoryIds: [], // selected categories id
@ -33,6 +34,10 @@ Module.CouponBlockModel = base.BlockModel.extend({
minimumAmount: '',
maximumAmount: '',
emailRestrictions: '',
restrictToSubscriber: false,
showRestrictToSubscriber:
App.getNewsletter().isAutomationEmail() ||
App.getNewsletter().isWelcomeEmail(),
styles: {
block: {
backgroundColor: '#ffffff',

View File

@ -31,6 +31,7 @@ type State = {
productCategoryIds: Post[];
excludedProductCategoryIds: Post[];
emailRestrictions: string;
restrictToSubscriber: boolean;
};
class UsageRestriction extends Component<Props, State> {
@ -62,6 +63,8 @@ class UsageRestriction extends Component<Props, State> {
'excludedProductCategoryIds',
).toJSON() as Post[],
emailRestrictions: this.getValueCallback('emailRestrictions') as string,
restrictToSubscriber:
(this.getValueCallback('restrictToSubscriber') as boolean) || false,
};
}
@ -262,6 +265,25 @@ class UsageRestriction extends Component<Props, State> {
)}
/>
</PanelRow>
{this.getValueCallback('showRestrictToSubscriber') && (
<PanelRow>
<ToggleControl
checked={this.state.restrictToSubscriber}
label={__('Restrict to subscriber email', 'mailpoet')}
onChange={(restrictToSubscriber) => {
this.setValueCallback(
'restrictToSubscriber',
restrictToSubscriber,
);
this.setState({ restrictToSubscriber });
}}
help={__(
'Restrict coupon usage to the subscriber receiving this email.',
'mailpoet',
)}
/>
</PanelRow>
)}
</PanelBody>
</Panel>
);

View File

@ -46,6 +46,18 @@ const BlockView = BaseBlock.BlockView.extend({
});
},
getTemplate() {
if (window.mailpoet_woocommerce_email_improvements_enabled) {
if (this.model.get('selected') === 'new_account') {
return window.templates.woocommerceNewAccountImproved;
}
if (this.model.get('selected') === 'processing_order') {
return window.templates.woocommerceProcessingOrderImproved;
}
if (this.model.get('selected') === 'completed_order') {
return window.templates.woocommerceCompletedOrderImproved;
}
return window.templates.woocommerceCustomerNoteImproved;
}
if (this.model.get('selected') === 'new_account') {
return window.templates.woocommerceNewAccount;
}

View File

@ -35,6 +35,9 @@ Module.NewsletterModel = SuperModel.extend({
isStandardEmail: function isStandardEmail() {
return this.get('type') === NewsletterType.Standard;
},
isWelcomeEmail: function isWelcomeEmail() {
return this.get('type') === NewsletterType.Welcome;
},
});
// Content block view and model handlers for different content types

View File

@ -57,17 +57,17 @@ echo '[BUILD] Fetching prefixed production libraries'
# Remove Doctrinne Annotations (no need since generated metadata are packed)
# Should be removed before `dump-autoload` to not include the annotations classes on the autoloader.
rm -rf vendor-prefixed/doctrine/annotations
#rm -rf vendor-prefixed/doctrine/annotations
# Remove DI Container files used for container dump (no need since generated metadata are packed)
# Should be removed before `dump-autoload` to not include these classes in the autoloader.
echo '[BUILD] Removing DI Container development dependencies'
rm -rf vendor-prefixed/symfony/dependency-injection/Compiler
rm -rf vendor-prefixed/symfony/dependency-injection/Config
rm -rf vendor-prefixed/symfony/dependency-injection/Dumper
rm -rf vendor-prefixed/symfony/dependency-injection/Loader
rm -rf vendor-prefixed/symfony/dependency-injection/LazyProxy
rm -rf vendor-prefixed/symfony/dependency-injection/Extension
#rm -rf vendor-prefixed/symfony/dependency-injection/Compiler
#rm -rf vendor-prefixed/symfony/dependency-injection/Config
#rm -rf vendor-prefixed/symfony/dependency-injection/Dumper
#rm -rf vendor-prefixed/symfony/dependency-injection/Loader
#rm -rf vendor-prefixed/symfony/dependency-injection/LazyProxy
#rm -rf vendor-prefixed/symfony/dependency-injection/Extension
./tools/vendor/composer.phar dump-autoload

View File

@ -1,5 +1,17 @@
== Changelog ==
= 5.8.1 - 2025-03-03 =
* Changed: minimum required WooCommerce is 9.6.
= 5.8.0 - 2025-02-24 =
* Added: allow generating coupon code in automations for a subscriber the email is sent to;
* Changed: default MailPoet pages capability changed from post to page (one of improvements is hidden previous/next post links);
* Fixed: Prevent removing the content block from the Newsletter template in the new editor.
= 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;

View File

@ -5,7 +5,7 @@
"dragonmantank/cron-expression": "^3.3",
"mailpoet/email-editor": "*",
"mixpanel/mixpanel-php": "2.*",
"woocommerce/action-scheduler": "^3.8.0"
"woocommerce/action-scheduler": "3.9.2"
},
"require-dev": {
"ext-gd": "*",

18
mailpoet/composer.lock generated
View File

@ -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": "51893b0f5ed38d130932b86b48e55b94",
"content-hash": "a95263a51332277e75d7147e3dc4551e",
"packages": [
{
"name": "dragonmantank/cron-expression",
@ -221,20 +221,20 @@
},
{
"name": "woocommerce/action-scheduler",
"version": "3.8.0",
"version": "3.9.2",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/action-scheduler.git",
"reference": "99cd7981f51c98883082534d4852491858d72834"
"reference": "efbb7953f72a433086335b249292f280dd43ddfe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/99cd7981f51c98883082534d4852491858d72834",
"reference": "99cd7981f51c98883082534d4852491858d72834",
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/efbb7953f72a433086335b249292f280dd43ddfe",
"reference": "efbb7953f72a433086335b249292f280dd43ddfe",
"shasum": ""
},
"require": {
"php": ">=5.6"
"php": ">=7.1"
},
"require-dev": {
"phpunit/phpunit": "^7.5",
@ -258,9 +258,9 @@
"homepage": "https://actionscheduler.org/",
"support": {
"issues": "https://github.com/woocommerce/action-scheduler/issues",
"source": "https://github.com/woocommerce/action-scheduler/tree/3.8.0"
"source": "https://github.com/woocommerce/action-scheduler/tree/3.9.2"
},
"time": "2024-05-22T13:50:29+00:00"
"time": "2025-02-03T09:09:30+00:00"
}
],
"packages-dev": [
@ -8343,7 +8343,7 @@
],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {

View File

@ -158,7 +158,7 @@ class NewslettersResponseBuilder {
* @param SendingQueueEntity|null $latestQueue
* @return array<string, mixed>
*/
private function buildListingItem(NewsletterEntity $newsletter, NewsletterStatistics $statistics = null, SendingQueueEntity $latestQueue = null): array {
private function buildListingItem(NewsletterEntity $newsletter, ?NewsletterStatistics $statistics = null, ?SendingQueueEntity $latestQueue = null): array {
$couponBlockLogs = array_map(function ($item) {
return "Coupon block: $item";
}, $this->logRepository->getRawMessagesForNewsletter($newsletter, LoggerFactory::TOPIC_COUPONS));

View File

@ -6,7 +6,7 @@ use WP_REST_Response;
class Response extends WP_REST_Response {
public function __construct(
array $data = null,
?array $data = null,
int $status = 200
) {
parent::__construct(['data' => $data], $status);

View File

@ -107,7 +107,7 @@ class Help {
return $actionSchedulerData;
}
private function getLatestActionSchedulerActionDate(string $hook, string $status = null): ?string {
private function getLatestActionSchedulerActionDate(string $hook, ?string $status = null): ?string {
$query = [
'per_page' => 1,
'order' => 'DESC',

View File

@ -36,7 +36,7 @@ class FirstPurchase {
private $subscribersRepository;
public function __construct(
WCHelper $helper = null
?WCHelper $helper = null
) {
if ($helper === null) {
$helper = ContainerWrapper::getInstance()->get(WCHelper::class);

View File

@ -34,7 +34,7 @@ class PurchasedInCategory {
private $subscribersRepository;
public function __construct(
WCHelper $woocommerceHelper = null
?WCHelper $woocommerceHelper = null
) {
if ($woocommerceHelper === null) {
$woocommerceHelper = ContainerWrapper::getInstance()->get(WCHelper::class);

View File

@ -35,7 +35,7 @@ class PurchasedProduct {
private $subscribersRepository;
public function __construct(
WCHelper $helper = null
?WCHelper $helper = null
) {
if ($helper === null) {
$helper = ContainerWrapper::getInstance()->get(WCHelper::class);

View File

@ -25,15 +25,15 @@ class StepRunController {
$this->stepRunLogger = $stepRunLogger;
}
public function scheduleProgress(int $timestamp = null): int {
public function scheduleProgress(?int $timestamp = null): int {
return $this->stepScheduler->scheduleProgress($this->stepRunArgs, $timestamp);
}
public function scheduleNextStep(int $timestamp = null): int {
public function scheduleNextStep(?int $timestamp = null): int {
return $this->stepScheduler->scheduleNextStep($this->stepRunArgs, $timestamp);
}
public function scheduleNextStepByIndex(int $nextStepIndex, int $timestamp = null): int {
public function scheduleNextStepByIndex(int $nextStepIndex, ?int $timestamp = null): int {
return $this->stepScheduler->scheduleNextStepByIndex($this->stepRunArgs, $nextStepIndex, $timestamp);
}

View File

@ -42,7 +42,7 @@ class StepRunLogger {
string $stepId,
string $stepType,
int $runNumber,
bool $isWpDebug = null
?bool $isWpDebug = null
) {
$this->automationRunLogStorage = $automationRunLogStorage;
$this->hooks = $hooks;

View File

@ -23,13 +23,13 @@ class StepScheduler {
$this->automationRunStorage = $automationRunStorage;
}
public function scheduleProgress(StepRunArgs $args, int $timestamp = null): int {
public function scheduleProgress(StepRunArgs $args, ?int $timestamp = null): int {
$runId = $args->getAutomationRun()->getId();
$data = $this->getActionData($runId, $args->getStep()->getId(), $args->getRunNumber() + 1);
return $this->scheduleStepAction($data, $timestamp);
}
public function scheduleNextStep(StepRunArgs $args, int $timestamp = null): int {
public function scheduleNextStep(StepRunArgs $args, ?int $timestamp = null): int {
$step = $args->getStep();
$nextSteps = $step->getNextSteps();
@ -46,7 +46,7 @@ class StepScheduler {
return $this->scheduleNextStepByIndex($args, 0, $timestamp);
}
public function scheduleNextStepByIndex(StepRunArgs $args, int $nextStepIndex, int $timestamp = null): int {
public function scheduleNextStepByIndex(StepRunArgs $args, int $nextStepIndex, ?int $timestamp = null): int {
$step = $args->getStep();
$nextStep = $step->getNextSteps()[$nextStepIndex] ?? null;
if (!$nextStep) {
@ -95,7 +95,7 @@ class StepScheduler {
return $this->hasScheduledNextStep($args) || $this->hasScheduledProgress($args);
}
private function scheduleStepAction(array $data, int $timestamp = null): int {
private function scheduleStepAction(array $data, ?int $timestamp = null): int {
return $timestamp === null
? $this->actionScheduler->enqueue(Hooks::AUTOMATION_STEP, $data)
: $this->actionScheduler->schedule($timestamp, Hooks::AUTOMATION_STEP, $data);

View File

@ -53,8 +53,8 @@ class Automation {
string $name,
array $steps,
\WP_User $author,
int $id = null,
int $versionId = null
?int $id = null,
?int $versionId = null
) {
$this->name = $name;
$this->steps = $steps;

View File

@ -42,7 +42,7 @@ class AutomationRun {
int $versionId,
string $triggerKey,
array $subjects,
int $id = null
?int $id = null
) {
$this->automationId = $automationId;
$this->versionId = $versionId;
@ -91,7 +91,7 @@ class AutomationRun {
}
/** @return Subject[] */
public function getSubjects(string $key = null): array {
public function getSubjects(?string $key = null): array {
if ($key) {
return array_values(
array_filter($this->subjects, function (Subject $subject) use ($key) {

View File

@ -60,7 +60,7 @@ class AutomationRunLog {
int $automationRunId,
string $stepId,
string $stepType,
int $id = null
?int $id = null
) {
$this->automationRunId = $automationRunId;
$this->stepId = $stepId;

View File

@ -35,7 +35,7 @@ class Step {
string $key,
array $args,
array $nextSteps,
Filters $filters = null
?Filters $filters = null
) {
$this->id = $id;
$this->type = $type;

View File

@ -20,16 +20,16 @@ abstract class Exception extends PhpException implements RestException {
protected $errors = [];
final public function __construct(
string $message = null,
string $errorCode = null,
Throwable $previous = null
?string $message = null,
?string $errorCode = null,
?Throwable $previous = null
) {
parent::__construct($message ?? __('Unknown error.', 'mailpoet'), 0, $previous);
$this->errorCode = $errorCode ?? 'mailpoet_automation_unknown_error';
}
/** @return static */
public static function create(Throwable $previous = null) {
public static function create(?Throwable $previous = null) {
return new static(null, null, $previous);
}

View File

@ -19,7 +19,7 @@ class AutomationMapper {
$this->statisticsStorage = $statisticsStorage;
}
public function buildAutomation(Automation $automation, AutomationStatistics $statistics = null): array {
public function buildAutomation(Automation $automation, ?AutomationStatistics $statistics = null): array {
return [
'id' => $automation->getId(),

View File

@ -98,7 +98,7 @@ class Registry {
}
/** @return array<string, AutomationTemplate> */
public function getTemplates(string $category = null): array {
public function getTemplates(?string $category = null): array {
return $category
? array_filter(
$this->templates,
@ -254,7 +254,7 @@ class Registry {
$this->wordPress->addAction(Hooks::AUTOMATION_BEFORE_SAVE, $callback, $priority);
}
public function onBeforeAutomationStepSave(callable $callback, string $key = null, int $priority = 10): void {
public function onBeforeAutomationStepSave(callable $callback, ?string $key = null, int $priority = 10): void {
$keyPart = $key ? "/key=$key" : '';
$this->wordPress->addAction(Hooks::AUTOMATION_STEP_BEFORE_SAVE . $keyPart, $callback, $priority, 2);
}

View File

@ -32,7 +32,7 @@ class AutomationRunLogStorage {
}
}
public function getAutomationRunStatisticsForAutomationInTimeFrame(int $automationId, string $status, \DateTimeImmutable $after, \DateTimeImmutable $before, int $versionId = null): array {
public function getAutomationRunStatisticsForAutomationInTimeFrame(int $automationId, string $status, \DateTimeImmutable $after, \DateTimeImmutable $before, ?int $versionId = null): array {
global $wpdb;
$andWhere = $versionId ? 'AND run.version_id = %d' : '';
$results = $wpdb->get_results(

View File

@ -218,7 +218,7 @@ class AutomationRunStorage {
}
}
public function getAutomationStepStatisticForTimeFrame(int $automationId, string $status, \DateTimeImmutable $after, \DateTimeImmutable $before, int $versionId = null): array {
public function getAutomationStepStatisticForTimeFrame(int $automationId, string $status, \DateTimeImmutable $after, \DateTimeImmutable $before, ?int $versionId = null): array {
global $wpdb;
$andWhere = $versionId ? 'AND version_id = %d' : '';
$result = $wpdb->get_results(

View File

@ -39,7 +39,7 @@ class AutomationStatisticsStorage {
return $statistics;
}
public function getAutomationStats(int $automationId, int $versionId = null, \DateTimeImmutable $after = null, \DateTimeImmutable $before = null): AutomationStatistics {
public function getAutomationStats(int $automationId, ?int $versionId = null, ?\DateTimeImmutable $after = null, ?\DateTimeImmutable $before = null): AutomationStatistics {
$data = $this->getStatistics([$automationId], $versionId, $after, $before);
return new AutomationStatistics(
$automationId,
@ -53,7 +53,7 @@ class AutomationStatisticsStorage {
* @param int[] $automationIds
* @return array<int, array{id: int, total: int, running: int}>
*/
private function getStatistics(array $automationIds, int $versionId = null, \DateTimeImmutable $after = null, \DateTimeImmutable $before = null): array {
private function getStatistics(array $automationIds, ?int $versionId = null, ?\DateTimeImmutable $after = null, ?\DateTimeImmutable $before = null): array {
global $wpdb;
$totalSubquery = $this->getStatsQuery($automationIds, $versionId, $after, $before);
$runningSubquery = $this->getStatsQuery($automationIds, $versionId, $after, $before, AutomationRun::STATUS_RUNNING);
@ -71,7 +71,7 @@ class AutomationStatisticsStorage {
return array_combine(array_column($results, 'id'), $results) ?: [];
}
private function getStatsQuery(array $automationIds, int $versionId = null, \DateTimeImmutable $after = null, \DateTimeImmutable $before = null, string $status = null): string {
private function getStatsQuery(array $automationIds, ?int $versionId = null, ?\DateTimeImmutable $after = null, ?\DateTimeImmutable $before = null, ?string $status = null): string {
global $wpdb;
$versionCondition = $versionId ? 'AND version_id = %d' : '';

View File

@ -126,7 +126,7 @@ class AutomationStorage {
) : [];
}
public function getAutomation(int $automationId, int $versionId = null): ?Automation {
public function getAutomation(int $automationId, ?int $versionId = null): ?Automation {
global $wpdb;
if ($versionId) {
@ -153,7 +153,7 @@ class AutomationStorage {
}
/** @return Automation[] */
public function getAutomations(array $status = null): array {
public function getAutomations(?array $status = null): array {
global $wpdb;
$statusFilter = $status ? 'AND a.status IN (' . implode(',', array_fill(0, count($status), '%s')) . ')' : '';
@ -184,7 +184,7 @@ class AutomationStorage {
}
/** @return int[] */
public function getAutomationIdsBySubject(Subject $subject, array $runStatus = null, int $inTheLastSeconds = null): array {
public function getAutomationIdsBySubject(Subject $subject, ?array $runStatus = null, ?int $inTheLastSeconds = null): array {
global $wpdb;
$statusFilter = $runStatus ? 'AND r.status IN (' . implode(',', array_fill(0, count($runStatus), '%s')) . ')' : '';

View File

@ -68,7 +68,7 @@ class WordPress {
}
/** @return WP_Post[]|int[] */
public function getPosts(array $args = null): array {
public function getPosts(?array $args = null): array {
return get_posts($args);
}

View File

@ -423,7 +423,7 @@ class SendEmailAction implements Action {
$this->newslettersRepository->flush();
}
private function storeNewsletterOption(NewsletterEntity $newsletter, string $optionName, string $optionValue = null): void {
private function storeNewsletterOption(NewsletterEntity $newsletter, string $optionName, ?string $optionValue = null): void {
$options = $newsletter->getOptions()->toArray();
foreach ($options as $key => $option) {
if ($option->getName() === $optionName) {

View File

@ -41,7 +41,7 @@ class Query {
string $orderDirection = 'asc',
int $page = 1,
array $filter = [],
string $search = null
?string $search = null
) {
$this->primaryAfter = $primaryAfter;
$this->primaryBefore = $primaryBefore;

View File

@ -25,7 +25,7 @@ class QueryWithCompare extends Query {
string $orderDirection = 'asc',
int $page = 0,
array $filter = [],
string $search = null
?string $search = null
) {
parent::__construct($primaryAfter, $primaryBefore, $limit, $orderBy, $orderDirection, $page, $filter, $search);
$this->secondaryAfter = $secondaryAfter;

View File

@ -66,7 +66,7 @@ class SubscriberAutomationFieldsFactory {
];
}
private function getAutomationIds(SubscriberPayload $payload, array $status = null, array $params = []): array {
private function getAutomationIds(SubscriberPayload $payload, ?array $status = null, array $params = []): array {
$inTheLastSeconds = isset($params['in_the_last']) ? (int)$params['in_the_last'] : null;
$subject = new Subject(SubscriberSubject::KEY, ['subscriber_id' => $payload->getId()]);
return $this->automationStorage->getAutomationIdsBySubject($subject, $status, $inTheLastSeconds);

View File

@ -141,7 +141,7 @@ class SubscriberCustomFieldsFactory {
private function getCustomFieldValue(SubscriberPayload $payload, CustomFieldEntity $customField): ?string {
$subscriberCustomField = $payload->getSubscriber()->getSubscriberCustomFields()->filter(
function (SubscriberCustomFieldEntity $subscriberCustomField = null) use ($customField) {
function (?SubscriberCustomFieldEntity $subscriberCustomField = null) use ($customField) {
return $subscriberCustomField && $subscriberCustomField->getCustomField() === $customField;
}
)->first() ?: null;

View File

@ -332,7 +332,7 @@ class CustomerOrderFieldsFactory {
return $date ? new DateTimeImmutable($date, new DateTimeZone('GMT')) : null;
}
private function getOrderProductTermIds(WC_Customer $customer, string $taxonomy, int $inTheLastSeconds = null): array {
private function getOrderProductTermIds(WC_Customer $customer, string $taxonomy, ?int $inTheLastSeconds = null): array {
global $wpdb;
$statuses = array_map(function (string $status) {

View File

@ -53,7 +53,7 @@ class CustomerReviewFieldsFactory {
* Calculate the customer's review count excluding multiple reviews on the same product.
* Inspired by AutomateWoo implementation.
*/
private function getUniqueProductReviewCount(WC_Customer $customer, int $inTheLastSeconds = null): int {
private function getUniqueProductReviewCount(WC_Customer $customer, ?int $inTheLastSeconds = null): int {
global $wpdb;
$inTheLastFilter = isset($inTheLastSeconds) ? 'AND c.comment_date_gmt >= DATE_SUB(current_timestamp, INTERVAL %d SECOND)' : '';

View File

@ -11,8 +11,8 @@ class CustomerPayload implements Payload {
private ?WC_Order $order;
public function __construct(
WC_Customer $customer = null,
WC_Order $order = null
?WC_Customer $customer = null,
?WC_Order $order = null
) {
$this->customer = $customer;
$this->order = $order;

View File

@ -10,7 +10,7 @@ class CaptchaPhrase {
public function __construct(
CaptchaSession $session,
PhraseBuilder $phraseBuilder = null
?PhraseBuilder $phraseBuilder = null
) {
$this->session = $session;
$this->phraseBuilder = $phraseBuilder ?? new PhraseBuilder();

View File

@ -28,6 +28,7 @@ class PageRenderer {
$this->wp->removeAction('wp_head', 'noindex', 1);
$this->wp->addAction('wp_head', [$this, 'setMetaRobots'], 1);
$this->wp->addFilter('the_title', [$this, 'setPageTitle']);
$this->wp->addFilter('single_post_title', [$this, 'setPageTitle']);
$this->wp->addFilter('the_content', [$this, 'setPageContent']);
}
@ -39,18 +40,18 @@ class PageRenderer {
if ($separatorLocation === 'right') {
// first part
$titleParts[0] = $this->setPageTitle();
$titleParts[0] = $this->getPageTitle();
} else {
// last part
$lastIndex = count($titleParts) - 1;
$titleParts[$lastIndex] = $this->setPageTitle();
$titleParts[$lastIndex] = $this->getPageTitle();
}
return implode(" $separator ", $titleParts);
}
public function setWindowTitleParts($meta = []) {
$meta['title'] = $this->setPageTitle();
$meta['title'] = $this->getPageTitle();
return $meta;
}
@ -58,8 +59,11 @@ class PageRenderer {
echo '<meta name="robots" content="noindex,nofollow">';
}
public function setPageTitle() {
return __("Confirm youre not a robot", 'mailpoet');
public function setPageTitle($title = '') {
if ($title === __('MailPoet Page', 'mailpoet')) {
return $this->getPageTitle();
}
return $title;
}
public function setPageContent($pageContent) {
@ -72,4 +76,8 @@ class PageRenderer {
return str_replace('[mailpoet_page]', trim($content), $pageContent);
}
private function getPageTitle() {
return __('Confirm youre not a robot', 'mailpoet');
}
}

View File

@ -10,7 +10,7 @@ class ValidationError extends \RuntimeException {
$message = "",
array $meta = [],
$code = 0,
\Throwable $previous = null
?\Throwable $previous = null
) {
$this->meta = $meta;
$this->meta['error'] = $message;

View File

@ -16,7 +16,7 @@ class Capabilities {
public function __construct(
$renderer = null,
WPFunctions $wp = null
?WPFunctions $wp = null
) {
if ($renderer !== null) {
$this->renderer = $renderer;

View File

@ -399,7 +399,7 @@ class Hooks {
}
public function setupWooCommerceSettings() {
$this->wp->addAction('woocommerce_settings_start', [
$this->wp->addAction('woocommerce_settings_email_options_after', [
$this->hooksWooCommerce,
'disableWooCommerceSettings',
]);

View File

@ -145,7 +145,7 @@ class HooksWooCommerce {
}
}
public function onRegister($errors, string $userLogin, string $userEmail = null) {
public function onRegister($errors, string $userLogin, ?string $userEmail = null) {
try {
if (empty($errors->errors)) {
$this->subscriberRegistration->onRegister($errors, $userLogin, $userEmail);

View File

@ -711,7 +711,7 @@ class Menu {
);
}
public static function isOnMailPoetAdminPage(array $exclude = null, $screenId = null) {
public static function isOnMailPoetAdminPage(?array $exclude = null, $screenId = null) {
if (is_null($screenId)) {
if (empty($_REQUEST['page'])) {
return false;
@ -762,7 +762,7 @@ class Menu {
// Used for displaying admin notices only
}
public function checkPremiumKey(ServicesChecker $checker = null) {
public function checkPremiumKey(?ServicesChecker $checker = null) {
$showNotices = isset($_SERVER['SCRIPT_NAME'])
&& stripos(sanitize_text_field(wp_unslash($_SERVER['SCRIPT_NAME'])), 'plugins.php') !== false;
$checker = $checker ?: $this->servicesChecker;

View File

@ -14,7 +14,7 @@ class TwigEnvironment extends Environment {
* We need to produce the same class regardless of PHP_VERSION. Therefore, we
* overwrite this method.
**/
public function getTemplateClass(string $name, int $index = null): string {
public function getTemplateClass(string $name, ?int $index = null): string {
return $this->templateClassPrefix . \hash('sha256', $name) . (null === $index ? '' : '___' . $index);
}
}

View File

@ -27,7 +27,7 @@ class DaemonHttpRunner {
private $wordpressTrigger;
public function __construct(
Daemon $daemon = null,
?Daemon $daemon,
CronHelper $cronHelper,
SettingsController $settings,
WordPress $wordpressTrigger
@ -140,7 +140,7 @@ class DaemonHttpRunner {
*
* @return bool
*/
private function shouldTerminateExecution(array $settingsDaemonData = null) {
private function shouldTerminateExecution(?array $settingsDaemonData = null) {
return !$settingsDaemonData ||
$settingsDaemonData['token'] !== $this->token ||
(isset($settingsDaemonData['status']) && $settingsDaemonData['status'] !== CronHelper::DAEMON_STATUS_ACTIVE);

View File

@ -64,7 +64,7 @@ class Links {
return $this->newsletterLinks->save($links, $newsletter->getId(), $queue->getId());
}
public function getUnsubscribeUrl($queueId, SubscriberEntity $subscriber = null) {
public function getUnsubscribeUrl($queueId, ?SubscriberEntity $subscriber = null) {
if ($this->trackingConfig->isEmailTrackingEnabled() && $subscriber) {
$linkHash = $this->newsletterLinkRepository->findOneBy(
[

View File

@ -23,7 +23,7 @@ class Mailer {
$this->mailer = $this->configureMailer();
}
public function configureMailer(NewsletterEntity $newsletter = null) {
public function configureMailer(?NewsletterEntity $newsletter = null) {
$sender['address'] = ($newsletter && !empty($newsletter->getSenderAddress())) ?
$newsletter->getSenderAddress() :
null;

View File

@ -79,10 +79,10 @@ class Newsletter {
private $personalizer;
public function __construct(
WPFunctions $wp = null,
PostsTask $postsTask = null,
GATracking $gaTracking = null,
Emoji $emoji = null
?WPFunctions $wp = null,
?PostsTask $postsTask = null,
?GATracking $gaTracking = null,
?Emoji $emoji = null
) {
$trackingConfig = ContainerWrapper::getInstance()->get(TrackingConfig::class);
$this->trackingEnabled = $trackingConfig->isEmailTrackingEnabled();

View File

@ -16,7 +16,7 @@ class Shortcodes {
* @param SubscriberEntity|null $subscriber
* @param SendingQueueEntity|null $queue
*/
public static function process($content, $contentSource = null, NewsletterEntity $newsletter = null, SubscriberEntity $subscriber = null, SendingQueueEntity $queue = null) {
public static function process($content, $contentSource = null, ?NewsletterEntity $newsletter = null, ?SubscriberEntity $subscriber = null, ?SendingQueueEntity $queue = null) {
/** @var NewsletterShortcodes $shortcodes */
$shortcodes = ContainerWrapper::getInstance()->get(NewsletterShortcodes::class);

View File

@ -138,7 +138,7 @@ class Worker {
];
}
private function prepareContext(NewsletterEntity $newsletter, SendingQueueEntity $sendingQueue, NewsletterLinkEntity $link = null) {
private function prepareContext(NewsletterEntity $newsletter, SendingQueueEntity $sendingQueue, ?NewsletterLinkEntity $link = null) {
$statistics = $this->newsletterStatisticsRepository->getStatistics($newsletter);
$clicked = ($statistics->getClickCount() * 100) / $statistics->getTotalSentCount();
$opened = ($statistics->getOpenCount() * 100) / $statistics->getTotalSentCount();

View File

@ -699,7 +699,7 @@ class ContainerConfigurator implements IContainerConfigurator {
return $container;
}
public static function getPremiumService($id, ContainerInterface $container = null) {
public static function getPremiumService($id, ?ContainerInterface $container = null) {
if ($container === null) {
return null;
}

View File

@ -20,7 +20,7 @@ class ContainerWrapper implements ContainerInterface {
public function __construct(
Container $freeContainer,
Container $premiumContainer = null
?Container $premiumContainer = null
) {
$this->freeContainer = $freeContainer;
$this->premiumContainer = $premiumContainer;

View File

@ -41,7 +41,7 @@ abstract class Repository {
* @param int|null $offset
* @return T[]
*/
public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) {
public function findBy(array $criteria, ?array $orderBy = null, $limit = null, $offset = null) {
return $this->doctrineRepository->findBy($criteria, $orderBy, $limit, $offset);
}
@ -62,7 +62,7 @@ abstract class Repository {
* @param array|null $orderBy
* @return T|null
*/
public function findOneBy(array $criteria, array $orderBy = null) {
public function findOneBy(array $criteria, ?array $orderBy = null) {
return $this->doctrineRepository->findOneBy($criteria, $orderBy);
}
@ -114,7 +114,7 @@ abstract class Repository {
/**
* @param callable(T): bool|null $filter
*/
public function refreshAll(callable $filter = null): void {
public function refreshAll(?callable $filter = null): void {
$entities = $this->getAllFromIdentityMap();
foreach ($entities as $entity) {
if ($filter && !$filter($entity)) {
@ -142,7 +142,7 @@ abstract class Repository {
/**
* @param callable(T): bool|null $filter
*/
public function detachAll(callable $filter = null): void {
public function detachAll(?callable $filter = null): void {
$entities = $this->getAllFromIdentityMap();
foreach ($entities as $entity) {
if ($filter && !$filter($entity)) {

View File

@ -35,7 +35,7 @@ class EmailEditorPreviewEmail {
}
private function validateData($data) {
if (empty($data['email']) || empty($data['postId']) || empty($data['newsletterId'])) {
if (empty($data['email']) || empty($data['postId'])) {
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->findOneById((int)$postData['newsletterId']);
$newsletter = $this->newslettersRepository->findOneBy(['wpPost' => (int)$postData['postId']]);
if (!$newsletter) {
if (!$newsletter instanceof NewsletterEntity) {
throw new \Exception(esc_html__('This email does not exist.', 'mailpoet'));
}

View File

@ -174,7 +174,7 @@ class FormEntity {
];
}
public function getBlocksByTypes(array $types, array $blocks = null): array {
public function getBlocksByTypes(array $types, ?array $blocks = null): array {
$found = [];
if ($blocks === null) {
$blocks = $this->getBody() ?? [];

View File

@ -474,7 +474,7 @@ class NewsletterEntity {
* @return int[]
*/
public function getSegmentIds() {
return array_filter($this->newsletterSegments->map(function(NewsletterSegmentEntity $newsletterSegment = null) {
return array_filter($this->newsletterSegments->map(function(?NewsletterSegmentEntity $newsletterSegment = null) {
if (!$newsletterSegment) return null;
$segment = $newsletterSegment->getSegment();
return $segment ? (int)$segment->getId() : null;
@ -489,7 +489,7 @@ class NewsletterEntity {
}
public function getOption(string $name): ?NewsletterOptionEntity {
$option = $this->options->filter(function (NewsletterOptionEntity $option = null) use ($name): bool {
$option = $this->options->filter(function (?NewsletterOptionEntity $option = null) use ($name): bool {
if (!$option) return false;
return ($field = $option->getOptionField()) ? $field->getName() === $name : false;
})->first();

View File

@ -243,7 +243,7 @@ class ScheduledTaskEntity {
public function getSubscribersByProcessed(int $processed): array {
$criteria = Criteria::create()
->where(Criteria::expr()->eq('processed', $processed));
$subscribers = $this->subscribers->matching($criteria)->map(function (ScheduledTaskSubscriberEntity $taskSubscriber = null): ?SubscriberEntity {
$subscribers = $this->subscribers->matching($criteria)->map(function (?ScheduledTaskSubscriberEntity $taskSubscriber = null): ?SubscriberEntity {
if (!$taskSubscriber) return null;
return $taskSubscriber->getSubscriber();
});

View File

@ -61,7 +61,7 @@ class ScheduledTaskSubscriberEntity {
SubscriberEntity $subscriber,
int $processed = 0,
int $failed = 0,
string $error = null
?string $error = null
) {
$this->task = $task;
$this->subscriber = $subscriber;

View File

@ -45,7 +45,7 @@ class StatisticsNewsletterEntity {
NewsletterEntity $newsletter,
SendingQueueEntity $queue,
SubscriberEntity $subscriber,
\DateTimeInterface $sentAt = null
?\DateTimeInterface $sentAt = null
) {
$this->newsletter = $newsletter;
$this->queue = $queue;

View File

@ -67,8 +67,8 @@ class StatisticsUnsubscribeEntity {
private $method = self::METHOD_UNKNOWN;
public function __construct(
NewsletterEntity $newsletter = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter,
?SendingQueueEntity $queue,
SubscriberEntity $subscriber
) {
$this->newsletter = $newsletter;

View File

@ -504,7 +504,7 @@ class SubscriberEntity {
/** * @return Collection<int, SegmentEntity> */
public function getSegments() {
return $this->subscriberSegments->map(function (SubscriberSegmentEntity $subscriberSegment = null) {
return $this->subscriberSegments->map(function (?SubscriberSegmentEntity $subscriberSegment = null) {
if (!$subscriberSegment) return null;
return $subscriberSegment->getSegment();
})->filter(function (?SegmentEntity $segment = null) {
@ -635,7 +635,7 @@ class SubscriberEntity {
/** @ORM\PreFlush */
public function cleanupSubscriberSegments(): void {
// Delete old orphan SubscriberSegments to avoid errors on update
$this->subscriberSegments->map(function (SubscriberSegmentEntity $subscriberSegment = null) {
$this->subscriberSegments->map(function (?SubscriberSegmentEntity $subscriberSegment = null) {
if (!$subscriberSegment) return null;
if ($subscriberSegment->getSegment() === null) {
$this->subscriberSegments->removeElement($subscriberSegment);

View File

@ -41,14 +41,14 @@ class Renderer {
return $html;
}
public function renderHTML(FormEntity $form = null): string {
public function renderHTML(?FormEntity $form = null): string {
if (($form instanceof FormEntity) && !empty($form->getBody()) && is_array($form->getSettings())) {
return $this->renderBlocks($form->getBody(), $form->getSettings() ?? [], $form->getId());
}
return '';
}
public function getCustomStyles(FormEntity $form = null): string {
public function getCustomStyles(?FormEntity $form = null): string {
if (($form instanceof FormEntity) && (strlen(trim($form->getStyles() ?? '')) > 0)) {
return strip_tags($form->getStyles() ?? '');
} else {

View File

@ -31,9 +31,9 @@ class ListingDefinition {
private $selection;
public function __construct(
string $group = null,
?string $group = null,
array $filters = [],
string $search = null,
?string $search = null,
array $parameters = [],
string $sortBy = 'created_at',
string $sortOrder = 'desc',

View File

@ -50,11 +50,11 @@ class LogRepository extends Repository {
* @return LogEntity[]
*/
public function getLogs(
\DateTimeInterface $dateFrom = null,
\DateTimeInterface $dateTo = null,
string $search = null,
string $offset = null,
string $limit = null
?\DateTimeInterface $dateFrom = null,
?\DateTimeInterface $dateTo = null,
?string $search = null,
?string $offset = null,
?string $limit = null
): array {
$query = $this->doctrineRepository->createQueryBuilder('l')
->select('l');

View File

@ -45,7 +45,7 @@ class MailerFactory {
return $this->defaultMailer;
}
public function buildMailer(array $mailerConfig = null, array $sender = null, array $replyTo = null, string $returnPath = null): Mailer {
public function buildMailer(?array $mailerConfig = null, ?array $sender = null, ?array $replyTo = null, ?string $returnPath = null): Mailer {
$sender = $this->getSenderNameAndAddress($sender);
$replyTo = $this->getReplyToNameAndAddress($sender, $replyTo);
$mailerConfig = $mailerConfig ?? $this->getMailerConfig();
@ -120,7 +120,7 @@ class MailerFactory {
return $config;
}
private function getSenderNameAndAddress(array $sender = null): array {
private function getSenderNameAndAddress(?array $sender = null): array {
if (empty($sender)) {
$sender = $this->settings->get('sender', []);
if (empty($sender['address'])) throw new InvalidStateException(__('Sender name and email are not configured.', 'mailpoet'));
@ -133,7 +133,7 @@ class MailerFactory {
];
}
private function getReplyToNameAndAddress(array $sender, array $replyTo = null): array {
private function getReplyToNameAndAddress(array $sender, ?array $replyTo = null): array {
if (!$replyTo) {
$replyTo = $this->settings->get('reply_to');
$replyTo['name'] = (!empty($replyTo['name'])) ?

View File

@ -33,7 +33,7 @@ class MailerLog {
* @param MailerLogData|null $mailerLog
* @return MailerLogData
*/
public static function getMailerLog(array $mailerLog = null): array {
public static function getMailerLog(?array $mailerLog = null): array {
if ($mailerLog) return $mailerLog;
$settings = SettingsController::getInstance();
$mailerLog = $settings->get(self::SETTING_NAME);
@ -90,7 +90,7 @@ class MailerLog {
* @return null
* @throws \Exception
*/
public static function enforceExecutionRequirements(array $mailerLog = null) {
public static function enforceExecutionRequirements(?array $mailerLog = null) {
$mailerLog = self::getMailerLog($mailerLog);
if ($mailerLog['retry_attempt'] === self::RETRY_ATTEMPTS_LIMIT) {
$mailerLog = self::pauseSending($mailerLog);
@ -162,9 +162,9 @@ class MailerLog {
public static function processError(
string $operation,
string $errorMessage,
string $errorCode = null,
?string $errorCode = null,
bool $pauseSending = false,
int $throttledBatchSize = null
?int $throttledBatchSize = null
) {
$mailerLog = self::getMailerLog();
if (!isset($throttledBatchSize) || $throttledBatchSize === 1) {
@ -232,7 +232,7 @@ class MailerLog {
array $mailerLog,
string $operation,
string $errorMessage,
string $errorCode = null
?string $errorCode = null
): array {
$mailerLog['error'] = [
'operation' => $operation,
@ -248,7 +248,7 @@ class MailerLog {
* @param MailerLogData|null $mailerLog
* @return MailerLogError|null
*/
public static function getError(array $mailerLog = null): ?array {
public static function getError(?array $mailerLog = null): ?array {
$mailerLog = self::getMailerLog($mailerLog);
return isset($mailerLog['error']) ? $mailerLog['error'] : null;
}
@ -300,7 +300,7 @@ class MailerLog {
* @param MailerLogData|null $mailerLog
* @return bool
*/
public static function isSendingLimitReached(array $mailerLog = null): bool {
public static function isSendingLimitReached(?array $mailerLog = null): bool {
$settings = SettingsController::getInstance();
$mailerConfig = $settings->get(Mailer::MAILER_CONFIG_SETTING_NAME);
// do not enforce sending limit for MailPoet's sending method
@ -322,7 +322,7 @@ class MailerLog {
* @param MailerLogData|null $mailerLog
* @return int
*/
public static function sentSince(int $sinceSeconds = null, array $mailerLog = null): int {
public static function sentSince(?int $sinceSeconds = null, ?array $mailerLog = null): int {
if ($sinceSeconds === null) {
$settings = SettingsController::getInstance();
@ -353,7 +353,7 @@ class MailerLog {
* @param MailerLogData|null $mailerLog
* @return MailerLogData
*/
private static function removeOutdatedSentInformationFromMailerlog(array $mailerLog = null): array {
private static function removeOutdatedSentInformationFromMailerlog(?array $mailerLog = null): array {
$settings = SettingsController::getInstance();
$mailerConfig = $settings->get(Mailer::MAILER_CONFIG_SETTING_NAME);
@ -375,7 +375,7 @@ class MailerLog {
* @param int|null $timestamp
* @return string
*/
private static function sentEntriesDate(int $timestamp = null): string {
private static function sentEntriesDate(?int $timestamp = null): string {
return date('Y-m-d H:i:s', $timestamp ?? time());
}
@ -384,7 +384,7 @@ class MailerLog {
* @param MailerLogData|null $mailerLog
* @return bool
*/
public static function isSendingPaused(array $mailerLog = null): bool {
public static function isSendingPaused(?array $mailerLog = null): bool {
$mailerLog = self::getMailerLog($mailerLog);
return $mailerLog['status'] === self::STATUS_PAUSED;
}
@ -393,7 +393,7 @@ class MailerLog {
* @param MailerLogData|null $mailerLog
* @return bool
*/
public static function isSendingWaitingForRetry(array $mailerLog = null): bool {
public static function isSendingWaitingForRetry(?array $mailerLog = null): bool {
$mailerLog = self::getMailerLog($mailerLog);
$retryAt = $mailerLog['retry_at'] ?? null;
return $retryAt && (time() <= $retryAt);

View File

@ -18,7 +18,7 @@ class MetaInfo {
return $this->makeMetaInfo('email_stats_notification', 'unknown', 'administrator');
}
public function getWordPressTransactionalMetaInfo(SubscriberEntity $subscriber = null) {
public function getWordPressTransactionalMetaInfo(?SubscriberEntity $subscriber = null) {
return $this->makeMetaInfo(
'transactional',
$subscriber ? $subscriber->getStatus() : 'unknown',

View File

@ -9,7 +9,7 @@ class BlacklistCheck {
private $blacklist;
public function __construct(
Blacklist $blacklist = null
?Blacklist $blacklist = null
) {
if (is_null($blacklist)) {
$blacklist = new Blacklist();

View File

@ -30,7 +30,7 @@ class Migrator {
$this->store = $store;
}
public function run(Logger $logger = null): void {
public function run(?Logger $logger = null): void {
$this->store->ensureMigrationsTable();
$migrations = $this->getStatus();

View File

@ -18,7 +18,7 @@ class PostContentManager {
private $wp;
public function __construct(
WooCommerceHelper $woocommerceHelper = null
?WooCommerceHelper $woocommerceHelper = null
) {
$this->wp = new WPFunctions;
$this->maxExcerptLength = $this->wp->applyFilters('mailpoet_newsletter_post_excerpt_length', $this->maxExcerptLength);

View File

@ -15,7 +15,7 @@ class PostTransformer {
public function __construct(
$args,
PostTransformerContentsExtractor $extractor = null
?PostTransformerContentsExtractor $extractor = null
) {
$this->args = $args;
$this->withLayout = isset($args['withLayout']) ? (bool)filter_var($args['withLayout'], FILTER_VALIDATE_BOOLEAN) : false;

View File

@ -260,7 +260,7 @@ class NewsletterListingRepository extends ListingRepository {
$queryBuilder->addOrderBy("n.$sortBy", $sortOrder);
}
private function applyType(QueryBuilder $queryBuilder, string $type, string $group = null) {
private function applyType(QueryBuilder $queryBuilder, string $type, ?string $group = null) {
if (!in_array($type, self::$supportedTypes)) {
return;
}

View File

@ -22,7 +22,7 @@ class AbandonedCartContent {
NewsletterEntity $newsletter,
array $args,
bool $preview = false,
SendingQueueEntity $sendingQueue = null
?SendingQueueEntity $sendingQueue = null
): array {
if (
!in_array(
@ -38,12 +38,12 @@ class AbandonedCartContent {
// Do not display the block if not an automatic email
return [];
}
$groupOption = $newsletter->getOptions()->filter(function (NewsletterOptionEntity $newsletterOption = null) {
$groupOption = $newsletter->getOptions()->filter(function (?NewsletterOptionEntity $newsletterOption = null) {
if (!$newsletterOption) return false;
$optionField = $newsletterOption->getOptionField();
return $optionField && $optionField->getName() === 'group';
})->first();
$eventOption = $newsletter->getOptions()->filter(function (NewsletterOptionEntity $newsletterOption = null) {
$eventOption = $newsletter->getOptions()->filter(function (?NewsletterOptionEntity $newsletterOption = null) {
if (!$newsletterOption) return false;
$optionField = $newsletterOption->getOptionField();
return $optionField && $optionField->getName() === 'event';

View File

@ -48,12 +48,12 @@ class Preprocessor {
* @param NewsletterEntity $newsletter
* @return array
*/
public function process(NewsletterEntity $newsletter, $content, bool $preview = false, SendingQueueEntity $sendingQueue = null) {
public function process(NewsletterEntity $newsletter, $content, bool $preview = false, ?SendingQueueEntity $sendingQueue = null) {
if (!array_key_exists('blocks', $content)) {
return $content;
}
$contentBlocks = $content['blocks'];
$contentBlocks = $this->couponPreProcessor->processCoupons($newsletter, $contentBlocks, $preview);
$contentBlocks = $this->couponPreProcessor->processCoupons($newsletter, $contentBlocks, $preview, $sendingQueue);
$content['blocks'] = $this->processContainer($newsletter, $contentBlocks, $preview, $sendingQueue);
return $content;
}
@ -74,7 +74,7 @@ class Preprocessor {
return $containerBlocks;
}
public function processBlock(NewsletterEntity $newsletter, array $block, bool $preview = false, SendingQueueEntity $sendingQueue = null): array {
public function processBlock(NewsletterEntity $newsletter, array $block, bool $preview = false, ?SendingQueueEntity $sendingQueue = null): array {
switch ($block['type']) {
case 'abandonedCartContent':
return $this->abandonedCartContent->render($newsletter, $block, $preview, $sendingQueue);

View File

@ -68,7 +68,7 @@ class Renderer {
$this->capabilitiesManager = $capabilitiesManager;
}
public function render(NewsletterEntity $newsletter, SendingQueueEntity $sendingQueue = null, $type = false) {
public function render(NewsletterEntity $newsletter, ?SendingQueueEntity $sendingQueue = null, $type = false) {
return $this->_render($newsletter, $sendingQueue, $type);
}
@ -76,7 +76,7 @@ class Renderer {
return $this->_render($newsletter, null, $type, true, $subject);
}
private function _render(NewsletterEntity $newsletter, SendingQueueEntity $sendingQueue = null, $type = false, $preview = false, $subject = null) {
private function _render(NewsletterEntity $newsletter, ?SendingQueueEntity $sendingQueue = null, $type = false, $preview = false, $subject = null) {
$language = $this->wp->getBloginfo('language');
$metaRobots = $preview ? '<meta name="robots" content="noindex, nofollow" />' : '';
$subject = $subject ?: $newsletter->getSubject();

View File

@ -9,9 +9,9 @@ use MailPoet\Entities\SubscriberEntity;
interface CategoryInterface {
public function process(
array $shortcodeDetails,
NewsletterEntity $newsletter = null,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter = null,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null,
string $content = '',
bool $wpUserPreview = false
): ?string;

View File

@ -10,9 +10,9 @@ use MailPoet\WP\Functions as WPFunctions;
class Date implements CategoryInterface {
public function process(
array $shortcodeDetails,
NewsletterEntity $newsletter = null,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter = null,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null,
string $content = '',
bool $wpUserPreview = false
): ?string {

View File

@ -40,9 +40,9 @@ class Link implements CategoryInterface {
public function process(
array $shortcodeDetails,
NewsletterEntity $newsletter = null,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter = null,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null,
string $content = '',
bool $wpUserPreview = false
): ?string {
@ -119,9 +119,9 @@ class Link implements CategoryInterface {
public function processShortcodeAction(
$shortcodeAction,
NewsletterEntity $newsletter = null,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter = null,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null,
$wpUserPreview = false
): ?string {
$subscriptionUrlFactory = SubscriptionUrlFactory::getInstance();

View File

@ -25,9 +25,9 @@ class Newsletter implements CategoryInterface {
public function process(
array $shortcodeDetails,
NewsletterEntity $newsletter = null,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter = null,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null,
string $content = '',
bool $wpUserPreview = false
): ?string {

View File

@ -19,9 +19,9 @@ class Site implements CategoryInterface {
public function process(
array $shortcodeDetails,
NewsletterEntity $newsletter = null,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter = null,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null,
string $content = '',
bool $wpUserPreview = false
): ?string {

View File

@ -28,9 +28,9 @@ class Subscriber implements CategoryInterface {
public function process(
array $shortcodeDetails,
NewsletterEntity $newsletter = null,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null,
?NewsletterEntity $newsletter = null,
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null,
string $content = '',
bool $wpUserPreview = false
): ?string {

View File

@ -60,15 +60,15 @@ class Shortcodes {
$this->wp = $wp;
}
public function setNewsletter(NewsletterEntity $newsletter = null): void {
public function setNewsletter(?NewsletterEntity $newsletter = null): void {
$this->newsletter = $newsletter;
}
public function setSubscriber(SubscriberEntity $subscriber = null): void {
public function setSubscriber(?SubscriberEntity $subscriber = null): void {
$this->subscriber = $subscriber;
}
public function setQueue(SendingQueueEntity $queue = null): void {
public function setQueue(?SendingQueueEntity $queue = null): void {
$this->queue = $queue;
}

View File

@ -65,8 +65,8 @@ class NewsletterStatisticsRepository extends Repository {
*/
public function getBatchStatistics(
array $newsletters,
\DateTimeImmutable $from = null,
\DateTimeImmutable $to = null,
?\DateTimeImmutable $from = null,
?\DateTimeImmutable $to = null,
array $include = [
'totals',
StatisticsClickEntity::class,
@ -133,8 +133,8 @@ class NewsletterStatisticsRepository extends Repository {
*/
public function getAllForSubscriber(
SubscriberEntity $subscriber,
int $limit = null,
int $offset = null
?int $limit = null,
?int $offset = null
): array {
return $this->entityManager->createQueryBuilder()
->select('IDENTITY(statistics.newsletter) AS newsletter_id')
@ -192,7 +192,7 @@ class NewsletterStatisticsRepository extends Repository {
}
}
private function getTotalSentCounts(array $newsletters, \DateTimeImmutable $from = null, \DateTimeImmutable $to = null): array {
private function getTotalSentCounts(array $newsletters, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null): array {
$query = $this->doctrineRepository
->createQueryBuilder('n')
->select('n.id, SUM(q.countProcessed) AS cnt')
@ -226,7 +226,7 @@ class NewsletterStatisticsRepository extends Repository {
return $counts;
}
private function getStatisticCounts(string $statisticsEntityName, array $newsletters, \DateTimeImmutable $from = null, \DateTimeImmutable $to = null): array {
private function getStatisticCounts(string $statisticsEntityName, array $newsletters, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null): array {
$qb = $this->getStatisticsQuery($statisticsEntityName, $newsletters);
if (
$statisticsEntityName === StatisticsClickEntity::class
@ -267,7 +267,7 @@ class NewsletterStatisticsRepository extends Repository {
->setParameter('newsletters', $newsletters);
}
private function getWooCommerceRevenues(array $newsletters, \DateTimeImmutable $from = null, \DateTimeImmutable $to = null) {
private function getWooCommerceRevenues(array $newsletters, ?\DateTimeImmutable $from = null, ?\DateTimeImmutable $to = null) {
if (!$this->wcHelper->isWooCommerceActive()) {
return null;
}

View File

@ -49,8 +49,8 @@ class ViewInBrowserRenderer {
public function render(
bool $isPreview,
NewsletterEntity $newsletter,
SubscriberEntity $subscriber = null,
SendingQueueEntity $queue = null
?SubscriberEntity $subscriber = null,
?SendingQueueEntity $queue = null
) {
$wpUserPreview = $isPreview;
$isTrackingEnabled = $this->trackingConfig->isEmailTrackingEnabled();

Some files were not shown because too many files have changed in this diff Show More