Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a72d2e9
UserPersonalizer in CampaignProcessorMessageHandler
tatevikg1 Dec 11, 2025
d40dedd
HtmlToText
tatevikg1 Dec 11, 2025
613a196
MessageDataLoader
tatevikg1 Dec 11, 2025
759d8e0
TextParser
tatevikg1 Dec 11, 2025
d94f825
RemotePageFetcher
tatevikg1 Dec 13, 2025
3b9267f
Use repo methods
tatevikg1 Dec 14, 2025
5fc8637
Use MessagePrecacheDto
tatevikg1 Dec 14, 2025
69884a8
Refactor
tatevikg1 Dec 14, 2025
077bc63
Todo
tatevikg1 Dec 15, 2025
492e1d0
SystemMailConstructor
tatevikg1 Dec 15, 2025
109b07a
EmailBuilder
tatevikg1 Dec 16, 2025
65c0030
InjectedByHeaderSubscriber
tatevikg1 Dec 16, 2025
7e9bab2
TemplateImageManager
tatevikg1 Dec 17, 2025
3dcb90a
ExternalImageCacher
tatevikg1 Dec 17, 2025
0c5b4f4
TemplateImageEmbedder
tatevikg1 Dec 18, 2025
25ef84a
Mailer
tatevikg1 Dec 21, 2025
166a66c
RemotePageFetcherTest
tatevikg1 Dec 22, 2025
e7f9eca
TextParserTest
tatevikg1 Dec 22, 2025
cec0eb1
MessageDataLoaderTest
tatevikg1 Dec 22, 2025
2b0c256
MessageDataLoaderTest
tatevikg1 Dec 22, 2025
1643a29
Test fix
tatevikg1 Dec 22, 2025
04e84a1
Fix: phpmd
tatevikg1 Dec 23, 2025
4a9e895
Fix: phpcs
tatevikg1 Dec 24, 2025
b6e2ee2
After review 0
tatevikg1 Dec 25, 2025
ca6dc94
After review 1
tatevikg1 Dec 25, 2025
9fea466
Add tests
tatevikg1 Dec 27, 2025
2323119
EmailBuilderTest
tatevikg1 Dec 29, 2025
823e006
update coderabbit.yaml
tatevikg1 Dec 29, 2025
a140c2b
Add tests
tatevikg1 Dec 29, 2025
7f21c58
MailSizeChecker
tatevikg1 Dec 29, 2025
8342b4a
Feat/email building with attachments (#375)
TatevikGr Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .coderabbit.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ reviews:
instructions: |
You are reviewing PHP domain-layer code. Enforce domain purity, with a relaxed policy for DynamicListAttr:

- ❌ Do not allow persistence or transaction side effects here for *normal* domain models.
- Flag ANY usage of Doctrine persistence APIs on regular domain entities, especially:
- ❌ Do not allow, flag ANY DB write / finalization:
- `$entityManager->flush(...)`, `$this->entityManager->flush(...)`
- `$em->persist(...)`, `$em->remove(...)`
- `$em->beginTransaction()`, `$em->commit()`, `$em->rollback()`
- `$em->beginTransaction()`, `$em->commit()`, `$em->rollback()`, `$em->transactional(...)`
- `$em->getConnection()->executeStatement(...)` for DML/DDL (INSERT/UPDATE/DELETE/ALTER/...)
- ✅ Accessing Doctrine *metadata*, *schema manager*, or *read-only schema info* is acceptable
as long as it does not modify state or perform writes.
as long as it does not modify state or perform writes. Accessing Doctrine *persistence APIs*
persist, remove, etc.) is acceptable, allow scheduling changes in the UnitOfWork (no DB writes)

- ✅ **Relaxed rule for DynamicListAttr-related code**:
- DynamicListAttr is a special case dealing with dynamic tables/attrs.
Expand Down
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@
"ext-imap": "*",
"tatevikgr/rss-feed": "dev-main",
"ext-pdo": "*",
"ezyang/htmlpurifier": "^4.19"
"ezyang/htmlpurifier": "^4.19",
"ext-libxml": "*",
"ext-gd": "*",
"ext-curl": "*",
"ext-fileinfo": "*",
"setasign/fpdf": "^1.8"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
Expand Down
6 changes: 3 additions & 3 deletions config/PHPMD/rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<exclude-pattern>*/Migrations/*</exclude-pattern>

<!-- rules from the "clean code" rule set -->
<rule ref="rulesets/cleancode.xml/BooleanArgumentFlag"/>
<!-- <rule ref="rulesets/cleancode.xml/BooleanArgumentFlag"/>-->
<rule ref="rulesets/codesize.xml/CyclomaticComplexity"/>
<rule ref="rulesets/codesize.xml/NPathComplexity"/>
<rule ref="rulesets/codesize.xml/ExcessiveMethodLength"/>
Expand Down Expand Up @@ -41,12 +41,12 @@
<!-- rules from the "naming" rule set -->
<rule ref="rulesets/naming.xml/ShortVariable">
<properties>
<property name="exceptions" value="id,ip,cc,io"/>
<property name="exceptions" value="id,ip,cc,io,to"/>
</properties>
</rule>
<rule ref="rulesets/naming.xml/LongVariable">
<properties>
<property name="maximum" value="25"/>
<property name="maximum" value="30"/>
</properties>
</rule>
<rule ref="rulesets/naming.xml/ShortMethodName"/>
Expand Down
6 changes: 5 additions & 1 deletion config/PhpCodeSniffer/ruleset.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@
<rule ref="PEAR.WhiteSpace.ScopeClosingBrace"/>
<rule ref="Squiz.WhiteSpace.CastSpacing"/>
<rule ref="Squiz.WhiteSpace.LogicalOperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.OperatorSpacing"/>
<rule ref="Squiz.WhiteSpace.OperatorSpacing">
<properties>
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
</ruleset>
51 changes: 50 additions & 1 deletion config/parameters.yml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,22 @@ parameters:
env(DATABASE_PREFIX): 'phplist_'
list_table_prefix: '%%env(LIST_TABLE_PREFIX)%%'
env(LIST_TABLE_PREFIX): 'listattr_'
app.dev_version: '%%env(APP_DEV_VERSION)%%'
env(APP_DEV_VERSION): '0'
app.dev_email: '%%env(APP_DEV_EMAIL)%%'
env(APP_DEV_EMAIL): '[email protected]'
app.powered_by_phplist: '%%env(APP_POWERED_BY_PHPLIST)%%'
env(APP_POWERED_BY_PHPLIST): '0'
app.preference_page_show_private_lists: '%%env(PREFERENCEPAGE_SHOW_PRIVATE_LISTS)%%'
env(PREFERENCEPAGE_SHOW_PRIVATE_LISTS): '0'
app.rest_api_domain: '%%env(REST_API_DOMAIN)%%'
env(REST_API_DOMAIN): 'https://example.com/api/v2'

# Email configuration
app.mailer_from: '%%env(MAILER_FROM)%%'
env(MAILER_FROM): '[email protected]'
app.mailer_dsn: '%%env(MAILER_DSN)%%'
env(MAILER_DSN): 'null://null'
env(MAILER_DSN): 'null://null' # set local_domain on transport
app.confirmation_url: '%%env(CONFIRMATION_URL)%%'
env(CONFIRMATION_URL): 'https://example.com/subscriber/confirm/'
app.subscription_confirmation_url: '%%env(SUBSCRIPTION_CONFIRMATION_URL)%%'
Expand Down Expand Up @@ -89,3 +99,42 @@ parameters:
env(MESSAGING_MAX_PROCESS_TIME): '600'
messaging.max_mail_size: '%%env(MAX_MAILSIZE)%%'
env(MAX_MAILSIZE): '209715200'
messaging.default_message_age: '%%env(DEFAULT_MESSAGEAGE)%%'
env(DEFAULT_MESSAGEAGE): '691200'
messaging.use_manual_text_part: '%%env(USE_MANUAL_TEXT_PART)%%'
env(USE_MANUAL_TEXT_PART): '0'
messaging.blacklist_grace_time: '%%env(MESSAGING_BLACKLIST_GRACE_TIME)%%'
env(MESSAGING_BLACKLIST_GRACE_TIME): '600'
messaging.google_sender_id: '%%env(GOOGLE_SENDERID)%%'
env(GOOGLE_SENDERID): ''
messaging.use_amazon_ses: '%%env(USE_AMAZONSES)%%'
env(USE_AMAZONSES): '0'
messaging.use_precedence_header: '%%env(USE_PRECEDENCE_HEADER)%%'
env(USE_PRECEDENCE_HEADER): '0'
messaging.embed_external_images: '%%env(EMBEDEXTERNALIMAGES)%%'
env(EMBEDEXTERNALIMAGES): '0'
messaging.embed_uploaded_images: '%%env(EMBEDUPLOADIMAGES)%%'
env(EMBEDUPLOADIMAGES): '0'
messaging.external_image_max_age: '%%env(EXTERNALIMAGE_MAXAGE)%%'
env(EXTERNALIMAGE_MAXAGE): '0'
messaging.external_image_timeout: '%%env(EXTERNALIMAGE_TIMEOUT)%%'
env(EXTERNALIMAGE_TIMEOUT): '30'
messaging.external_image_max_size: '%%env(EXTERNALIMAGE_MAXSIZE)%%'
env(EXTERNALIMAGE_MAXSIZE): '204800'
messaging.forward_alternative_content: '%%env(FORWARD_ALTERNATIVE_CONTENT)%%'
env(FORWARD_ALTERNATIVE_CONTENT): '0'
messaging.email_text_credits: '%%env(EMAILTEXTCREDITS)%%'
env(EMAILTEXTCREDITS): '0'
messaging.always_add_user_track: '%%env(ALWAYS_ADD_USERTRACK)%%'
env(ALWAYS_ADD_USERTRACK): '1'

phplist.upload_images_dir: '%%env(PHPLIST_UPLOADIMAGES_DIR)%%'
env(PHPLIST_UPLOADIMAGES_DIR): 'images'
phplist.editor_images_dir: '%%env(FCKIMAGES_DIR)%%'
env(FCKIMAGES_DIR): 'uploadimages'
phplist.public_schema: '%%env(PUBLIC_SCHEMA)%%'
env(PUBLIC_SCHEMA): 'https'
phplist.attachment_download_url: '%%env(PHPLIST_ATTACHMENT_DOWNLOAD_URL)%%'
env(PHPLIST_ATTACHMENT_DOWNLOAD_URL): 'https://example.com/download/'
phplist.attachment_repository_path: '%%env(PHPLIST_ATTACHMENT_REPOSITORY_PATH)%%'
env(PHPLIST_ATTACHMENT_REPOSITORY_PATH): '/tmp'
40 changes: 24 additions & 16 deletions config/services/builders.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,30 @@ services:
autoconfigure: true
public: false

PhpList\Core\Domain\Messaging\Service\Builder\MessageBuilder:
autowire: true
autoconfigure: true
PhpList\Core\Domain\:
resource: '../../src/Domain/*/Service/Builder/*'

PhpList\Core\Domain\Messaging\Service\Builder\MessageFormatBuilder:
autowire: true
autoconfigure: true
# Concrete mail constructors
PhpList\Core\Domain\Messaging\Service\Constructor\SystemMailContentBuilder: ~
PhpList\Core\Domain\Messaging\Service\Constructor\CampaignMailContentBuilder: ~

PhpList\Core\Domain\Messaging\Service\Builder\MessageScheduleBuilder:
autowire: true
autoconfigure: true
# Two EmailBuilder services with different constructors injected
Core.EmailBuilder.system:
class: PhpList\Core\Domain\Messaging\Service\Builder\SystemEmailBuilder
arguments:
$mailConstructor: '@PhpList\Core\Domain\Messaging\Service\Constructor\SystemMailContentBuilder'
$googleSenderId: '%messaging.google_sender_id%'
$useAmazonSes: '%messaging.use_amazon_ses%'
$usePrecedenceHeader: '%messaging.use_precedence_header%'
$devVersion: '%app.dev_version%'
$devEmail: '%app.dev_email%'

PhpList\Core\Domain\Messaging\Service\Builder\MessageContentBuilder:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Builder\MessageOptionsBuilder:
autowire: true
autoconfigure: true
Core.EmailBuilder.campaign:
class: PhpList\Core\Domain\Messaging\Service\Builder\EmailBuilder
arguments:
$mailConstructor: '@PhpList\Core\Domain\Messaging\Service\Constructor\CampaignMailContentBuilder'
$googleSenderId: '%messaging.google_sender_id%'
$useAmazonSes: '%messaging.use_amazon_ses%'
$usePrecedenceHeader: '%messaging.use_precedence_header%'
$devVersion: '%app.dev_version%'
$devEmail: '%app.dev_email%'
102 changes: 4 additions & 98 deletions config/services/managers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,53 +4,11 @@ services:
autoconfigure: true
public: false

PhpList\Core\Domain\Configuration\Service\Manager\ConfigManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Configuration\Service\Manager\EventLogManager:
autowire: true
autoconfigure: true
PhpList\Core\Domain\:
resource: '../../src/Domain/*/Service/Manager/*'
exclude: '../../src/Domain/*/Service/Manager/Builder/*'

PhpList\Core\Domain\Identity\Service\SessionManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Identity\Service\AdministratorManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Identity\Service\AdminAttributeDefinitionManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Identity\Service\AdminAttributeManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Identity\Service\PasswordManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\SubscriberManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\SubscriberListManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\SubscriptionManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\AttributeDefinitionManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\DynamicListAttrManager:
autowire: true
autoconfigure: true
PhpList\Core\Bounce\Service\Manager\BounceManager: ~

Doctrine\DBAL\Schema\AbstractSchemaManager:
factory: ['@doctrine.dbal.default_connection', 'createSchemaManager']
Expand All @@ -62,55 +20,3 @@ services:
arguments:
$dbPrefix: '%database_prefix%'
$dynamicListTablePrefix: '%list_table_prefix%'

PhpList\Core\Domain\Subscription\Service\Manager\SubscriberHistoryManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\SubscriberAttributeManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\SubscriberBlacklistManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Subscription\Service\Manager\SubscribePageManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\MessageManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\TemplateManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\TemplateImageManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\BounceRegexManager:
autowire: true
autoconfigure: true

PhpList\Core\Bounce\Service\Manager\BounceManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\ListMessageManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\BounceRuleManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\SendProcessManager:
autowire: true
autoconfigure: true

PhpList\Core\Domain\Messaging\Service\Manager\MessageDataManager:
autowire: true
autoconfigure: true
33 changes: 6 additions & 27 deletions config/services/messenger.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,15 @@ services:
resource: '../../src/Domain/Messaging/MessageHandler'
tags: [ 'messenger.message_handler' ]

PhpList\Core\Domain\Messaging\MessageHandler\SubscriberConfirmationMessageHandler:
# Register Subscription message handlers (e.g., DynamicTableMessageHandler)
PhpList\Core\Domain\Subscription\MessageHandler\:
autowire: true
autoconfigure: true
tags: [ 'messenger.message_handler' ]
arguments:
$confirmationUrl: '%app.confirmation_url%'

PhpList\Core\Domain\Messaging\MessageHandler\AsyncEmailMessageHandler:
autowire: true
autoconfigure: true
tags: [ 'messenger.message_handler' ]

PhpList\Core\Domain\Messaging\MessageHandler\PasswordResetMessageHandler:
autowire: true
autoconfigure: true
tags: [ 'messenger.message_handler' ]
arguments:
$passwordResetUrl: '%app.password_reset_url%'

PhpList\Core\Domain\Messaging\MessageHandler\SubscriptionConfirmationMessageHandler:
autowire: true
autoconfigure: true
resource: '../../src/Domain/Subscription/MessageHandler'
tags: [ 'messenger.message_handler' ]

PhpList\Core\Domain\Messaging\MessageHandler\CampaignProcessorMessageHandler:
autowire: true
arguments:
$maxMailSize: '%messaging.max_mail_size%'

PhpList\Core\Domain\Subscription\MessageHandler\DynamicTableMessageHandler:
autowire: true
autoconfigure: true
tags: [ 'messenger.message_handler' ]
arguments:
$campaignEmailBuilder: '@Core.EmailBuilder.campaign'
$systemEmailBuilder: '@Core.EmailBuilder.system'
5 changes: 5 additions & 0 deletions config/services/parameters.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
parameters:
# Flattened parameters for direct DI usage (Symfony does not support dot access into arrays)
app.config.message_from_address: '[email protected]'
app.config.default_message_age: 15768000

# Keep original grouped array for legacy/config-provider usage
app.config:
message_from_address: '[email protected]'
admin_address: '[email protected]'
Expand Down
9 changes: 3 additions & 6 deletions config/services/providers.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,6 @@ services:
arguments:
$config: '%app.config%'

PhpList\Core\Domain\Common\IspRestrictionsProvider:
autowire: true
autoconfigure: true
arguments:
$confPath: '%app.phplist_isp_conf_path%'

PhpList\Core\Domain\Subscription\Service\Provider\CheckboxGroupValueProvider:
autowire: true
PhpList\Core\Domain\Subscription\Service\Provider\SelectOrRadioValueProvider:
Expand All @@ -30,3 +24,6 @@ services:

PhpList\Core\Domain\Subscription\Service\Provider\SubscriberAttributeChangeSetProvider:
autowire: true

PhpList\Core\Domain\Common\IspRestrictionsProvider:
autowire: true
15 changes: 15 additions & 0 deletions config/services/repositories.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ services:
arguments:
- PhpList\Core\Domain\Configuration\Model\EventLog

PhpList\Core\Domain\Configuration\Repository\UrlCacheRepository:
parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
arguments:
- PhpList\Core\Domain\Configuration\Model\UrlCache


PhpList\Core\Domain\Identity\Repository\AdministratorRepository:
parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
Expand Down Expand Up @@ -145,3 +150,13 @@ services:
parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
arguments:
- PhpList\Core\Domain\Messaging\Model\MessageData

PhpList\Core\Domain\Messaging\Repository\AttachmentRepository:
parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
arguments:
- PhpList\Core\Domain\Messaging\Model\Attachment

PhpList\Core\Domain\Messaging\Repository\MessageAttachmentRepository:
parent: PhpList\Core\Domain\Common\Repository\AbstractRepository
arguments:
- PhpList\Core\Domain\Messaging\Model\MessageAttachment
Loading
Loading